From 5494f610e2055314475af3f3014ea0d1ab71be03 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 29 Aug 2007 16:23:50 +0000 Subject: [PATCH] split to trunk/branches --- AUTHORS | 19 + COPYING | 504 ++ ChangeLog | 1511 +++++ INSTALL | 182 + Makefile.am | 56 + NEWS | 232 + README | 143 + THANKS | 25 + TODO | 147 + acinclude.m4 | 584 ++ benchmark/README | 19 + benchmark/benchmarkn.sh | 47 + benchmark/sample2.v | Bin 0 -> 512784 bytes bootstrap.sh | 34 + configure.in | 382 ++ contrib/Makefile.am | 4 + contrib/mitsub/Makefile.am | 7 + contrib/mitsub/mitsub.c | 491 ++ contrib/vdump/Makefile.am | 15 + contrib/vdump/vdump.1 | 69 + contrib/vdump/vdump.c | 399 ++ contrib/vdump/vdump.pro | 25 + contrib/vips2dj/Makefile.am | 17 + contrib/vips2dj/share/Makefile.am | 2 + contrib/vips2dj/share/vips2dj/Makefile.am | 2 + .../vips2dj/share/vips2dj/cmyk/Makefile.am | 14 + contrib/vips2dj/share/vips2dj/cmyk/head1 | 686 +++ contrib/vips2dj/share/vips2dj/cmyk/head2 | 160 + contrib/vips2dj/share/vips2dj/cmyk/head3 | 12 + contrib/vips2dj/share/vips2dj/cmyk/head4 | 76 + contrib/vips2dj/share/vips2dj/cmyk/head5 | 101 + contrib/vips2dj/share/vips2dj/cmyk/head6 | 10 + contrib/vips2dj/share/vips2dj/lab/Makefile.am | 14 + contrib/vips2dj/share/vips2dj/lab/head1 | 600 ++ contrib/vips2dj/share/vips2dj/lab/head2 | 3 + contrib/vips2dj/share/vips2dj/lab/head3 | 9 + contrib/vips2dj/share/vips2dj/lab/head4 | 71 + contrib/vips2dj/share/vips2dj/lab/head5 | 70 + contrib/vips2dj/share/vips2dj/lab/head6 | 9 + .../vips2dj/share/vips2dj/mono/Makefile.am | 14 + contrib/vips2dj/share/vips2dj/mono/head1 | 686 +++ contrib/vips2dj/share/vips2dj/mono/head2 | 160 + contrib/vips2dj/share/vips2dj/mono/head3 | 12 + contrib/vips2dj/share/vips2dj/mono/head4 | 29 + contrib/vips2dj/share/vips2dj/mono/head5 | 27 + contrib/vips2dj/share/vips2dj/mono/head6 | 11 + contrib/vips2dj/vips2ah.c | 170 + contrib/vips2dj/vips2dj.c | 377 ++ contrib/vips2dj/vips2dj.h | 45 + doc/Makefile | 13 + doc/Makehtmlman | 93 + doc/README | 15 + doc/src/Makefile | 65 + doc/src/applintro.tex | 51 + doc/src/cppintro.tex | 105 + doc/src/figs/arch.png | Bin 0 -> 32167 bytes doc/src/figs/arch.svg | 385 ++ doc/src/figs/interconvert.png | Bin 0 -> 26322 bytes doc/src/figs/interconvert.svg | 505 ++ doc/src/fileformat.tex | 160 + doc/src/func.tex | 676 +++ doc/src/html.cfg | 17 + doc/src/iosys.tex | 845 +++ doc/src/ipio.tex | 56 + doc/src/mydefs.tex | 113 + doc/src/operintro.tex | 109 + doc/src/packages.tex | 822 +++ doc/src/pio.tex | 868 +++ doc/src/refintro.tex | 105 + doc/src/vdisplay.tex | 75 + doc/src/verror.tex | 76 + doc/src/vimage.tex | 279 + doc/src/vipsmanual.tex | 88 + doc/src/vmask.tex | 151 + doc/src/wio.tex | 363 ++ include/Makefile.am | 2 + include/vips/Makefile.am | 34 + include/vips/VDisplay.h | 113 + include/vips/VError.h | 82 + include/vips/VImage.h | 401 ++ include/vips/VMask.h | 375 ++ include/vips/colour.h | 234 + include/vips/debug.h | 50 + include/vips/dispatch.h | 269 + include/vips/fmask.h | 86 + include/vips/history.h | 59 + include/vips/internal.h | 121 + include/vips/intl.h | 44 + include/vips/meta.h | 107 + include/vips/mosaic.h | 86 + include/vips/proto.h | 749 +++ include/vips/r_access.h | 85 + include/vips/rect.h | 75 + include/vips/region.h | 293 + include/vips/semaphore.h | 64 + include/vips/struct.h | 44 + include/vips/thread.h | 77 + include/vips/threadgroup.h | 134 + include/vips/time.h | 63 + include/vips/util.h | 324 + include/vips/vbuf.h | 84 + include/vips/version.h.in | 15 + include/vips/vips | 106 + include/vips/vips.h | 463 ++ include/vips/vipsc++.h | 308 + include/vips/vipscpp.h | 39 + libsrc/Makefile.am | 58 + libsrc/acquire/Makefile.am | 9 + libsrc/acquire/im_clamp.c | 127 + libsrc/acquire/man3/Makefile.am | 1 + libsrc/arithmetic/Makefile.am | 46 + libsrc/arithmetic/arith_dispatch.c | 1247 ++++ libsrc/arithmetic/im_abs.c | 241 + libsrc/arithmetic/im_add.c | 302 + libsrc/arithmetic/im_avg.c | 203 + libsrc/arithmetic/im_bandmean.c | 161 + libsrc/arithmetic/im_ceil.c | 114 + libsrc/arithmetic/im_cmulnorm.c | 75 + libsrc/arithmetic/im_costra.c | 239 + libsrc/arithmetic/im_deviate.c | 222 + libsrc/arithmetic/im_divide.c | 214 + libsrc/arithmetic/im_expntra.c | 249 + libsrc/arithmetic/im_fav4.c | 95 + libsrc/arithmetic/im_floor.c | 128 + libsrc/arithmetic/im_gadd.c | 127 + libsrc/arithmetic/im_gaddim.c | 241 + libsrc/arithmetic/im_gfadd.c | 351 ++ libsrc/arithmetic/im_invert.c | 149 + libsrc/arithmetic/im_linreg.c | 430 ++ libsrc/arithmetic/im_lintra.c | 383 ++ libsrc/arithmetic/im_litecor.c | 321 + libsrc/arithmetic/im_log10tra.c | 166 + libsrc/arithmetic/im_logtra.c | 166 + libsrc/arithmetic/im_max.c | 242 + libsrc/arithmetic/im_maxpos.c | 150 + libsrc/arithmetic/im_maxpos_avg.c | 201 + libsrc/arithmetic/im_maxpos_vec.c | 459 ++ libsrc/arithmetic/im_measure.c | 210 + libsrc/arithmetic/im_min.c | 241 + libsrc/arithmetic/im_minpos.c | 143 + libsrc/arithmetic/im_multiply.c | 261 + libsrc/arithmetic/im_point_bilinear.c | 127 + libsrc/arithmetic/im_powtra.c | 235 + libsrc/arithmetic/im_remainder.c | 278 + libsrc/arithmetic/im_rint.c | 114 + libsrc/arithmetic/im_sign.c | 162 + libsrc/arithmetic/im_sintra.c | 239 + libsrc/arithmetic/im_stats.c | 277 + libsrc/arithmetic/im_subtract.c | 232 + libsrc/arithmetic/im_tantra.c | 240 + libsrc/arithmetic/man3/Makefile.am | 48 + libsrc/arithmetic/man3/im_abs.3 | 18 + libsrc/arithmetic/man3/im_acostra.3 | 1 + libsrc/arithmetic/man3/im_add.3 | 102 + libsrc/arithmetic/man3/im_asintra.3 | 1 + libsrc/arithmetic/man3/im_atantra.3 | 1 + libsrc/arithmetic/man3/im_avg.3 | 101 + libsrc/arithmetic/man3/im_bandmean.3 | 20 + libsrc/arithmetic/man3/im_ceil.3 | 17 + libsrc/arithmetic/man3/im_cmulnorm.3 | 67 + libsrc/arithmetic/man3/im_costra.3 | 47 + libsrc/arithmetic/man3/im_deviate.3 | 1 + libsrc/arithmetic/man3/im_divide.3 | 33 + libsrc/arithmetic/man3/im_exp10tra.3 | 100 + libsrc/arithmetic/man3/im_expntra.3 | 1 + libsrc/arithmetic/man3/im_expntra_vec.3 | 1 + libsrc/arithmetic/man3/im_exptra.3 | 1 + libsrc/arithmetic/man3/im_fav4.3 | 23 + libsrc/arithmetic/man3/im_floor.3 | 17 + libsrc/arithmetic/man3/im_gadd.3 | 1 + libsrc/arithmetic/man3/im_gaddim.3 | 1 + libsrc/arithmetic/man3/im_gfadd.3 | 1 + libsrc/arithmetic/man3/im_invert.3 | 24 + libsrc/arithmetic/man3/im_lintra.3 | 58 + libsrc/arithmetic/man3/im_lintra_vec.3 | 1 + libsrc/arithmetic/man3/im_litecor.3 | 52 + libsrc/arithmetic/man3/im_log10tra.3 | 2 + libsrc/arithmetic/man3/im_logtra.3 | 2 + libsrc/arithmetic/man3/im_max.3 | 2 + libsrc/arithmetic/man3/im_maxpos.3 | 2 + libsrc/arithmetic/man3/im_maxpos_vec.3 | 42 + libsrc/arithmetic/man3/im_measure.3 | 47 + libsrc/arithmetic/man3/im_min.3 | 1 + libsrc/arithmetic/man3/im_minpos.3 | 2 + libsrc/arithmetic/man3/im_minpos_vec.3 | 2 + libsrc/arithmetic/man3/im_multiply.3 | 1 + libsrc/arithmetic/man3/im_powtra.3 | 2 + libsrc/arithmetic/man3/im_powtra_vec.3 | 1 + libsrc/arithmetic/man3/im_remainder.3 | 47 + libsrc/arithmetic/man3/im_remainderconst.3 | 1 + .../arithmetic/man3/im_remainderconst_vec.3 | 1 + libsrc/arithmetic/man3/im_rint.3 | 18 + libsrc/arithmetic/man3/im_sign.3 | 19 + libsrc/arithmetic/man3/im_sintra.3 | 1 + libsrc/arithmetic/man3/im_stats.3 | 24 + libsrc/arithmetic/man3/im_subtract.3 | 41 + libsrc/arithmetic/man3/im_tantra.3 | 1 + libsrc/boolean/Makefile.am | 9 + libsrc/boolean/bool_dispatch.c | 316 + libsrc/boolean/boolean.c | 668 ++ libsrc/boolean/man3/Makefile.am | 16 + libsrc/boolean/man3/im_and_vec.3 | 2 + libsrc/boolean/man3/im_andconst.3 | 1 + libsrc/boolean/man3/im_andimage.3 | 91 + libsrc/boolean/man3/im_eor_vec.3 | 2 + libsrc/boolean/man3/im_eorconst.3 | 2 + libsrc/boolean/man3/im_eorimage.3 | 2 + libsrc/boolean/man3/im_or_vec.3 | 2 + libsrc/boolean/man3/im_orconst.3 | 2 + libsrc/boolean/man3/im_orimage.3 | 1 + libsrc/boolean/man3/im_shiftleft.3 | 30 + libsrc/boolean/man3/im_shiftright.3 | 1 + libsrc/colour/Makefile.am | 32 + libsrc/colour/colour.c | 1118 ++++ libsrc/colour/colour_dispatch.c | 1012 ++++ libsrc/colour/derived.c | 214 + libsrc/colour/im_LCh2Lab.c | 108 + libsrc/colour/im_LCh2UCS.c | 99 + libsrc/colour/im_Lab2LCh.c | 102 + libsrc/colour/im_Lab2LabQ.c | 158 + libsrc/colour/im_Lab2LabS.c | 92 + libsrc/colour/im_Lab2XYZ.c | 144 + libsrc/colour/im_LabQ2Lab.c | 133 + libsrc/colour/im_LabQ2LabS.c | 125 + libsrc/colour/im_LabQ2disp.c | 209 + libsrc/colour/im_LabS2Lab.c | 96 + libsrc/colour/im_LabS2LabQ.c | 151 + libsrc/colour/im_UCS2LCh.c | 109 + libsrc/colour/im_XYZ2Lab.c | 185 + libsrc/colour/im_XYZ2Yxy.c | 101 + libsrc/colour/im_XYZ2disp.c | 164 + libsrc/colour/im_Yxy2XYZ.c | 102 + libsrc/colour/im_dE00_fromLab.c | 110 + libsrc/colour/im_dECMC_fromLab.c | 110 + libsrc/colour/im_dE_fromLab.c | 116 + libsrc/colour/im_disp2XYZ.c | 115 + libsrc/colour/im_icc_transform.c | 796 +++ libsrc/colour/im_lab_morph.c | 257 + libsrc/colour/man3/Makefile.am | 63 + libsrc/colour/man3/im_LCh2Lab.3 | 1 + libsrc/colour/man3/im_LCh2UCS.3 | 1 + libsrc/colour/man3/im_Lab2LCh.3 | 1 + libsrc/colour/man3/im_Lab2LabQ.3 | 61 + libsrc/colour/man3/im_Lab2LabS.3 | 1 + libsrc/colour/man3/im_Lab2UCS.3 | 72 + libsrc/colour/man3/im_Lab2XYZ.3 | 1 + libsrc/colour/man3/im_Lab2disp.3 | 1 + libsrc/colour/man3/im_LabQ2Lab.3 | 1 + libsrc/colour/man3/im_LabQ2LabS.3 | 1 + libsrc/colour/man3/im_LabQ2XYZ.3 | 1 + libsrc/colour/man3/im_LabQ2disp.3 | 34 + libsrc/colour/man3/im_LabQ2disp_build_table.3 | 1 + libsrc/colour/man3/im_LabQ2disp_table.3 | 1 + libsrc/colour/man3/im_LabS2Lab.3 | 1 + libsrc/colour/man3/im_LabS2LabQ.3 | 1 + libsrc/colour/man3/im_UCS2LCh.3 | 1 + libsrc/colour/man3/im_UCS2Lab.3 | 1 + libsrc/colour/man3/im_UCS2XYZ.3 | 1 + libsrc/colour/man3/im_XYZ2Lab.3 | 1 + libsrc/colour/man3/im_XYZ2UCS.3 | 1 + libsrc/colour/man3/im_XYZ2Yxy.3 | 1 + libsrc/colour/man3/im_XYZ2disp.3 | 144 + libsrc/colour/man3/im_XYZ2sRGB.3 | 1 + libsrc/colour/man3/im_Yxy2XYZ.3 | 1 + libsrc/colour/man3/im_col_C2Cucs.3 | 1 + libsrc/colour/man3/im_col_Ch2ab.3 | 1 + libsrc/colour/man3/im_col_Ch2hucs.3 | 1 + libsrc/colour/man3/im_col_Chucs2h.3 | 1 + libsrc/colour/man3/im_col_Cucs2C.3 | 1 + libsrc/colour/man3/im_col_L2Lucs.3 | 1 + libsrc/colour/man3/im_col_Lab2XYZ.3 | 1 + libsrc/colour/man3/im_col_Lucs2L.3 | 1 + libsrc/colour/man3/im_col_XYZ2Lab.3 | 1 + libsrc/colour/man3/im_col_XYZ2rgb.3 | 145 + libsrc/colour/man3/im_col_ab2Ch.3 | 1 + libsrc/colour/man3/im_col_dECMC.3 | 1 + libsrc/colour/man3/im_col_display.3 | 1 + libsrc/colour/man3/im_col_make_tables_RGB.3 | 1 + libsrc/colour/man3/im_col_make_tables_UCS.3 | 1 + libsrc/colour/man3/im_col_pythagoras.3 | 1 + libsrc/colour/man3/im_col_rgb2XYZ.3 | 1 + libsrc/colour/man3/im_dE00_fromLab.3 | 1 + libsrc/colour/man3/im_dECMC_fromLab.3 | 1 + libsrc/colour/man3/im_dECMC_fromdisp.3 | 1 + libsrc/colour/man3/im_dE_fromLab.3 | 44 + libsrc/colour/man3/im_dE_fromXYZ.3 | 1 + libsrc/colour/man3/im_dE_fromdisp.3 | 40 + libsrc/colour/man3/im_disp2Lab.3 | 1 + libsrc/colour/man3/im_disp2XYZ.3 | 1 + libsrc/colour/man3/im_icc_ac2rc.3 | 1 + libsrc/colour/man3/im_icc_export.3 | 1 + libsrc/colour/man3/im_icc_export_depth.3 | 1 + libsrc/colour/man3/im_icc_import.3 | 1 + libsrc/colour/man3/im_icc_import_embedded.3 | 1 + libsrc/colour/man3/im_icc_present.3 | 1 + libsrc/colour/man3/im_icc_transform.3 | 118 + libsrc/colour/man3/im_lab_morph.3 | 44 + libsrc/colour/man3/im_sRGB2XYZ.3 | 1 + libsrc/conversion/Makefile.am | 62 + libsrc/conversion/conver_dispatch.c | 1964 ++++++ libsrc/conversion/dbh.h | 98 + libsrc/conversion/im_analyze2vips.c | 583 ++ libsrc/conversion/im_bandjoin.c | 161 + libsrc/conversion/im_bernd.c | 94 + libsrc/conversion/im_black.c | 123 + libsrc/conversion/im_c2amph.c | 142 + libsrc/conversion/im_c2imag.c | 117 + libsrc/conversion/im_c2ps.c | 64 + libsrc/conversion/im_c2real.c | 122 + libsrc/conversion/im_c2rect.c | 108 + libsrc/conversion/im_clip.c | 484 ++ libsrc/conversion/im_copy.c | 530 ++ libsrc/conversion/im_csv2vips.c | 316 + libsrc/conversion/im_exr2vips.c | 424 ++ libsrc/conversion/im_extract.c | 261 + libsrc/conversion/im_falsecolour.c | 355 ++ libsrc/conversion/im_fliphor.c | 150 + libsrc/conversion/im_flipver.c | 141 + libsrc/conversion/im_gbandjoin.c | 228 + libsrc/conversion/im_grid.c | 180 + libsrc/conversion/im_insert.c | 379 ++ libsrc/conversion/im_jpeg2vips.c | 714 +++ libsrc/conversion/im_lrjoin.c | 89 + libsrc/conversion/im_magick2vips.c | 621 ++ libsrc/conversion/im_mask2vips.c | 97 + libsrc/conversion/im_msb.c | 344 ++ libsrc/conversion/im_png2vips.c | 381 ++ libsrc/conversion/im_ppm2vips.c | 440 ++ libsrc/conversion/im_print.c | 52 + libsrc/conversion/im_raw2vips.c | 64 + libsrc/conversion/im_recomb.c | 167 + libsrc/conversion/im_replicate.c | 156 + libsrc/conversion/im_ri2c.c | 170 + libsrc/conversion/im_rightshift_size.c | 298 + libsrc/conversion/im_rot180.c | 158 + libsrc/conversion/im_rot270.c | 162 + libsrc/conversion/im_rot90.c | 162 + libsrc/conversion/im_scale.c | 98 + libsrc/conversion/im_scaleps.c | 98 + libsrc/conversion/im_slice.c | 157 + libsrc/conversion/im_subsample.c | 241 + libsrc/conversion/im_system.c | 226 + libsrc/conversion/im_tbjoin.c | 89 + libsrc/conversion/im_text.c | 237 + libsrc/conversion/im_thresh.c | 134 + libsrc/conversion/im_tiff2vips.c | 1490 +++++ libsrc/conversion/im_tile_cache.c | 390 ++ libsrc/conversion/im_vips2csv.c | 146 + libsrc/conversion/im_vips2jpeg.c | 867 +++ libsrc/conversion/im_vips2mask.c | 126 + libsrc/conversion/im_vips2png.c | 311 + libsrc/conversion/im_vips2ppm.c | 293 + libsrc/conversion/im_vips2tiff.c | 1605 +++++ libsrc/conversion/im_zoom.c | 371 ++ libsrc/conversion/man3/Makefile.am | 84 + libsrc/conversion/man3/im_analyze2vips.3 | 39 + libsrc/conversion/man3/im_bandjoin.3 | 39 + libsrc/conversion/man3/im_black.3 | 23 + libsrc/conversion/man3/im_c2amph.3 | 1 + libsrc/conversion/man3/im_c2imag.3 | 1 + libsrc/conversion/man3/im_c2ps.3 | 1 + libsrc/conversion/man3/im_c2real.3 | 1 + libsrc/conversion/man3/im_c2rect.3 | 1 + libsrc/conversion/man3/im_clip.3 | 90 + libsrc/conversion/man3/im_clip2c.3 | 1 + libsrc/conversion/man3/im_clip2cm.3 | 1 + libsrc/conversion/man3/im_clip2d.3 | 1 + libsrc/conversion/man3/im_clip2dcm.3 | 1 + libsrc/conversion/man3/im_clip2f.3 | 1 + libsrc/conversion/man3/im_clip2fmt.3 | 1 + libsrc/conversion/man3/im_clip2i.3 | 1 + libsrc/conversion/man3/im_clip2s.3 | 1 + libsrc/conversion/man3/im_clip2ui.3 | 1 + libsrc/conversion/man3/im_clip2us.3 | 1 + libsrc/conversion/man3/im_copy.3 | 79 + libsrc/conversion/man3/im_copy_from.3 | 1 + libsrc/conversion/man3/im_copy_morph.3 | 1 + libsrc/conversion/man3/im_copy_set.3 | 1 + libsrc/conversion/man3/im_copy_set_meta.3 | 1 + libsrc/conversion/man3/im_copy_swap.3 | 1 + libsrc/conversion/man3/im_csv2vips.3 | 76 + libsrc/conversion/man3/im_csv2vips_header.3 | 1 + libsrc/conversion/man3/im_exr2vips.3 | 23 + libsrc/conversion/man3/im_exr2vips_header.3 | 1 + libsrc/conversion/man3/im_extract.3 | 60 + libsrc/conversion/man3/im_extract_area.3 | 1 + libsrc/conversion/man3/im_extract_areabands.3 | 1 + libsrc/conversion/man3/im_extract_bands.3 | 1 + libsrc/conversion/man3/im_falsecolour.3 | 23 + libsrc/conversion/man3/im_fliphor.3 | 1 + libsrc/conversion/man3/im_flipver.3 | 1 + libsrc/conversion/man3/im_gbandjoin.3 | 1 + libsrc/conversion/man3/im_grid.3 | 42 + libsrc/conversion/man3/im_insert.3 | 29 + libsrc/conversion/man3/im_istiff.3 | 1 + libsrc/conversion/man3/im_jpeg2vips.3 | 70 + libsrc/conversion/man3/im_jpeg2vips_header.3 | 1 + libsrc/conversion/man3/im_lrjoin.3 | 38 + libsrc/conversion/man3/im_magick2vips.3 | 25 + .../conversion/man3/im_magick2vips_header.3 | 1 + libsrc/conversion/man3/im_mask2vips.3 | 1 + libsrc/conversion/man3/im_msb.3 | 63 + libsrc/conversion/man3/im_msb_band.3 | 1 + libsrc/conversion/man3/im_png2vips.3 | 56 + libsrc/conversion/man3/im_png2vips_header.3 | 1 + libsrc/conversion/man3/im_ppm2vips.3 | 38 + libsrc/conversion/man3/im_ppm2vips_header.3 | 1 + libsrc/conversion/man3/im_print.3 | 21 + libsrc/conversion/man3/im_raw2vips.3 | 30 + libsrc/conversion/man3/im_recomb.3 | 36 + libsrc/conversion/man3/im_replicate.3 | 31 + libsrc/conversion/man3/im_ri2c.3 | 1 + libsrc/conversion/man3/im_rightshift_size.3 | 30 + libsrc/conversion/man3/im_rot180.3 | 38 + libsrc/conversion/man3/im_rot270.3 | 1 + libsrc/conversion/man3/im_rot90.3 | 1 + libsrc/conversion/man3/im_scale.3 | 40 + libsrc/conversion/man3/im_scaleps.3 | 1 + libsrc/conversion/man3/im_slice.3 | 1 + libsrc/conversion/man3/im_subsample.3 | 24 + libsrc/conversion/man3/im_system.3 | 48 + libsrc/conversion/man3/im_tbjoin.3 | 1 + libsrc/conversion/man3/im_text.3 | 47 + libsrc/conversion/man3/im_thresh.3 | 46 + libsrc/conversion/man3/im_tiff2vips.3 | 152 + libsrc/conversion/man3/im_tiff2vips_header.3 | 1 + libsrc/conversion/man3/im_tile_cache.3 | 45 + libsrc/conversion/man3/im_vips2bufjpeg.3 | 1 + libsrc/conversion/man3/im_vips2csv.3 | 1 + libsrc/conversion/man3/im_vips2jpeg.3 | 1 + libsrc/conversion/man3/im_vips2mask.3 | 26 + libsrc/conversion/man3/im_vips2mimejpeg.3 | 1 + libsrc/conversion/man3/im_vips2png.3 | 1 + libsrc/conversion/man3/im_vips2ppm.3 | 1 + libsrc/conversion/man3/im_vips2tiff.3 | 1 + libsrc/conversion/man3/im_zoom.3 | 23 + libsrc/convolution/Makefile.am | 33 + libsrc/convolution/convol_dispatch.c | 1407 +++++ libsrc/convolution/im_addgnoise.c | 96 + libsrc/convolution/im_compass.c | 148 + libsrc/convolution/im_contrast_surface.c | 274 + libsrc/convolution/im_conv.c | 538 ++ libsrc/convolution/im_convf.c | 349 ++ libsrc/convolution/im_convsep.c | 429 ++ libsrc/convolution/im_convsepf.c | 347 ++ libsrc/convolution/im_convsub.c | 264 + libsrc/convolution/im_embed.c | 544 ++ libsrc/convolution/im_fastcor.c | 209 + libsrc/convolution/im_gaussmasks.c | 182 + libsrc/convolution/im_gaussnoise.c | 157 + libsrc/convolution/im_gradcor.c | 536 ++ libsrc/convolution/im_logmasks.c | 220 + libsrc/convolution/im_mpercent.c | 101 + libsrc/convolution/im_rank.c | 424 ++ libsrc/convolution/im_rank_image.c | 327 + libsrc/convolution/im_resize_linear.c | 194 + libsrc/convolution/im_sharpen.c | 308 + libsrc/convolution/im_shrink.c | 313 + libsrc/convolution/im_spcor.c | 550 ++ libsrc/convolution/im_stretch3.c | 323 + libsrc/convolution/im_zerox.c | 178 + libsrc/convolution/man3/Makefile.am | 58 + libsrc/convolution/man3/im_addgnoise.3 | 27 + libsrc/convolution/man3/im_compass.3 | 106 + libsrc/convolution/man3/im_contrast_surface.3 | 40 + .../man3/im_contrast_surface_raw.3 | 1 + libsrc/convolution/man3/im_conv.3 | 149 + libsrc/convolution/man3/im_conv_raw.3 | 123 + libsrc/convolution/man3/im_convf.3 | 1 + libsrc/convolution/man3/im_convf_raw.3 | 1 + libsrc/convolution/man3/im_convsep.3 | 1 + libsrc/convolution/man3/im_convsep_raw.3 | 1 + libsrc/convolution/man3/im_convsepf.3 | 1 + libsrc/convolution/man3/im_convsepf_raw.3 | 1 + libsrc/convolution/man3/im_convsub.3 | 1 + libsrc/convolution/man3/im_create_dmask.3 | 224 + libsrc/convolution/man3/im_create_imask.3 | 1 + libsrc/convolution/man3/im_dup_dmask.3 | 1 + libsrc/convolution/man3/im_dup_imask.3 | 1 + libsrc/convolution/man3/im_embed.3 | 36 + libsrc/convolution/man3/im_fastcor.3 | 67 + libsrc/convolution/man3/im_free_dmask.3 | 1 + libsrc/convolution/man3/im_free_imask.3 | 1 + libsrc/convolution/man3/im_gauss_dmask.3 | 48 + libsrc/convolution/man3/im_gauss_imask.3 | 1 + libsrc/convolution/man3/im_gaussnoise.3 | 27 + libsrc/convolution/man3/im_gradient.3 | 2 + libsrc/convolution/man3/im_lindetect.3 | 1 + libsrc/convolution/man3/im_log_dmask.3 | 1 + libsrc/convolution/man3/im_log_imask.3 | 56 + libsrc/convolution/man3/im_lowpass.3 | 26 + libsrc/convolution/man3/im_maxvalue.3 | 1 + libsrc/convolution/man3/im_mpercent.3 | 30 + libsrc/convolution/man3/im_norm_dmask.3 | 1 + libsrc/convolution/man3/im_offsets45.3 | 99 + libsrc/convolution/man3/im_offsets90.3 | 1 + libsrc/convolution/man3/im_print_dmask.3 | 1 + libsrc/convolution/man3/im_print_imask.3 | 1 + libsrc/convolution/man3/im_rank.3 | 45 + libsrc/convolution/man3/im_rank_image.3 | 1 + libsrc/convolution/man3/im_read_dmask.3 | 1 + libsrc/convolution/man3/im_read_imask.3 | 1 + libsrc/convolution/man3/im_rotate_dmask45.3 | 1 + libsrc/convolution/man3/im_rotate_dmask90.3 | 1 + libsrc/convolution/man3/im_rotate_imask45.3 | 1 + libsrc/convolution/man3/im_rotate_imask90.3 | 1 + libsrc/convolution/man3/im_scale_dmask.3 | 1 + libsrc/convolution/man3/im_sharpen.3 | 163 + libsrc/convolution/man3/im_shrink.3 | 1 + libsrc/convolution/man3/im_spcor.3 | 1 + libsrc/convolution/man3/im_stretch3.3 | 28 + libsrc/convolution/man3/im_write_dmask.3 | 1 + libsrc/convolution/man3/im_write_dmask_name.3 | 1 + libsrc/convolution/man3/im_write_imask.3 | 1 + libsrc/convolution/man3/im_write_imask_name.3 | 1 + libsrc/convolution/man3/im_zerox.3 | 30 + libsrc/convolution/rotmask.c | 356 ++ libsrc/convolution/rw_mask.c | 725 +++ libsrc/dummy.c | 3 + libsrc/freq_filt/Makefile.am | 19 + libsrc/freq_filt/fft_sp.c | 213 + libsrc/freq_filt/fmask4th.c | 800 +++ libsrc/freq_filt/fmaskcir.c | 664 ++ libsrc/freq_filt/freq_dispatch.c | 311 + libsrc/freq_filt/im_disp_ps.c | 107 + libsrc/freq_filt/im_fractsurf.c | 87 + libsrc/freq_filt/im_freq_mask.c | 237 + libsrc/freq_filt/im_freqflt.c | 124 + libsrc/freq_filt/im_fwfft.c | 638 ++ libsrc/freq_filt/im_invfft.c | 280 + libsrc/freq_filt/im_invfftr.c | 330 + libsrc/freq_filt/im_rotquad.c | 103 + libsrc/freq_filt/man3/Makefile.am | 13 + libsrc/freq_filt/man3/im_create_fmask.3 | 359 ++ libsrc/freq_filt/man3/im_disp_ps.3 | 32 + libsrc/freq_filt/man3/im_flt_imag_freq.3 | 27 + libsrc/freq_filt/man3/im_fractsurf.3 | 29 + libsrc/freq_filt/man3/im_freqflt.3 | 35 + libsrc/freq_filt/man3/im_fwfft.3 | 60 + libsrc/freq_filt/man3/im_invfft.3 | 1 + libsrc/freq_filt/man3/im_invfftr.3 | 1 + libsrc/freq_filt/man3/im_rotquad.3 | 28 + libsrc/histograms_lut/Makefile.am | 25 + libsrc/histograms_lut/hist_dispatch.c | 811 +++ libsrc/histograms_lut/im_buildlut.c | 241 + libsrc/histograms_lut/im_gammacorrect.c | 82 + libsrc/histograms_lut/im_heq.c | 79 + libsrc/histograms_lut/im_hist.c | 75 + libsrc/histograms_lut/im_histeq.c | 212 + libsrc/histograms_lut/im_histgr.c | 379 ++ libsrc/histograms_lut/im_histnD.c | 267 + libsrc/histograms_lut/im_histplot.c | 336 ++ libsrc/histograms_lut/im_histspec.c | 217 + libsrc/histograms_lut/im_hsp.c | 77 + libsrc/histograms_lut/im_identity.c | 163 + libsrc/histograms_lut/im_invertlut.c | 268 + libsrc/histograms_lut/im_lhisteq.c | 212 + libsrc/histograms_lut/im_maplut.c | 623 ++ libsrc/histograms_lut/im_project.c | 299 + libsrc/histograms_lut/im_stdif.c | 260 + libsrc/histograms_lut/man3/Makefile.am | 27 + libsrc/histograms_lut/man3/im_buildlut.3 | 38 + libsrc/histograms_lut/man3/im_gammacorrect.3 | 24 + libsrc/histograms_lut/man3/im_heq.3 | 53 + libsrc/histograms_lut/man3/im_hist.3 | 36 + libsrc/histograms_lut/man3/im_histcum.3 | 1 + libsrc/histograms_lut/man3/im_histeq.3 | 1 + libsrc/histograms_lut/man3/im_histgr.3 | 142 + libsrc/histograms_lut/man3/im_histnD.3 | 1 + libsrc/histograms_lut/man3/im_histnorm.3 | 1 + libsrc/histograms_lut/man3/im_histplot.3 | 47 + libsrc/histograms_lut/man3/im_histspec.3 | 1 + libsrc/histograms_lut/man3/im_hsp.3 | 1 + libsrc/histograms_lut/man3/im_identity.3 | 1 + .../histograms_lut/man3/im_identity_ushort.3 | 1 + libsrc/histograms_lut/man3/im_invertlut.3 | 41 + libsrc/histograms_lut/man3/im_lhisteq.3 | 1 + libsrc/histograms_lut/man3/im_lhisteq_raw.3 | 1 + libsrc/histograms_lut/man3/im_maplut.3 | 46 + libsrc/histograms_lut/man3/im_project.3 | 34 + libsrc/histograms_lut/man3/im_stdif.3 | 54 + libsrc/histograms_lut/man3/im_tone_analyse.3 | 1 + libsrc/histograms_lut/man3/im_tone_build.3 | 151 + libsrc/histograms_lut/man3/im_tone_map.3 | 1 + libsrc/histograms_lut/tone.c | 528 ++ libsrc/inplace/Makefile.am | 17 + libsrc/inplace/im_circle.c | 134 + libsrc/inplace/im_flood.c | 421 ++ libsrc/inplace/im_insertplace.c | 123 + libsrc/inplace/im_line.c | 153 + libsrc/inplace/im_paintrect.c | 105 + libsrc/inplace/im_plotmask.c | 239 + libsrc/inplace/inplace_dispatch.c | 287 + libsrc/inplace/line_draw.c | 449 ++ libsrc/inplace/man3/Makefile.am | 18 + libsrc/inplace/man3/im_circle.3 | 33 + libsrc/inplace/man3/im_fastline.3 | 1 + libsrc/inplace/man3/im_fastlineuser.3 | 1 + libsrc/inplace/man3/im_flood.3 | 41 + libsrc/inplace/man3/im_flood_blob.3 | 1 + libsrc/inplace/man3/im_insertplace.3 | 27 + libsrc/inplace/man3/im_line.3 | 29 + libsrc/inplace/man3/im_lineset.3 | 1 + libsrc/inplace/man3/im_paintrect.3 | 126 + libsrc/inplace/man3/im_plotmask.3 | 1 + libsrc/inplace/man3/im_plotpoint.3 | 1 + libsrc/inplace/man3/im_readpoint.3 | 1 + libsrc/inplace/man3/im_smear.3 | 1 + libsrc/inplace/man3/im_smudge.3 | 42 + libsrc/inplace/plot_point.c | 119 + libsrc/inplace/smudge_area.c | 319 + libsrc/iofuncs/Makefile.am | 64 + libsrc/iofuncs/base64.c | 285 + libsrc/iofuncs/base64.h | 25 + libsrc/iofuncs/buffer.c | 579 ++ libsrc/iofuncs/callback.c | 145 + libsrc/iofuncs/debug.c | 134 + libsrc/iofuncs/dispatch_types.c | 829 +++ libsrc/iofuncs/error.c | 248 + libsrc/iofuncs/error_exit.c | 75 + libsrc/iofuncs/im_binfile.c | 182 + libsrc/iofuncs/im_bits_of_fmt.c | 64 + libsrc/iofuncs/im_close.c | 303 + libsrc/iofuncs/im_cp_desc.c | 152 + libsrc/iofuncs/im_debugim.c | 138 + libsrc/iofuncs/im_demand_hint.c | 188 + libsrc/iofuncs/im_desc_hd.c | 204 + libsrc/iofuncs/im_generate.c | 850 +++ libsrc/iofuncs/im_guess_prefix.c | 398 ++ libsrc/iofuncs/im_header.c | 265 + libsrc/iofuncs/im_histlin.c | 120 + libsrc/iofuncs/im_image.c | 92 + libsrc/iofuncs/im_init.c | 168 + libsrc/iofuncs/im_init_world.c | 221 + libsrc/iofuncs/im_initdesc.c | 81 + libsrc/iofuncs/im_iocheck.c | 313 + libsrc/iofuncs/im_iterate.c | 230 + libsrc/iofuncs/im_makerw.c | 86 + libsrc/iofuncs/im_mapfile.c | 351 ++ libsrc/iofuncs/im_open.c | 605 ++ libsrc/iofuncs/im_openin.c | 293 + libsrc/iofuncs/im_openout.c | 75 + libsrc/iofuncs/im_partial.c | 64 + libsrc/iofuncs/im_piocheck.c | 197 + libsrc/iofuncs/im_prepare.c | 384 ++ libsrc/iofuncs/im_printdesc.c | 346 ++ libsrc/iofuncs/im_printlines.c | 150 + libsrc/iofuncs/im_readhist.c | 753 +++ libsrc/iofuncs/im_render.c | 1181 ++++ libsrc/iofuncs/im_setbox.c | 64 + libsrc/iofuncs/im_setbuf.c | 73 + libsrc/iofuncs/im_setupout.c | 159 + libsrc/iofuncs/im_unmapfile.c | 71 + libsrc/iofuncs/im_updatehist.c | 80 + libsrc/iofuncs/im_wrapmany.c | 202 + libsrc/iofuncs/im_wrapone.c | 121 + libsrc/iofuncs/im_writeline.c | 113 + libsrc/iofuncs/man3/IM_ARRAY.3 | 80 + libsrc/iofuncs/man3/IM_IMAGE_ADDR.3 | 66 + libsrc/iofuncs/man3/IM_IMAGE_N_ELEMENTS.3 | 1 + libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_ELEMENT.3 | 1 + libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_LINE.3 | 1 + libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_PEL.3 | 1 + libsrc/iofuncs/man3/IM_MAX.3 | 1 + libsrc/iofuncs/man3/IM_MIN.3 | 1 + libsrc/iofuncs/man3/IM_NEW.3 | 1 + libsrc/iofuncs/man3/IM_NUMBER.3 | 1 + libsrc/iofuncs/man3/IM_RECT_BOTTOM.3 | 1 + libsrc/iofuncs/man3/IM_RECT_HCENTRE.3 | 1 + libsrc/iofuncs/man3/IM_RECT_RIGHT.3 | 1 + libsrc/iofuncs/man3/IM_RECT_VCENTRE.3 | 1 + libsrc/iofuncs/man3/IM_REGION_ADDR.3 | 65 + libsrc/iofuncs/man3/IM_REGION_LSKIP.3 | 1 + libsrc/iofuncs/man3/IM_REGION_N_ELEMENTS.3 | 1 + libsrc/iofuncs/man3/IM_REGION_SIZEOF_LINE.3 | 1 + libsrc/iofuncs/man3/IM_RINT.3 | 36 + libsrc/iofuncs/man3/Makefile.am | 165 + libsrc/iofuncs/man3/error_exit.3 | 1 + libsrc/iofuncs/man3/im_BandFmt2char.3 | 1 + libsrc/iofuncs/man3/im_Coding2char.3 | 1 + libsrc/iofuncs/man3/im_Compression2char.3 | 1 + libsrc/iofuncs/man3/im_Type2char.3 | 1 + libsrc/iofuncs/man3/im_add_close_callback.3 | 1 + libsrc/iofuncs/man3/im_add_eval_callback.3 | 1 + libsrc/iofuncs/man3/im_add_evalend_callback.3 | 1 + libsrc/iofuncs/man3/im_allocate_input_array.3 | 1 + libsrc/iofuncs/man3/im_amiMSBfirst.3 | 1 + libsrc/iofuncs/man3/im_binfile.3 | 42 + libsrc/iofuncs/man3/im_bits_of_fmt.3 | 16 + libsrc/iofuncs/man3/im_cache.3 | 1 + libsrc/iofuncs/man3/im_char2BandFmt.3 | 1 + libsrc/iofuncs/man3/im_char2Coding.3 | 1 + libsrc/iofuncs/man3/im_char2Compression.3 | 1 + libsrc/iofuncs/man3/im_char2Type.3 | 1 + libsrc/iofuncs/man3/im_close.3 | 35 + libsrc/iofuncs/man3/im_concurrency_get.3 | 1 + libsrc/iofuncs/man3/im_concurrency_set.3 | 32 + libsrc/iofuncs/man3/im_cp_desc.3 | 48 + libsrc/iofuncs/man3/im_cp_desc_array.3 | 1 + libsrc/iofuncs/man3/im_cp_descv.3 | 1 + libsrc/iofuncs/man3/im_debugim.3 | 31 + libsrc/iofuncs/man3/im_demand_hint.3 | 132 + libsrc/iofuncs/man3/im_demand_hint_array.3 | 1 + libsrc/iofuncs/man3/im_diag.3 | 1 + libsrc/iofuncs/man3/im_error.3 | 84 + libsrc/iofuncs/man3/im_error_buffer.3 | 1 + libsrc/iofuncs/man3/im_error_clear.3 | 1 + libsrc/iofuncs/man3/im_free.3 | 1 + libsrc/iofuncs/man3/im_generate.3 | 215 + libsrc/iofuncs/man3/im_get_option_group.3 | 1 + libsrc/iofuncs/man3/im_guess_prefix.3 | 29 + libsrc/iofuncs/man3/im_header.3 | 57 + libsrc/iofuncs/man3/im_header_double.3 | 1 + libsrc/iofuncs/man3/im_header_get.3 | 1 + libsrc/iofuncs/man3/im_header_get_type.3 | 1 + libsrc/iofuncs/man3/im_header_int.3 | 1 + libsrc/iofuncs/man3/im_header_map.3 | 1 + libsrc/iofuncs/man3/im_header_string.3 | 1 + libsrc/iofuncs/man3/im_histlin.3 | 34 + libsrc/iofuncs/man3/im_history_get.3 | 1 + libsrc/iofuncs/man3/im_image.3 | 1 + libsrc/iofuncs/man3/im_image_sanity.3 | 19 + libsrc/iofuncs/man3/im_incheck.3 | 1 + libsrc/iofuncs/man3/im_init.3 | 22 + libsrc/iofuncs/man3/im_init_world.3 | 47 + libsrc/iofuncs/man3/im_initdesc.3 | 45 + libsrc/iofuncs/man3/im_invalidate.3 | 16 + libsrc/iofuncs/man3/im_iocheck.3 | 50 + libsrc/iofuncs/man3/im_isMSBfirst.3 | 1 + libsrc/iofuncs/man3/im_iscomplex.3 | 121 + libsrc/iofuncs/man3/im_isfile.3 | 1 + libsrc/iofuncs/man3/im_isfloat.3 | 1 + libsrc/iofuncs/man3/im_isint.3 | 1 + libsrc/iofuncs/man3/im_isjpeg.3 | 1 + libsrc/iofuncs/man3/im_ispartial.3 | 1 + libsrc/iofuncs/man3/im_ispng.3 | 1 + libsrc/iofuncs/man3/im_isppm.3 | 1 + libsrc/iofuncs/man3/im_isscalar.3 | 1 + libsrc/iofuncs/man3/im_istifftiled.3 | 1 + libsrc/iofuncs/man3/im_isuint.3 | 1 + libsrc/iofuncs/man3/im_isvips.3 | 1 + libsrc/iofuncs/man3/im_iterate.3 | 72 + libsrc/iofuncs/man3/im_list_add.3 | 229 + libsrc/iofuncs/man3/im_list_append.3 | 1 + libsrc/iofuncs/man3/im_list_eq.3 | 1 + libsrc/iofuncs/man3/im_list_fix.3 | 1 + libsrc/iofuncs/man3/im_list_fold.3 | 1 + libsrc/iofuncs/man3/im_list_free.3 | 1 + libsrc/iofuncs/man3/im_list_index.3 | 1 + libsrc/iofuncs/man3/im_list_insert.3 | 1 + libsrc/iofuncs/man3/im_list_len.3 | 1 + libsrc/iofuncs/man3/im_list_map.3 | 1 + libsrc/iofuncs/man3/im_list_map_rev.3 | 1 + libsrc/iofuncs/man3/im_list_member.3 | 1 + libsrc/iofuncs/man3/im_list_pos.3 | 1 + libsrc/iofuncs/man3/im_list_remove.3 | 1 + libsrc/iofuncs/man3/im_makerw.3 | 26 + libsrc/iofuncs/man3/im_malloc.3 | 155 + libsrc/iofuncs/man3/im_meta.3 | 118 + libsrc/iofuncs/man3/im_meta_get.3 | 1 + libsrc/iofuncs/man3/im_meta_get_area.3 | 1 + libsrc/iofuncs/man3/im_meta_get_blob.3 | 1 + libsrc/iofuncs/man3/im_meta_get_double.3 | 1 + libsrc/iofuncs/man3/im_meta_get_int.3 | 1 + libsrc/iofuncs/man3/im_meta_get_string.3 | 1 + libsrc/iofuncs/man3/im_meta_get_type.3 | 1 + libsrc/iofuncs/man3/im_meta_set.3 | 1 + libsrc/iofuncs/man3/im_meta_set_area.3 | 1 + libsrc/iofuncs/man3/im_meta_set_blob.3 | 1 + libsrc/iofuncs/man3/im_meta_set_double.3 | 1 + libsrc/iofuncs/man3/im_meta_set_int.3 | 1 + libsrc/iofuncs/man3/im_meta_set_string.3 | 1 + libsrc/iofuncs/man3/im_mmapin.3 | 22 + libsrc/iofuncs/man3/im_mmapinrw.3 | 27 + libsrc/iofuncs/man3/im_open.3 | 135 + libsrc/iofuncs/man3/im_open_local.3 | 1 + libsrc/iofuncs/man3/im_open_local_array.3 | 1 + libsrc/iofuncs/man3/im_openout.3 | 22 + libsrc/iofuncs/man3/im_outcheck.3 | 1 + libsrc/iofuncs/man3/im_partial.3 | 24 + libsrc/iofuncs/man3/im_pincheck.3 | 1 + libsrc/iofuncs/man3/im_piocheck.3 | 36 + libsrc/iofuncs/man3/im_poutcheck.3 | 1 + libsrc/iofuncs/man3/im_prepare.3 | 91 + libsrc/iofuncs/man3/im_prepare_many.3 | 1 + libsrc/iofuncs/man3/im_prepare_to.3 | 1 + libsrc/iofuncs/man3/im_printdesc.3 | 66 + libsrc/iofuncs/man3/im_printlines.3 | 1 + libsrc/iofuncs/man3/im_rect_dup.3 | 1 + libsrc/iofuncs/man3/im_rect_equalsrect.3 | 1 + libsrc/iofuncs/man3/im_rect_includespoint.3 | 1 + libsrc/iofuncs/man3/im_rect_includesrect.3 | 1 + libsrc/iofuncs/man3/im_rect_intersectrect.3 | 1 + libsrc/iofuncs/man3/im_rect_isempty.3 | 1 + libsrc/iofuncs/man3/im_rect_marginadjust.3 | 81 + libsrc/iofuncs/man3/im_rect_normalise.3 | 1 + libsrc/iofuncs/man3/im_rect_unionrect.3 | 1 + libsrc/iofuncs/man3/im_region_buffer.3 | 119 + libsrc/iofuncs/man3/im_region_create.3 | 40 + libsrc/iofuncs/man3/im_region_free.3 | 1 + libsrc/iofuncs/man3/im_region_image.3 | 1 + libsrc/iofuncs/man3/im_region_position.3 | 1 + libsrc/iofuncs/man3/im_region_region.3 | 1 + libsrc/iofuncs/man3/im_render.3 | 101 + libsrc/iofuncs/man3/im_render_fade.3 | 1 + libsrc/iofuncs/man3/im_setbuf.3 | 21 + libsrc/iofuncs/man3/im_setupout.3 | 51 + libsrc/iofuncs/man3/im_start_many.3 | 1 + libsrc/iofuncs/man3/im_start_one.3 | 1 + libsrc/iofuncs/man3/im_stop_many.3 | 1 + libsrc/iofuncs/man3/im_stop_one.3 | 1 + libsrc/iofuncs/man3/im_updatehist.3 | 1 + libsrc/iofuncs/man3/im_verror.3 | 1 + libsrc/iofuncs/man3/im_version.3 | 46 + libsrc/iofuncs/man3/im_version_string.3 | 1 + libsrc/iofuncs/man3/im_warn.3 | 1 + libsrc/iofuncs/man3/im_wrapmany.3 | 1 + libsrc/iofuncs/man3/im_wrapone.3 | 69 + libsrc/iofuncs/man3/im_writeline.3 | 37 + libsrc/iofuncs/memory.c | 209 + libsrc/iofuncs/meta.c | 926 +++ libsrc/iofuncs/package.c | 1029 ++++ libsrc/iofuncs/predicate.c | 528 ++ libsrc/iofuncs/rect.c | 167 + libsrc/iofuncs/region.c | 603 ++ libsrc/iofuncs/semaphore.c | 146 + libsrc/iofuncs/threadgroup.c | 678 +++ libsrc/iofuncs/time.c | 112 + libsrc/iofuncs/util.c | 915 +++ libsrc/iofuncs/vbuf.c | 332 + libsrc/iofuncs/window.c | 388 ++ libsrc/makedef.pl | 20 + libsrc/matrix/Makefile.am | 13 + libsrc/matrix/im_matcat.c | 88 + libsrc/matrix/im_matinv.c | 504 ++ libsrc/matrix/im_matmul.c | 107 + libsrc/matrix/im_mattrn.c | 87 + libsrc/matrix/man3/Makefile.am | 11 + libsrc/matrix/man3/im_lu_decomp.3 | 75 + libsrc/matrix/man3/im_lu_solve.3 | 2 + libsrc/matrix/man3/im_matcat.3 | 1 + libsrc/matrix/man3/im_matinv.3 | 90 + libsrc/matrix/man3/im_matinv_inplace.3 | 2 + libsrc/matrix/man3/im_matmul.3 | 1 + libsrc/matrix/man3/im_mattrn.3 | 1 + libsrc/matrix/matalloc.c | 241 + libsrc/matrix/matrix_dispatch.c | 181 + libsrc/morphology/Makefile.am | 12 + libsrc/morphology/im_cntlines.c | 131 + libsrc/morphology/im_dilate.c | 313 + libsrc/morphology/im_erode.c | 311 + libsrc/morphology/im_profile.c | 132 + libsrc/morphology/man3/Makefile.am | 9 + libsrc/morphology/man3/im_cntlines.3 | 57 + libsrc/morphology/man3/im_dilate.3 | 72 + libsrc/morphology/man3/im_dilate_raw.3 | 1 + libsrc/morphology/man3/im_erode.3 | 1 + libsrc/morphology/man3/im_erode_raw.3 | 1 + libsrc/morphology/man3/im_profile.3 | 1 + libsrc/morphology/morph_dispatch.c | 213 + libsrc/mosaicing/Makefile.am | 28 + libsrc/mosaicing/global_balance.c | 1747 ++++++ libsrc/mosaicing/global_balance.h | 126 + libsrc/mosaicing/im_affine.c | 761 +++ libsrc/mosaicing/im_avgdxdy.c | 85 + libsrc/mosaicing/im_chkpair.c | 241 + libsrc/mosaicing/im_clinear.c | 183 + libsrc/mosaicing/im_improve.c | 193 + libsrc/mosaicing/im_initialize.c | 100 + libsrc/mosaicing/im_lrcalcon.c | 318 + libsrc/mosaicing/im_lrmerge.c | 1128 ++++ libsrc/mosaicing/im_lrmosaic.c | 475 ++ libsrc/mosaicing/im_remosaic.c | 136 + libsrc/mosaicing/im_tbcalcon.c | 137 + libsrc/mosaicing/im_tbmerge.c | 732 +++ libsrc/mosaicing/im_tbmosaic.c | 309 + libsrc/mosaicing/man3/Makefile.am | 17 + libsrc/mosaicing/man3/im_affine.3 | 40 + libsrc/mosaicing/man3/im_correl.3 | 49 + libsrc/mosaicing/man3/im_global_balance.3 | 53 + .../mosaicing/man3/im_global_balance_float.3 | 1 + libsrc/mosaicing/man3/im_lrmerge.3 | 40 + libsrc/mosaicing/man3/im_lrmosaic.3 | 104 + libsrc/mosaicing/man3/im_match_linear.3 | 1 + .../mosaicing/man3/im_match_linear_search.3 | 55 + libsrc/mosaicing/man3/im_remosaic.3 | 28 + libsrc/mosaicing/man3/im_similarity.3 | 1 + libsrc/mosaicing/man3/im_similarity_area.3 | 61 + libsrc/mosaicing/man3/im_tbmerge.3 | 1 + libsrc/mosaicing/man3/im_tbmosaic.3 | 1 + libsrc/mosaicing/match.c | 154 + libsrc/mosaicing/merge.h | 151 + libsrc/mosaicing/mosaic.h | 97 + libsrc/mosaicing/mosaic1.c | 455 ++ libsrc/mosaicing/mosaicing_dispatch.c | 847 +++ libsrc/mosaicing/similarity.c | 159 + libsrc/other/Makefile.am | 20 + libsrc/other/cooc_funcs.c | 472 ++ libsrc/other/glds_funcs.c | 248 + libsrc/other/im_benchmark.c | 287 + libsrc/other/im_dif_std.c | 101 + libsrc/other/im_eye.c | 134 + libsrc/other/im_grey.c | 146 + libsrc/other/im_make_xy.c | 116 + libsrc/other/im_meanstd.c | 136 + libsrc/other/im_simcontr.c | 133 + libsrc/other/im_sines.c | 138 + libsrc/other/im_spatres.c | 146 + libsrc/other/im_zone.c | 127 + libsrc/other/man3/Makefile.am | 30 + libsrc/other/man3/im_benchmark.3 | 16 + libsrc/other/man3/im_cooc_asm.3 | 1 + libsrc/other/man3/im_cooc_contrast.3 | 1 + libsrc/other/man3/im_cooc_correlation.3 | 1 + libsrc/other/man3/im_cooc_entropy.3 | 1 + libsrc/other/man3/im_cooc_matrix.3 | 88 + libsrc/other/man3/im_dif_std.3 | 40 + libsrc/other/man3/im_eye.3 | 47 + libsrc/other/man3/im_feye.3 | 1 + libsrc/other/man3/im_fgrey.3 | 1 + libsrc/other/man3/im_fzone.3 | 1 + libsrc/other/man3/im_glds_asm.3 | 1 + libsrc/other/man3/im_glds_contrast.3 | 1 + libsrc/other/man3/im_glds_entropy.3 | 1 + libsrc/other/man3/im_glds_matrix.3 | 78 + libsrc/other/man3/im_glds_mean.3 | 1 + libsrc/other/man3/im_grey.3 | 46 + libsrc/other/man3/im_make_xy.3 | 1 + libsrc/other/man3/im_mean_std_double_buffer.3 | 1 + libsrc/other/man3/im_mean_std_int_buffer.3 | 38 + libsrc/other/man3/im_quantim.3 | 53 + libsrc/other/man3/im_quantlut.3 | 1 + libsrc/other/man3/im_simcontr.3 | 27 + libsrc/other/man3/im_sines.3 | 29 + libsrc/other/man3/im_spatres.3 | 1 + libsrc/other/man3/im_zone.3 | 38 + libsrc/other/other_dispatch.c | 332 + libsrc/relational/Makefile.am | 11 + libsrc/relational/im_blend.c | 364 ++ libsrc/relational/im_ifthenelse.c | 211 + libsrc/relational/man3/Makefile.am | 24 + libsrc/relational/man3/im_blend.3 | 1 + libsrc/relational/man3/im_equal.3 | 141 + libsrc/relational/man3/im_equal_vec.3 | 1 + libsrc/relational/man3/im_equalconst.3 | 1 + libsrc/relational/man3/im_ifthenelse.3 | 39 + libsrc/relational/man3/im_less.3 | 1 + libsrc/relational/man3/im_less_vec.3 | 1 + libsrc/relational/man3/im_lessconst.3 | 1 + libsrc/relational/man3/im_lesseq.3 | 1 + libsrc/relational/man3/im_lesseq_vec.3 | 1 + libsrc/relational/man3/im_lesseqconst.3 | 1 + libsrc/relational/man3/im_more.3 | 1 + libsrc/relational/man3/im_more_vec.3 | 1 + libsrc/relational/man3/im_moreconst.3 | 1 + libsrc/relational/man3/im_moreeq.3 | 1 + libsrc/relational/man3/im_moreeq_vec.3 | 1 + libsrc/relational/man3/im_moreeqconst.3 | 1 + libsrc/relational/man3/im_notequal.3 | 1 + libsrc/relational/man3/im_notequal_vec.3 | 1 + libsrc/relational/man3/im_notequalconst.3 | 1 + libsrc/relational/relational.c | 738 +++ libsrc/relational/relational_dispatch.c | 513 ++ libsrc/video/Makefile.am | 10 + libsrc/video/im_video_test.c | 51 + libsrc/video/im_video_v4l1.c | 677 +++ libsrc/video/im_video_v4l1.h | 76 + libsrc/video/man3/Makefile.am | 5 + libsrc/video/man3/im_video_v4l1.3 | 39 + libsrc/video/video_dispatch.c | 115 + libsrc/vips.def | 535 ++ libsrcCC/Makefile.am | 20 + libsrcCC/VDisplay.cc | 187 + libsrcCC/VError.cc | 98 + libsrcCC/VImage.cc | 384 ++ libsrcCC/VMask.cc | 644 ++ libsrcCC/vipsc++.cc | 5367 +++++++++++++++++ po/ChangeLog | 1 + po/POTFILES.in | 318 + po/POTFILES.skip | 0 po/README | 51 + po/en_GB.gmo | Bin 0 -> 362 bytes po/en_GB.po | 1045 ++++ po/malkovich.gmo | Bin 0 -> 380 bytes po/malkovich.po | 3259 ++++++++++ po/messages | Bin 0 -> 349 bytes po/missing | 0 python/Makefile.am | 5 + python/test/testvipsCC.py | 37 + python/vipsCC/Makefile.am | 46 + python/vipsCC/VDisplay.i | 15 + python/vipsCC/VError.i | 12 + python/vipsCC/VImage.i | 186 + python/vipsCC/VMask.i | 34 + python/vipsCC/__init__.py | 1 + src/Makefile.am | 6 + src/iofuncs/Makefile.am | 27 + src/iofuncs/binfile.c | 81 + src/iofuncs/debugim.c | 69 + src/iofuncs/edvips.c | 206 + src/iofuncs/header.c | 174 + src/iofuncs/man1/Makefile.am | 9 + src/iofuncs/man1/binfile.1 | 19 + src/iofuncs/man1/debugim.1 | 21 + src/iofuncs/man1/edvips.1 | 49 + src/iofuncs/man1/header.1 | 33 + src/iofuncs/man1/printlines.1 | 1 + src/iofuncs/man1/vips.1 | 58 + src/iofuncs/printlines.c | 70 + src/iofuncs/vips.c | 944 +++ src/mosaicing/Makefile.am | 12 + src/mosaicing/find_mosaic.c | 430 ++ src/mosaicing/mergeup.c | 556 ++ src/other/Makefile.am | 25 + src/other/cooc.c | 88 + src/other/cooc_features.c | 86 + src/other/glds.c | 86 + src/other/glds_features.c | 83 + src/other/man1/Makefile.am | 10 + src/other/man1/cooc.1 | 41 + src/other/man1/cooc_features.1 | 1 + src/other/man1/glds.1 | 35 + src/other/man1/glds_features.1 | 1 + src/other/man1/simcontr.1 | 18 + src/other/man1/sines.1 | 24 + src/other/man1/squares.1 | 1 + src/other/simcontr.c | 78 + src/other/sines.c | 86 + src/other/spatres.c | 83 + src/other/squares.c | 87 + src/scripts/Makefile.am | 27 + src/scripts/batch_crop.in | 45 + src/scripts/batch_image_convert.in | 43 + src/scripts/batch_rubber_sheet.in | 38 + src/scripts/light_correct.in | 57 + src/scripts/man1/Makefile.am | 7 + src/scripts/man1/batch_crop.1 | 22 + src/scripts/man1/batch_image_convert.1 | 29 + src/scripts/man1/batch_rubber_sheet.1 | 30 + src/scripts/man1/light_correct.1 | 39 + src/scripts/post_install | 5 + src/scripts/shrink_width.in | 18 + src/scripts/vips-7.12 | 116 + vips-7.12.pc.in | 11 + vips-7.12.spec.in | 114 + vipsCC-7.12.pc.in | 10 + win32/README | 15 + win32/msvc/config.h | 243 + win32/msvc/vips.dsp | 1327 ++++ win32/msvc/vips.dsw | 29 + win32/msvc/vips.opt | Bin 0 -> 49664 bytes win32/proj/README | 3 + win32/proj/config.h.win32 | 243 + win32/proj/libsrc/acquire/makefile.msc | 41 + win32/proj/libsrc/arithmetic/makefile.msc | 75 + win32/proj/libsrc/boolean/makefile.msc | 42 + win32/proj/libsrc/colour/makefile.msc | 65 + win32/proj/libsrc/conversion/makefile.msc | 84 + win32/proj/libsrc/convolution/makefile.msc | 66 + win32/proj/libsrc/freq_filt/makefile.msc | 51 + .../proj/libsrc/histrograms_lut/makefile.msc | 55 + win32/proj/libsrc/inplace/makefile.msc | 50 + win32/proj/libsrc/iofuncs/makefile.msc | 97 + win32/proj/libsrc/makefile.msc | 78 + win32/proj/libsrc/matrix/makefile.msc | 47 + win32/proj/libsrc/morphology/makefile.msc | 45 + win32/proj/libsrc/mosaicing/makefile.msc | 58 + win32/proj/libsrc/other/makefile.msc | 51 + win32/proj/libsrc/relational/makefile.msc | 44 + win32/proj/libsrc/video/makefile.msc | 42 + win32/proj/src/iofuncs/makefile.msc | 42 + win32/tmake/README | 16 + win32/tmake/config.h | 243 + win32/tmake/libsrc/Makefile | 99 + win32/tmake/libsrc/acquire/Makefile | 77 + win32/tmake/libsrc/acquire/acquire.pro | 18 + win32/tmake/libsrc/arithmetic/Makefile | 237 + win32/tmake/libsrc/arithmetic/arithmetic.pro | 51 + win32/tmake/libsrc/boolean/Makefile | 82 + win32/tmake/libsrc/boolean/boolean.pro | 20 + win32/tmake/libsrc/colour/Makefile | 197 + win32/tmake/libsrc/colour/colour.pro | 43 + win32/tmake/libsrc/conversion/Makefile | 277 + win32/tmake/libsrc/conversion/conversion.pro | 60 + win32/tmake/libsrc/convolution/Makefile | 202 + .../tmake/libsrc/convolution/convolution.pro | 45 + win32/tmake/libsrc/freq_filt/Makefile | 132 + win32/tmake/libsrc/freq_filt/freq_filt.pro | 31 + win32/tmake/libsrc/histograms_lut/Makefile | 147 + .../libsrc/histograms_lut/histograms_lut.pro | 34 + win32/tmake/libsrc/inplace/Makefile | 122 + win32/tmake/libsrc/inplace/inplace.pro | 28 + win32/tmake/libsrc/iofuncs/Makefile | 337 ++ win32/tmake/libsrc/iofuncs/iofuncs.pro | 72 + win32/tmake/libsrc/makefile_todo | 222 + win32/tmake/libsrc/matrix/Makefile | 107 + win32/tmake/libsrc/matrix/matrix.pro | 26 + win32/tmake/libsrc/morphology/Makefile | 97 + win32/tmake/libsrc/morphology/morphology.pro | 24 + win32/tmake/libsrc/mosaicing/Makefile | 185 + win32/tmake/libsrc/mosaicing/mosaicing.pro | 37 + win32/tmake/libsrc/other/Makefile | 127 + win32/tmake/libsrc/other/other.pro | 30 + win32/tmake/libsrc/relational/Makefile | 92 + win32/tmake/libsrc/relational/relational.pro | 23 + win32/tmake/libsrc/todo.pro | 19 + win32/tmake/libsrc/video/Makefile | 82 + win32/tmake/libsrc/video/video.pro | 21 + win32/tmake/libsrc/vips.pro | 21 + 1109 files changed, 143498 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 THANKS create mode 100644 TODO create mode 100644 acinclude.m4 create mode 100644 benchmark/README create mode 100755 benchmark/benchmarkn.sh create mode 100644 benchmark/sample2.v create mode 100755 bootstrap.sh create mode 100644 configure.in create mode 100644 contrib/Makefile.am create mode 100644 contrib/mitsub/Makefile.am create mode 100644 contrib/mitsub/mitsub.c create mode 100644 contrib/vdump/Makefile.am create mode 100644 contrib/vdump/vdump.1 create mode 100644 contrib/vdump/vdump.c create mode 100644 contrib/vdump/vdump.pro create mode 100644 contrib/vips2dj/Makefile.am create mode 100644 contrib/vips2dj/share/Makefile.am create mode 100644 contrib/vips2dj/share/vips2dj/Makefile.am create mode 100644 contrib/vips2dj/share/vips2dj/cmyk/Makefile.am create mode 100644 contrib/vips2dj/share/vips2dj/cmyk/head1 create mode 100644 contrib/vips2dj/share/vips2dj/cmyk/head2 create mode 100644 contrib/vips2dj/share/vips2dj/cmyk/head3 create mode 100644 contrib/vips2dj/share/vips2dj/cmyk/head4 create mode 100644 contrib/vips2dj/share/vips2dj/cmyk/head5 create mode 100644 contrib/vips2dj/share/vips2dj/cmyk/head6 create mode 100644 contrib/vips2dj/share/vips2dj/lab/Makefile.am create mode 100644 contrib/vips2dj/share/vips2dj/lab/head1 create mode 100644 contrib/vips2dj/share/vips2dj/lab/head2 create mode 100644 contrib/vips2dj/share/vips2dj/lab/head3 create mode 100644 contrib/vips2dj/share/vips2dj/lab/head4 create mode 100644 contrib/vips2dj/share/vips2dj/lab/head5 create mode 100644 contrib/vips2dj/share/vips2dj/lab/head6 create mode 100644 contrib/vips2dj/share/vips2dj/mono/Makefile.am create mode 100644 contrib/vips2dj/share/vips2dj/mono/head1 create mode 100644 contrib/vips2dj/share/vips2dj/mono/head2 create mode 100644 contrib/vips2dj/share/vips2dj/mono/head3 create mode 100644 contrib/vips2dj/share/vips2dj/mono/head4 create mode 100644 contrib/vips2dj/share/vips2dj/mono/head5 create mode 100644 contrib/vips2dj/share/vips2dj/mono/head6 create mode 100644 contrib/vips2dj/vips2ah.c create mode 100644 contrib/vips2dj/vips2dj.c create mode 100644 contrib/vips2dj/vips2dj.h create mode 100644 doc/Makefile create mode 100755 doc/Makehtmlman create mode 100644 doc/README create mode 100644 doc/src/Makefile create mode 100644 doc/src/applintro.tex create mode 100644 doc/src/cppintro.tex create mode 100644 doc/src/figs/arch.png create mode 100644 doc/src/figs/arch.svg create mode 100644 doc/src/figs/interconvert.png create mode 100644 doc/src/figs/interconvert.svg create mode 100644 doc/src/fileformat.tex create mode 100644 doc/src/func.tex create mode 100644 doc/src/html.cfg create mode 100644 doc/src/iosys.tex create mode 100644 doc/src/ipio.tex create mode 100644 doc/src/mydefs.tex create mode 100644 doc/src/operintro.tex create mode 100644 doc/src/packages.tex create mode 100644 doc/src/pio.tex create mode 100644 doc/src/refintro.tex create mode 100644 doc/src/vdisplay.tex create mode 100644 doc/src/verror.tex create mode 100644 doc/src/vimage.tex create mode 100644 doc/src/vipsmanual.tex create mode 100644 doc/src/vmask.tex create mode 100644 doc/src/wio.tex create mode 100644 include/Makefile.am create mode 100644 include/vips/Makefile.am create mode 100644 include/vips/VDisplay.h create mode 100644 include/vips/VError.h create mode 100644 include/vips/VImage.h create mode 100644 include/vips/VMask.h create mode 100644 include/vips/colour.h create mode 100644 include/vips/debug.h create mode 100644 include/vips/dispatch.h create mode 100644 include/vips/fmask.h create mode 100644 include/vips/history.h create mode 100644 include/vips/internal.h create mode 100644 include/vips/intl.h create mode 100644 include/vips/meta.h create mode 100644 include/vips/mosaic.h create mode 100644 include/vips/proto.h create mode 100644 include/vips/r_access.h create mode 100644 include/vips/rect.h create mode 100644 include/vips/region.h create mode 100644 include/vips/semaphore.h create mode 100644 include/vips/struct.h create mode 100644 include/vips/thread.h create mode 100644 include/vips/threadgroup.h create mode 100644 include/vips/time.h create mode 100644 include/vips/util.h create mode 100644 include/vips/vbuf.h create mode 100644 include/vips/version.h.in create mode 100644 include/vips/vips create mode 100644 include/vips/vips.h create mode 100644 include/vips/vipsc++.h create mode 100644 include/vips/vipscpp.h create mode 100644 libsrc/Makefile.am create mode 100644 libsrc/acquire/Makefile.am create mode 100644 libsrc/acquire/im_clamp.c create mode 100644 libsrc/acquire/man3/Makefile.am create mode 100644 libsrc/arithmetic/Makefile.am create mode 100644 libsrc/arithmetic/arith_dispatch.c create mode 100644 libsrc/arithmetic/im_abs.c create mode 100644 libsrc/arithmetic/im_add.c create mode 100644 libsrc/arithmetic/im_avg.c create mode 100644 libsrc/arithmetic/im_bandmean.c create mode 100644 libsrc/arithmetic/im_ceil.c create mode 100644 libsrc/arithmetic/im_cmulnorm.c create mode 100644 libsrc/arithmetic/im_costra.c create mode 100644 libsrc/arithmetic/im_deviate.c create mode 100644 libsrc/arithmetic/im_divide.c create mode 100644 libsrc/arithmetic/im_expntra.c create mode 100644 libsrc/arithmetic/im_fav4.c create mode 100644 libsrc/arithmetic/im_floor.c create mode 100644 libsrc/arithmetic/im_gadd.c create mode 100644 libsrc/arithmetic/im_gaddim.c create mode 100644 libsrc/arithmetic/im_gfadd.c create mode 100644 libsrc/arithmetic/im_invert.c create mode 100644 libsrc/arithmetic/im_linreg.c create mode 100644 libsrc/arithmetic/im_lintra.c create mode 100644 libsrc/arithmetic/im_litecor.c create mode 100644 libsrc/arithmetic/im_log10tra.c create mode 100644 libsrc/arithmetic/im_logtra.c create mode 100644 libsrc/arithmetic/im_max.c create mode 100644 libsrc/arithmetic/im_maxpos.c create mode 100644 libsrc/arithmetic/im_maxpos_avg.c create mode 100644 libsrc/arithmetic/im_maxpos_vec.c create mode 100644 libsrc/arithmetic/im_measure.c create mode 100644 libsrc/arithmetic/im_min.c create mode 100644 libsrc/arithmetic/im_minpos.c create mode 100644 libsrc/arithmetic/im_multiply.c create mode 100644 libsrc/arithmetic/im_point_bilinear.c create mode 100644 libsrc/arithmetic/im_powtra.c create mode 100644 libsrc/arithmetic/im_remainder.c create mode 100644 libsrc/arithmetic/im_rint.c create mode 100644 libsrc/arithmetic/im_sign.c create mode 100644 libsrc/arithmetic/im_sintra.c create mode 100644 libsrc/arithmetic/im_stats.c create mode 100644 libsrc/arithmetic/im_subtract.c create mode 100644 libsrc/arithmetic/im_tantra.c create mode 100644 libsrc/arithmetic/man3/Makefile.am create mode 100644 libsrc/arithmetic/man3/im_abs.3 create mode 100644 libsrc/arithmetic/man3/im_acostra.3 create mode 100644 libsrc/arithmetic/man3/im_add.3 create mode 100644 libsrc/arithmetic/man3/im_asintra.3 create mode 100644 libsrc/arithmetic/man3/im_atantra.3 create mode 100644 libsrc/arithmetic/man3/im_avg.3 create mode 100644 libsrc/arithmetic/man3/im_bandmean.3 create mode 100644 libsrc/arithmetic/man3/im_ceil.3 create mode 100644 libsrc/arithmetic/man3/im_cmulnorm.3 create mode 100644 libsrc/arithmetic/man3/im_costra.3 create mode 100644 libsrc/arithmetic/man3/im_deviate.3 create mode 100644 libsrc/arithmetic/man3/im_divide.3 create mode 100644 libsrc/arithmetic/man3/im_exp10tra.3 create mode 100644 libsrc/arithmetic/man3/im_expntra.3 create mode 100644 libsrc/arithmetic/man3/im_expntra_vec.3 create mode 100644 libsrc/arithmetic/man3/im_exptra.3 create mode 100644 libsrc/arithmetic/man3/im_fav4.3 create mode 100644 libsrc/arithmetic/man3/im_floor.3 create mode 100644 libsrc/arithmetic/man3/im_gadd.3 create mode 100644 libsrc/arithmetic/man3/im_gaddim.3 create mode 100644 libsrc/arithmetic/man3/im_gfadd.3 create mode 100644 libsrc/arithmetic/man3/im_invert.3 create mode 100644 libsrc/arithmetic/man3/im_lintra.3 create mode 100644 libsrc/arithmetic/man3/im_lintra_vec.3 create mode 100644 libsrc/arithmetic/man3/im_litecor.3 create mode 100644 libsrc/arithmetic/man3/im_log10tra.3 create mode 100644 libsrc/arithmetic/man3/im_logtra.3 create mode 100644 libsrc/arithmetic/man3/im_max.3 create mode 100644 libsrc/arithmetic/man3/im_maxpos.3 create mode 100644 libsrc/arithmetic/man3/im_maxpos_vec.3 create mode 100644 libsrc/arithmetic/man3/im_measure.3 create mode 100644 libsrc/arithmetic/man3/im_min.3 create mode 100644 libsrc/arithmetic/man3/im_minpos.3 create mode 100644 libsrc/arithmetic/man3/im_minpos_vec.3 create mode 100644 libsrc/arithmetic/man3/im_multiply.3 create mode 100644 libsrc/arithmetic/man3/im_powtra.3 create mode 100644 libsrc/arithmetic/man3/im_powtra_vec.3 create mode 100644 libsrc/arithmetic/man3/im_remainder.3 create mode 100644 libsrc/arithmetic/man3/im_remainderconst.3 create mode 100644 libsrc/arithmetic/man3/im_remainderconst_vec.3 create mode 100644 libsrc/arithmetic/man3/im_rint.3 create mode 100644 libsrc/arithmetic/man3/im_sign.3 create mode 100644 libsrc/arithmetic/man3/im_sintra.3 create mode 100644 libsrc/arithmetic/man3/im_stats.3 create mode 100644 libsrc/arithmetic/man3/im_subtract.3 create mode 100644 libsrc/arithmetic/man3/im_tantra.3 create mode 100644 libsrc/boolean/Makefile.am create mode 100644 libsrc/boolean/bool_dispatch.c create mode 100644 libsrc/boolean/boolean.c create mode 100644 libsrc/boolean/man3/Makefile.am create mode 100644 libsrc/boolean/man3/im_and_vec.3 create mode 100644 libsrc/boolean/man3/im_andconst.3 create mode 100644 libsrc/boolean/man3/im_andimage.3 create mode 100644 libsrc/boolean/man3/im_eor_vec.3 create mode 100644 libsrc/boolean/man3/im_eorconst.3 create mode 100644 libsrc/boolean/man3/im_eorimage.3 create mode 100644 libsrc/boolean/man3/im_or_vec.3 create mode 100644 libsrc/boolean/man3/im_orconst.3 create mode 100644 libsrc/boolean/man3/im_orimage.3 create mode 100644 libsrc/boolean/man3/im_shiftleft.3 create mode 100644 libsrc/boolean/man3/im_shiftright.3 create mode 100644 libsrc/colour/Makefile.am create mode 100644 libsrc/colour/colour.c create mode 100644 libsrc/colour/colour_dispatch.c create mode 100644 libsrc/colour/derived.c create mode 100644 libsrc/colour/im_LCh2Lab.c create mode 100644 libsrc/colour/im_LCh2UCS.c create mode 100644 libsrc/colour/im_Lab2LCh.c create mode 100644 libsrc/colour/im_Lab2LabQ.c create mode 100644 libsrc/colour/im_Lab2LabS.c create mode 100644 libsrc/colour/im_Lab2XYZ.c create mode 100644 libsrc/colour/im_LabQ2Lab.c create mode 100644 libsrc/colour/im_LabQ2LabS.c create mode 100644 libsrc/colour/im_LabQ2disp.c create mode 100644 libsrc/colour/im_LabS2Lab.c create mode 100644 libsrc/colour/im_LabS2LabQ.c create mode 100644 libsrc/colour/im_UCS2LCh.c create mode 100644 libsrc/colour/im_XYZ2Lab.c create mode 100644 libsrc/colour/im_XYZ2Yxy.c create mode 100644 libsrc/colour/im_XYZ2disp.c create mode 100644 libsrc/colour/im_Yxy2XYZ.c create mode 100644 libsrc/colour/im_dE00_fromLab.c create mode 100644 libsrc/colour/im_dECMC_fromLab.c create mode 100644 libsrc/colour/im_dE_fromLab.c create mode 100644 libsrc/colour/im_disp2XYZ.c create mode 100644 libsrc/colour/im_icc_transform.c create mode 100644 libsrc/colour/im_lab_morph.c create mode 100644 libsrc/colour/man3/Makefile.am create mode 100644 libsrc/colour/man3/im_LCh2Lab.3 create mode 100644 libsrc/colour/man3/im_LCh2UCS.3 create mode 100644 libsrc/colour/man3/im_Lab2LCh.3 create mode 100644 libsrc/colour/man3/im_Lab2LabQ.3 create mode 100644 libsrc/colour/man3/im_Lab2LabS.3 create mode 100644 libsrc/colour/man3/im_Lab2UCS.3 create mode 100644 libsrc/colour/man3/im_Lab2XYZ.3 create mode 100644 libsrc/colour/man3/im_Lab2disp.3 create mode 100644 libsrc/colour/man3/im_LabQ2Lab.3 create mode 100644 libsrc/colour/man3/im_LabQ2LabS.3 create mode 100644 libsrc/colour/man3/im_LabQ2XYZ.3 create mode 100644 libsrc/colour/man3/im_LabQ2disp.3 create mode 100644 libsrc/colour/man3/im_LabQ2disp_build_table.3 create mode 100644 libsrc/colour/man3/im_LabQ2disp_table.3 create mode 100644 libsrc/colour/man3/im_LabS2Lab.3 create mode 100644 libsrc/colour/man3/im_LabS2LabQ.3 create mode 100644 libsrc/colour/man3/im_UCS2LCh.3 create mode 100644 libsrc/colour/man3/im_UCS2Lab.3 create mode 100644 libsrc/colour/man3/im_UCS2XYZ.3 create mode 100644 libsrc/colour/man3/im_XYZ2Lab.3 create mode 100644 libsrc/colour/man3/im_XYZ2UCS.3 create mode 100644 libsrc/colour/man3/im_XYZ2Yxy.3 create mode 100644 libsrc/colour/man3/im_XYZ2disp.3 create mode 100644 libsrc/colour/man3/im_XYZ2sRGB.3 create mode 100644 libsrc/colour/man3/im_Yxy2XYZ.3 create mode 100644 libsrc/colour/man3/im_col_C2Cucs.3 create mode 100644 libsrc/colour/man3/im_col_Ch2ab.3 create mode 100644 libsrc/colour/man3/im_col_Ch2hucs.3 create mode 100644 libsrc/colour/man3/im_col_Chucs2h.3 create mode 100644 libsrc/colour/man3/im_col_Cucs2C.3 create mode 100644 libsrc/colour/man3/im_col_L2Lucs.3 create mode 100644 libsrc/colour/man3/im_col_Lab2XYZ.3 create mode 100644 libsrc/colour/man3/im_col_Lucs2L.3 create mode 100644 libsrc/colour/man3/im_col_XYZ2Lab.3 create mode 100644 libsrc/colour/man3/im_col_XYZ2rgb.3 create mode 100644 libsrc/colour/man3/im_col_ab2Ch.3 create mode 100644 libsrc/colour/man3/im_col_dECMC.3 create mode 100644 libsrc/colour/man3/im_col_display.3 create mode 100644 libsrc/colour/man3/im_col_make_tables_RGB.3 create mode 100644 libsrc/colour/man3/im_col_make_tables_UCS.3 create mode 100644 libsrc/colour/man3/im_col_pythagoras.3 create mode 100644 libsrc/colour/man3/im_col_rgb2XYZ.3 create mode 100644 libsrc/colour/man3/im_dE00_fromLab.3 create mode 100644 libsrc/colour/man3/im_dECMC_fromLab.3 create mode 100644 libsrc/colour/man3/im_dECMC_fromdisp.3 create mode 100644 libsrc/colour/man3/im_dE_fromLab.3 create mode 100644 libsrc/colour/man3/im_dE_fromXYZ.3 create mode 100644 libsrc/colour/man3/im_dE_fromdisp.3 create mode 100644 libsrc/colour/man3/im_disp2Lab.3 create mode 100644 libsrc/colour/man3/im_disp2XYZ.3 create mode 100644 libsrc/colour/man3/im_icc_ac2rc.3 create mode 100644 libsrc/colour/man3/im_icc_export.3 create mode 100644 libsrc/colour/man3/im_icc_export_depth.3 create mode 100644 libsrc/colour/man3/im_icc_import.3 create mode 100644 libsrc/colour/man3/im_icc_import_embedded.3 create mode 100644 libsrc/colour/man3/im_icc_present.3 create mode 100644 libsrc/colour/man3/im_icc_transform.3 create mode 100644 libsrc/colour/man3/im_lab_morph.3 create mode 100644 libsrc/colour/man3/im_sRGB2XYZ.3 create mode 100644 libsrc/conversion/Makefile.am create mode 100644 libsrc/conversion/conver_dispatch.c create mode 100644 libsrc/conversion/dbh.h create mode 100644 libsrc/conversion/im_analyze2vips.c create mode 100644 libsrc/conversion/im_bandjoin.c create mode 100644 libsrc/conversion/im_bernd.c create mode 100644 libsrc/conversion/im_black.c create mode 100644 libsrc/conversion/im_c2amph.c create mode 100644 libsrc/conversion/im_c2imag.c create mode 100644 libsrc/conversion/im_c2ps.c create mode 100644 libsrc/conversion/im_c2real.c create mode 100644 libsrc/conversion/im_c2rect.c create mode 100644 libsrc/conversion/im_clip.c create mode 100644 libsrc/conversion/im_copy.c create mode 100644 libsrc/conversion/im_csv2vips.c create mode 100644 libsrc/conversion/im_exr2vips.c create mode 100644 libsrc/conversion/im_extract.c create mode 100644 libsrc/conversion/im_falsecolour.c create mode 100644 libsrc/conversion/im_fliphor.c create mode 100644 libsrc/conversion/im_flipver.c create mode 100644 libsrc/conversion/im_gbandjoin.c create mode 100644 libsrc/conversion/im_grid.c create mode 100644 libsrc/conversion/im_insert.c create mode 100644 libsrc/conversion/im_jpeg2vips.c create mode 100644 libsrc/conversion/im_lrjoin.c create mode 100644 libsrc/conversion/im_magick2vips.c create mode 100644 libsrc/conversion/im_mask2vips.c create mode 100644 libsrc/conversion/im_msb.c create mode 100644 libsrc/conversion/im_png2vips.c create mode 100644 libsrc/conversion/im_ppm2vips.c create mode 100644 libsrc/conversion/im_print.c create mode 100644 libsrc/conversion/im_raw2vips.c create mode 100644 libsrc/conversion/im_recomb.c create mode 100644 libsrc/conversion/im_replicate.c create mode 100644 libsrc/conversion/im_ri2c.c create mode 100644 libsrc/conversion/im_rightshift_size.c create mode 100644 libsrc/conversion/im_rot180.c create mode 100644 libsrc/conversion/im_rot270.c create mode 100644 libsrc/conversion/im_rot90.c create mode 100644 libsrc/conversion/im_scale.c create mode 100644 libsrc/conversion/im_scaleps.c create mode 100644 libsrc/conversion/im_slice.c create mode 100644 libsrc/conversion/im_subsample.c create mode 100644 libsrc/conversion/im_system.c create mode 100644 libsrc/conversion/im_tbjoin.c create mode 100644 libsrc/conversion/im_text.c create mode 100644 libsrc/conversion/im_thresh.c create mode 100644 libsrc/conversion/im_tiff2vips.c create mode 100644 libsrc/conversion/im_tile_cache.c create mode 100644 libsrc/conversion/im_vips2csv.c create mode 100644 libsrc/conversion/im_vips2jpeg.c create mode 100644 libsrc/conversion/im_vips2mask.c create mode 100644 libsrc/conversion/im_vips2png.c create mode 100644 libsrc/conversion/im_vips2ppm.c create mode 100644 libsrc/conversion/im_vips2tiff.c create mode 100644 libsrc/conversion/im_zoom.c create mode 100644 libsrc/conversion/man3/Makefile.am create mode 100644 libsrc/conversion/man3/im_analyze2vips.3 create mode 100644 libsrc/conversion/man3/im_bandjoin.3 create mode 100644 libsrc/conversion/man3/im_black.3 create mode 100644 libsrc/conversion/man3/im_c2amph.3 create mode 100644 libsrc/conversion/man3/im_c2imag.3 create mode 100644 libsrc/conversion/man3/im_c2ps.3 create mode 100644 libsrc/conversion/man3/im_c2real.3 create mode 100644 libsrc/conversion/man3/im_c2rect.3 create mode 100644 libsrc/conversion/man3/im_clip.3 create mode 100644 libsrc/conversion/man3/im_clip2c.3 create mode 100644 libsrc/conversion/man3/im_clip2cm.3 create mode 100644 libsrc/conversion/man3/im_clip2d.3 create mode 100644 libsrc/conversion/man3/im_clip2dcm.3 create mode 100644 libsrc/conversion/man3/im_clip2f.3 create mode 100644 libsrc/conversion/man3/im_clip2fmt.3 create mode 100644 libsrc/conversion/man3/im_clip2i.3 create mode 100644 libsrc/conversion/man3/im_clip2s.3 create mode 100644 libsrc/conversion/man3/im_clip2ui.3 create mode 100644 libsrc/conversion/man3/im_clip2us.3 create mode 100644 libsrc/conversion/man3/im_copy.3 create mode 100644 libsrc/conversion/man3/im_copy_from.3 create mode 100644 libsrc/conversion/man3/im_copy_morph.3 create mode 100644 libsrc/conversion/man3/im_copy_set.3 create mode 100644 libsrc/conversion/man3/im_copy_set_meta.3 create mode 100644 libsrc/conversion/man3/im_copy_swap.3 create mode 100644 libsrc/conversion/man3/im_csv2vips.3 create mode 100644 libsrc/conversion/man3/im_csv2vips_header.3 create mode 100644 libsrc/conversion/man3/im_exr2vips.3 create mode 100644 libsrc/conversion/man3/im_exr2vips_header.3 create mode 100644 libsrc/conversion/man3/im_extract.3 create mode 100644 libsrc/conversion/man3/im_extract_area.3 create mode 100644 libsrc/conversion/man3/im_extract_areabands.3 create mode 100644 libsrc/conversion/man3/im_extract_bands.3 create mode 100644 libsrc/conversion/man3/im_falsecolour.3 create mode 100644 libsrc/conversion/man3/im_fliphor.3 create mode 100644 libsrc/conversion/man3/im_flipver.3 create mode 100644 libsrc/conversion/man3/im_gbandjoin.3 create mode 100644 libsrc/conversion/man3/im_grid.3 create mode 100644 libsrc/conversion/man3/im_insert.3 create mode 100644 libsrc/conversion/man3/im_istiff.3 create mode 100644 libsrc/conversion/man3/im_jpeg2vips.3 create mode 100644 libsrc/conversion/man3/im_jpeg2vips_header.3 create mode 100644 libsrc/conversion/man3/im_lrjoin.3 create mode 100644 libsrc/conversion/man3/im_magick2vips.3 create mode 100644 libsrc/conversion/man3/im_magick2vips_header.3 create mode 100644 libsrc/conversion/man3/im_mask2vips.3 create mode 100644 libsrc/conversion/man3/im_msb.3 create mode 100644 libsrc/conversion/man3/im_msb_band.3 create mode 100644 libsrc/conversion/man3/im_png2vips.3 create mode 100644 libsrc/conversion/man3/im_png2vips_header.3 create mode 100644 libsrc/conversion/man3/im_ppm2vips.3 create mode 100644 libsrc/conversion/man3/im_ppm2vips_header.3 create mode 100644 libsrc/conversion/man3/im_print.3 create mode 100644 libsrc/conversion/man3/im_raw2vips.3 create mode 100644 libsrc/conversion/man3/im_recomb.3 create mode 100644 libsrc/conversion/man3/im_replicate.3 create mode 100644 libsrc/conversion/man3/im_ri2c.3 create mode 100644 libsrc/conversion/man3/im_rightshift_size.3 create mode 100644 libsrc/conversion/man3/im_rot180.3 create mode 100644 libsrc/conversion/man3/im_rot270.3 create mode 100644 libsrc/conversion/man3/im_rot90.3 create mode 100644 libsrc/conversion/man3/im_scale.3 create mode 100644 libsrc/conversion/man3/im_scaleps.3 create mode 100644 libsrc/conversion/man3/im_slice.3 create mode 100644 libsrc/conversion/man3/im_subsample.3 create mode 100644 libsrc/conversion/man3/im_system.3 create mode 100644 libsrc/conversion/man3/im_tbjoin.3 create mode 100644 libsrc/conversion/man3/im_text.3 create mode 100644 libsrc/conversion/man3/im_thresh.3 create mode 100644 libsrc/conversion/man3/im_tiff2vips.3 create mode 100644 libsrc/conversion/man3/im_tiff2vips_header.3 create mode 100644 libsrc/conversion/man3/im_tile_cache.3 create mode 100644 libsrc/conversion/man3/im_vips2bufjpeg.3 create mode 100644 libsrc/conversion/man3/im_vips2csv.3 create mode 100644 libsrc/conversion/man3/im_vips2jpeg.3 create mode 100644 libsrc/conversion/man3/im_vips2mask.3 create mode 100644 libsrc/conversion/man3/im_vips2mimejpeg.3 create mode 100644 libsrc/conversion/man3/im_vips2png.3 create mode 100644 libsrc/conversion/man3/im_vips2ppm.3 create mode 100644 libsrc/conversion/man3/im_vips2tiff.3 create mode 100644 libsrc/conversion/man3/im_zoom.3 create mode 100644 libsrc/convolution/Makefile.am create mode 100644 libsrc/convolution/convol_dispatch.c create mode 100644 libsrc/convolution/im_addgnoise.c create mode 100644 libsrc/convolution/im_compass.c create mode 100644 libsrc/convolution/im_contrast_surface.c create mode 100644 libsrc/convolution/im_conv.c create mode 100644 libsrc/convolution/im_convf.c create mode 100644 libsrc/convolution/im_convsep.c create mode 100644 libsrc/convolution/im_convsepf.c create mode 100644 libsrc/convolution/im_convsub.c create mode 100644 libsrc/convolution/im_embed.c create mode 100644 libsrc/convolution/im_fastcor.c create mode 100644 libsrc/convolution/im_gaussmasks.c create mode 100644 libsrc/convolution/im_gaussnoise.c create mode 100644 libsrc/convolution/im_gradcor.c create mode 100644 libsrc/convolution/im_logmasks.c create mode 100644 libsrc/convolution/im_mpercent.c create mode 100644 libsrc/convolution/im_rank.c create mode 100644 libsrc/convolution/im_rank_image.c create mode 100644 libsrc/convolution/im_resize_linear.c create mode 100644 libsrc/convolution/im_sharpen.c create mode 100644 libsrc/convolution/im_shrink.c create mode 100644 libsrc/convolution/im_spcor.c create mode 100644 libsrc/convolution/im_stretch3.c create mode 100644 libsrc/convolution/im_zerox.c create mode 100644 libsrc/convolution/man3/Makefile.am create mode 100644 libsrc/convolution/man3/im_addgnoise.3 create mode 100644 libsrc/convolution/man3/im_compass.3 create mode 100644 libsrc/convolution/man3/im_contrast_surface.3 create mode 100644 libsrc/convolution/man3/im_contrast_surface_raw.3 create mode 100644 libsrc/convolution/man3/im_conv.3 create mode 100644 libsrc/convolution/man3/im_conv_raw.3 create mode 100644 libsrc/convolution/man3/im_convf.3 create mode 100644 libsrc/convolution/man3/im_convf_raw.3 create mode 100644 libsrc/convolution/man3/im_convsep.3 create mode 100644 libsrc/convolution/man3/im_convsep_raw.3 create mode 100644 libsrc/convolution/man3/im_convsepf.3 create mode 100644 libsrc/convolution/man3/im_convsepf_raw.3 create mode 100644 libsrc/convolution/man3/im_convsub.3 create mode 100644 libsrc/convolution/man3/im_create_dmask.3 create mode 100644 libsrc/convolution/man3/im_create_imask.3 create mode 100644 libsrc/convolution/man3/im_dup_dmask.3 create mode 100644 libsrc/convolution/man3/im_dup_imask.3 create mode 100644 libsrc/convolution/man3/im_embed.3 create mode 100644 libsrc/convolution/man3/im_fastcor.3 create mode 100644 libsrc/convolution/man3/im_free_dmask.3 create mode 100644 libsrc/convolution/man3/im_free_imask.3 create mode 100644 libsrc/convolution/man3/im_gauss_dmask.3 create mode 100644 libsrc/convolution/man3/im_gauss_imask.3 create mode 100644 libsrc/convolution/man3/im_gaussnoise.3 create mode 100644 libsrc/convolution/man3/im_gradient.3 create mode 100644 libsrc/convolution/man3/im_lindetect.3 create mode 100644 libsrc/convolution/man3/im_log_dmask.3 create mode 100644 libsrc/convolution/man3/im_log_imask.3 create mode 100644 libsrc/convolution/man3/im_lowpass.3 create mode 100644 libsrc/convolution/man3/im_maxvalue.3 create mode 100644 libsrc/convolution/man3/im_mpercent.3 create mode 100644 libsrc/convolution/man3/im_norm_dmask.3 create mode 100644 libsrc/convolution/man3/im_offsets45.3 create mode 100644 libsrc/convolution/man3/im_offsets90.3 create mode 100644 libsrc/convolution/man3/im_print_dmask.3 create mode 100644 libsrc/convolution/man3/im_print_imask.3 create mode 100644 libsrc/convolution/man3/im_rank.3 create mode 100644 libsrc/convolution/man3/im_rank_image.3 create mode 100644 libsrc/convolution/man3/im_read_dmask.3 create mode 100644 libsrc/convolution/man3/im_read_imask.3 create mode 100644 libsrc/convolution/man3/im_rotate_dmask45.3 create mode 100644 libsrc/convolution/man3/im_rotate_dmask90.3 create mode 100644 libsrc/convolution/man3/im_rotate_imask45.3 create mode 100644 libsrc/convolution/man3/im_rotate_imask90.3 create mode 100644 libsrc/convolution/man3/im_scale_dmask.3 create mode 100644 libsrc/convolution/man3/im_sharpen.3 create mode 100644 libsrc/convolution/man3/im_shrink.3 create mode 100644 libsrc/convolution/man3/im_spcor.3 create mode 100644 libsrc/convolution/man3/im_stretch3.3 create mode 100644 libsrc/convolution/man3/im_write_dmask.3 create mode 100644 libsrc/convolution/man3/im_write_dmask_name.3 create mode 100644 libsrc/convolution/man3/im_write_imask.3 create mode 100644 libsrc/convolution/man3/im_write_imask_name.3 create mode 100644 libsrc/convolution/man3/im_zerox.3 create mode 100644 libsrc/convolution/rotmask.c create mode 100644 libsrc/convolution/rw_mask.c create mode 100644 libsrc/dummy.c create mode 100644 libsrc/freq_filt/Makefile.am create mode 100644 libsrc/freq_filt/fft_sp.c create mode 100644 libsrc/freq_filt/fmask4th.c create mode 100644 libsrc/freq_filt/fmaskcir.c create mode 100644 libsrc/freq_filt/freq_dispatch.c create mode 100644 libsrc/freq_filt/im_disp_ps.c create mode 100644 libsrc/freq_filt/im_fractsurf.c create mode 100644 libsrc/freq_filt/im_freq_mask.c create mode 100644 libsrc/freq_filt/im_freqflt.c create mode 100644 libsrc/freq_filt/im_fwfft.c create mode 100644 libsrc/freq_filt/im_invfft.c create mode 100644 libsrc/freq_filt/im_invfftr.c create mode 100644 libsrc/freq_filt/im_rotquad.c create mode 100644 libsrc/freq_filt/man3/Makefile.am create mode 100644 libsrc/freq_filt/man3/im_create_fmask.3 create mode 100644 libsrc/freq_filt/man3/im_disp_ps.3 create mode 100644 libsrc/freq_filt/man3/im_flt_imag_freq.3 create mode 100644 libsrc/freq_filt/man3/im_fractsurf.3 create mode 100644 libsrc/freq_filt/man3/im_freqflt.3 create mode 100644 libsrc/freq_filt/man3/im_fwfft.3 create mode 100644 libsrc/freq_filt/man3/im_invfft.3 create mode 100644 libsrc/freq_filt/man3/im_invfftr.3 create mode 100644 libsrc/freq_filt/man3/im_rotquad.3 create mode 100644 libsrc/histograms_lut/Makefile.am create mode 100644 libsrc/histograms_lut/hist_dispatch.c create mode 100644 libsrc/histograms_lut/im_buildlut.c create mode 100644 libsrc/histograms_lut/im_gammacorrect.c create mode 100644 libsrc/histograms_lut/im_heq.c create mode 100644 libsrc/histograms_lut/im_hist.c create mode 100644 libsrc/histograms_lut/im_histeq.c create mode 100644 libsrc/histograms_lut/im_histgr.c create mode 100644 libsrc/histograms_lut/im_histnD.c create mode 100644 libsrc/histograms_lut/im_histplot.c create mode 100644 libsrc/histograms_lut/im_histspec.c create mode 100644 libsrc/histograms_lut/im_hsp.c create mode 100644 libsrc/histograms_lut/im_identity.c create mode 100644 libsrc/histograms_lut/im_invertlut.c create mode 100644 libsrc/histograms_lut/im_lhisteq.c create mode 100644 libsrc/histograms_lut/im_maplut.c create mode 100644 libsrc/histograms_lut/im_project.c create mode 100644 libsrc/histograms_lut/im_stdif.c create mode 100644 libsrc/histograms_lut/man3/Makefile.am create mode 100644 libsrc/histograms_lut/man3/im_buildlut.3 create mode 100644 libsrc/histograms_lut/man3/im_gammacorrect.3 create mode 100644 libsrc/histograms_lut/man3/im_heq.3 create mode 100644 libsrc/histograms_lut/man3/im_hist.3 create mode 100644 libsrc/histograms_lut/man3/im_histcum.3 create mode 100644 libsrc/histograms_lut/man3/im_histeq.3 create mode 100644 libsrc/histograms_lut/man3/im_histgr.3 create mode 100644 libsrc/histograms_lut/man3/im_histnD.3 create mode 100644 libsrc/histograms_lut/man3/im_histnorm.3 create mode 100644 libsrc/histograms_lut/man3/im_histplot.3 create mode 100644 libsrc/histograms_lut/man3/im_histspec.3 create mode 100644 libsrc/histograms_lut/man3/im_hsp.3 create mode 100644 libsrc/histograms_lut/man3/im_identity.3 create mode 100644 libsrc/histograms_lut/man3/im_identity_ushort.3 create mode 100644 libsrc/histograms_lut/man3/im_invertlut.3 create mode 100644 libsrc/histograms_lut/man3/im_lhisteq.3 create mode 100644 libsrc/histograms_lut/man3/im_lhisteq_raw.3 create mode 100644 libsrc/histograms_lut/man3/im_maplut.3 create mode 100644 libsrc/histograms_lut/man3/im_project.3 create mode 100644 libsrc/histograms_lut/man3/im_stdif.3 create mode 100644 libsrc/histograms_lut/man3/im_tone_analyse.3 create mode 100644 libsrc/histograms_lut/man3/im_tone_build.3 create mode 100644 libsrc/histograms_lut/man3/im_tone_map.3 create mode 100644 libsrc/histograms_lut/tone.c create mode 100644 libsrc/inplace/Makefile.am create mode 100644 libsrc/inplace/im_circle.c create mode 100644 libsrc/inplace/im_flood.c create mode 100644 libsrc/inplace/im_insertplace.c create mode 100644 libsrc/inplace/im_line.c create mode 100644 libsrc/inplace/im_paintrect.c create mode 100644 libsrc/inplace/im_plotmask.c create mode 100644 libsrc/inplace/inplace_dispatch.c create mode 100644 libsrc/inplace/line_draw.c create mode 100644 libsrc/inplace/man3/Makefile.am create mode 100644 libsrc/inplace/man3/im_circle.3 create mode 100644 libsrc/inplace/man3/im_fastline.3 create mode 100644 libsrc/inplace/man3/im_fastlineuser.3 create mode 100644 libsrc/inplace/man3/im_flood.3 create mode 100644 libsrc/inplace/man3/im_flood_blob.3 create mode 100644 libsrc/inplace/man3/im_insertplace.3 create mode 100644 libsrc/inplace/man3/im_line.3 create mode 100644 libsrc/inplace/man3/im_lineset.3 create mode 100644 libsrc/inplace/man3/im_paintrect.3 create mode 100644 libsrc/inplace/man3/im_plotmask.3 create mode 100644 libsrc/inplace/man3/im_plotpoint.3 create mode 100644 libsrc/inplace/man3/im_readpoint.3 create mode 100644 libsrc/inplace/man3/im_smear.3 create mode 100644 libsrc/inplace/man3/im_smudge.3 create mode 100644 libsrc/inplace/plot_point.c create mode 100644 libsrc/inplace/smudge_area.c create mode 100644 libsrc/iofuncs/Makefile.am create mode 100644 libsrc/iofuncs/base64.c create mode 100644 libsrc/iofuncs/base64.h create mode 100644 libsrc/iofuncs/buffer.c create mode 100644 libsrc/iofuncs/callback.c create mode 100644 libsrc/iofuncs/debug.c create mode 100644 libsrc/iofuncs/dispatch_types.c create mode 100644 libsrc/iofuncs/error.c create mode 100644 libsrc/iofuncs/error_exit.c create mode 100644 libsrc/iofuncs/im_binfile.c create mode 100644 libsrc/iofuncs/im_bits_of_fmt.c create mode 100644 libsrc/iofuncs/im_close.c create mode 100644 libsrc/iofuncs/im_cp_desc.c create mode 100644 libsrc/iofuncs/im_debugim.c create mode 100644 libsrc/iofuncs/im_demand_hint.c create mode 100644 libsrc/iofuncs/im_desc_hd.c create mode 100644 libsrc/iofuncs/im_generate.c create mode 100644 libsrc/iofuncs/im_guess_prefix.c create mode 100644 libsrc/iofuncs/im_header.c create mode 100644 libsrc/iofuncs/im_histlin.c create mode 100644 libsrc/iofuncs/im_image.c create mode 100644 libsrc/iofuncs/im_init.c create mode 100644 libsrc/iofuncs/im_init_world.c create mode 100644 libsrc/iofuncs/im_initdesc.c create mode 100644 libsrc/iofuncs/im_iocheck.c create mode 100644 libsrc/iofuncs/im_iterate.c create mode 100644 libsrc/iofuncs/im_makerw.c create mode 100644 libsrc/iofuncs/im_mapfile.c create mode 100644 libsrc/iofuncs/im_open.c create mode 100644 libsrc/iofuncs/im_openin.c create mode 100644 libsrc/iofuncs/im_openout.c create mode 100644 libsrc/iofuncs/im_partial.c create mode 100644 libsrc/iofuncs/im_piocheck.c create mode 100644 libsrc/iofuncs/im_prepare.c create mode 100644 libsrc/iofuncs/im_printdesc.c create mode 100644 libsrc/iofuncs/im_printlines.c create mode 100644 libsrc/iofuncs/im_readhist.c create mode 100644 libsrc/iofuncs/im_render.c create mode 100644 libsrc/iofuncs/im_setbox.c create mode 100644 libsrc/iofuncs/im_setbuf.c create mode 100644 libsrc/iofuncs/im_setupout.c create mode 100644 libsrc/iofuncs/im_unmapfile.c create mode 100644 libsrc/iofuncs/im_updatehist.c create mode 100644 libsrc/iofuncs/im_wrapmany.c create mode 100644 libsrc/iofuncs/im_wrapone.c create mode 100644 libsrc/iofuncs/im_writeline.c create mode 100644 libsrc/iofuncs/man3/IM_ARRAY.3 create mode 100644 libsrc/iofuncs/man3/IM_IMAGE_ADDR.3 create mode 100644 libsrc/iofuncs/man3/IM_IMAGE_N_ELEMENTS.3 create mode 100644 libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_ELEMENT.3 create mode 100644 libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_LINE.3 create mode 100644 libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_PEL.3 create mode 100644 libsrc/iofuncs/man3/IM_MAX.3 create mode 100644 libsrc/iofuncs/man3/IM_MIN.3 create mode 100644 libsrc/iofuncs/man3/IM_NEW.3 create mode 100644 libsrc/iofuncs/man3/IM_NUMBER.3 create mode 100644 libsrc/iofuncs/man3/IM_RECT_BOTTOM.3 create mode 100644 libsrc/iofuncs/man3/IM_RECT_HCENTRE.3 create mode 100644 libsrc/iofuncs/man3/IM_RECT_RIGHT.3 create mode 100644 libsrc/iofuncs/man3/IM_RECT_VCENTRE.3 create mode 100644 libsrc/iofuncs/man3/IM_REGION_ADDR.3 create mode 100644 libsrc/iofuncs/man3/IM_REGION_LSKIP.3 create mode 100644 libsrc/iofuncs/man3/IM_REGION_N_ELEMENTS.3 create mode 100644 libsrc/iofuncs/man3/IM_REGION_SIZEOF_LINE.3 create mode 100644 libsrc/iofuncs/man3/IM_RINT.3 create mode 100644 libsrc/iofuncs/man3/Makefile.am create mode 100644 libsrc/iofuncs/man3/error_exit.3 create mode 100644 libsrc/iofuncs/man3/im_BandFmt2char.3 create mode 100644 libsrc/iofuncs/man3/im_Coding2char.3 create mode 100644 libsrc/iofuncs/man3/im_Compression2char.3 create mode 100644 libsrc/iofuncs/man3/im_Type2char.3 create mode 100644 libsrc/iofuncs/man3/im_add_close_callback.3 create mode 100644 libsrc/iofuncs/man3/im_add_eval_callback.3 create mode 100644 libsrc/iofuncs/man3/im_add_evalend_callback.3 create mode 100644 libsrc/iofuncs/man3/im_allocate_input_array.3 create mode 100644 libsrc/iofuncs/man3/im_amiMSBfirst.3 create mode 100644 libsrc/iofuncs/man3/im_binfile.3 create mode 100644 libsrc/iofuncs/man3/im_bits_of_fmt.3 create mode 100644 libsrc/iofuncs/man3/im_cache.3 create mode 100644 libsrc/iofuncs/man3/im_char2BandFmt.3 create mode 100644 libsrc/iofuncs/man3/im_char2Coding.3 create mode 100644 libsrc/iofuncs/man3/im_char2Compression.3 create mode 100644 libsrc/iofuncs/man3/im_char2Type.3 create mode 100644 libsrc/iofuncs/man3/im_close.3 create mode 100644 libsrc/iofuncs/man3/im_concurrency_get.3 create mode 100644 libsrc/iofuncs/man3/im_concurrency_set.3 create mode 100644 libsrc/iofuncs/man3/im_cp_desc.3 create mode 100644 libsrc/iofuncs/man3/im_cp_desc_array.3 create mode 100644 libsrc/iofuncs/man3/im_cp_descv.3 create mode 100644 libsrc/iofuncs/man3/im_debugim.3 create mode 100644 libsrc/iofuncs/man3/im_demand_hint.3 create mode 100644 libsrc/iofuncs/man3/im_demand_hint_array.3 create mode 100644 libsrc/iofuncs/man3/im_diag.3 create mode 100644 libsrc/iofuncs/man3/im_error.3 create mode 100644 libsrc/iofuncs/man3/im_error_buffer.3 create mode 100644 libsrc/iofuncs/man3/im_error_clear.3 create mode 100644 libsrc/iofuncs/man3/im_free.3 create mode 100644 libsrc/iofuncs/man3/im_generate.3 create mode 100644 libsrc/iofuncs/man3/im_get_option_group.3 create mode 100644 libsrc/iofuncs/man3/im_guess_prefix.3 create mode 100644 libsrc/iofuncs/man3/im_header.3 create mode 100644 libsrc/iofuncs/man3/im_header_double.3 create mode 100644 libsrc/iofuncs/man3/im_header_get.3 create mode 100644 libsrc/iofuncs/man3/im_header_get_type.3 create mode 100644 libsrc/iofuncs/man3/im_header_int.3 create mode 100644 libsrc/iofuncs/man3/im_header_map.3 create mode 100644 libsrc/iofuncs/man3/im_header_string.3 create mode 100644 libsrc/iofuncs/man3/im_histlin.3 create mode 100644 libsrc/iofuncs/man3/im_history_get.3 create mode 100644 libsrc/iofuncs/man3/im_image.3 create mode 100644 libsrc/iofuncs/man3/im_image_sanity.3 create mode 100644 libsrc/iofuncs/man3/im_incheck.3 create mode 100644 libsrc/iofuncs/man3/im_init.3 create mode 100644 libsrc/iofuncs/man3/im_init_world.3 create mode 100644 libsrc/iofuncs/man3/im_initdesc.3 create mode 100644 libsrc/iofuncs/man3/im_invalidate.3 create mode 100644 libsrc/iofuncs/man3/im_iocheck.3 create mode 100644 libsrc/iofuncs/man3/im_isMSBfirst.3 create mode 100644 libsrc/iofuncs/man3/im_iscomplex.3 create mode 100644 libsrc/iofuncs/man3/im_isfile.3 create mode 100644 libsrc/iofuncs/man3/im_isfloat.3 create mode 100644 libsrc/iofuncs/man3/im_isint.3 create mode 100644 libsrc/iofuncs/man3/im_isjpeg.3 create mode 100644 libsrc/iofuncs/man3/im_ispartial.3 create mode 100644 libsrc/iofuncs/man3/im_ispng.3 create mode 100644 libsrc/iofuncs/man3/im_isppm.3 create mode 100644 libsrc/iofuncs/man3/im_isscalar.3 create mode 100644 libsrc/iofuncs/man3/im_istifftiled.3 create mode 100644 libsrc/iofuncs/man3/im_isuint.3 create mode 100644 libsrc/iofuncs/man3/im_isvips.3 create mode 100644 libsrc/iofuncs/man3/im_iterate.3 create mode 100644 libsrc/iofuncs/man3/im_list_add.3 create mode 100644 libsrc/iofuncs/man3/im_list_append.3 create mode 100644 libsrc/iofuncs/man3/im_list_eq.3 create mode 100644 libsrc/iofuncs/man3/im_list_fix.3 create mode 100644 libsrc/iofuncs/man3/im_list_fold.3 create mode 100644 libsrc/iofuncs/man3/im_list_free.3 create mode 100644 libsrc/iofuncs/man3/im_list_index.3 create mode 100644 libsrc/iofuncs/man3/im_list_insert.3 create mode 100644 libsrc/iofuncs/man3/im_list_len.3 create mode 100644 libsrc/iofuncs/man3/im_list_map.3 create mode 100644 libsrc/iofuncs/man3/im_list_map_rev.3 create mode 100644 libsrc/iofuncs/man3/im_list_member.3 create mode 100644 libsrc/iofuncs/man3/im_list_pos.3 create mode 100644 libsrc/iofuncs/man3/im_list_remove.3 create mode 100644 libsrc/iofuncs/man3/im_makerw.3 create mode 100644 libsrc/iofuncs/man3/im_malloc.3 create mode 100644 libsrc/iofuncs/man3/im_meta.3 create mode 100644 libsrc/iofuncs/man3/im_meta_get.3 create mode 100644 libsrc/iofuncs/man3/im_meta_get_area.3 create mode 100644 libsrc/iofuncs/man3/im_meta_get_blob.3 create mode 100644 libsrc/iofuncs/man3/im_meta_get_double.3 create mode 100644 libsrc/iofuncs/man3/im_meta_get_int.3 create mode 100644 libsrc/iofuncs/man3/im_meta_get_string.3 create mode 100644 libsrc/iofuncs/man3/im_meta_get_type.3 create mode 100644 libsrc/iofuncs/man3/im_meta_set.3 create mode 100644 libsrc/iofuncs/man3/im_meta_set_area.3 create mode 100644 libsrc/iofuncs/man3/im_meta_set_blob.3 create mode 100644 libsrc/iofuncs/man3/im_meta_set_double.3 create mode 100644 libsrc/iofuncs/man3/im_meta_set_int.3 create mode 100644 libsrc/iofuncs/man3/im_meta_set_string.3 create mode 100644 libsrc/iofuncs/man3/im_mmapin.3 create mode 100644 libsrc/iofuncs/man3/im_mmapinrw.3 create mode 100644 libsrc/iofuncs/man3/im_open.3 create mode 100644 libsrc/iofuncs/man3/im_open_local.3 create mode 100644 libsrc/iofuncs/man3/im_open_local_array.3 create mode 100644 libsrc/iofuncs/man3/im_openout.3 create mode 100644 libsrc/iofuncs/man3/im_outcheck.3 create mode 100644 libsrc/iofuncs/man3/im_partial.3 create mode 100644 libsrc/iofuncs/man3/im_pincheck.3 create mode 100644 libsrc/iofuncs/man3/im_piocheck.3 create mode 100644 libsrc/iofuncs/man3/im_poutcheck.3 create mode 100644 libsrc/iofuncs/man3/im_prepare.3 create mode 100644 libsrc/iofuncs/man3/im_prepare_many.3 create mode 100644 libsrc/iofuncs/man3/im_prepare_to.3 create mode 100644 libsrc/iofuncs/man3/im_printdesc.3 create mode 100644 libsrc/iofuncs/man3/im_printlines.3 create mode 100644 libsrc/iofuncs/man3/im_rect_dup.3 create mode 100644 libsrc/iofuncs/man3/im_rect_equalsrect.3 create mode 100644 libsrc/iofuncs/man3/im_rect_includespoint.3 create mode 100644 libsrc/iofuncs/man3/im_rect_includesrect.3 create mode 100644 libsrc/iofuncs/man3/im_rect_intersectrect.3 create mode 100644 libsrc/iofuncs/man3/im_rect_isempty.3 create mode 100644 libsrc/iofuncs/man3/im_rect_marginadjust.3 create mode 100644 libsrc/iofuncs/man3/im_rect_normalise.3 create mode 100644 libsrc/iofuncs/man3/im_rect_unionrect.3 create mode 100644 libsrc/iofuncs/man3/im_region_buffer.3 create mode 100644 libsrc/iofuncs/man3/im_region_create.3 create mode 100644 libsrc/iofuncs/man3/im_region_free.3 create mode 100644 libsrc/iofuncs/man3/im_region_image.3 create mode 100644 libsrc/iofuncs/man3/im_region_position.3 create mode 100644 libsrc/iofuncs/man3/im_region_region.3 create mode 100644 libsrc/iofuncs/man3/im_render.3 create mode 100644 libsrc/iofuncs/man3/im_render_fade.3 create mode 100644 libsrc/iofuncs/man3/im_setbuf.3 create mode 100644 libsrc/iofuncs/man3/im_setupout.3 create mode 100644 libsrc/iofuncs/man3/im_start_many.3 create mode 100644 libsrc/iofuncs/man3/im_start_one.3 create mode 100644 libsrc/iofuncs/man3/im_stop_many.3 create mode 100644 libsrc/iofuncs/man3/im_stop_one.3 create mode 100644 libsrc/iofuncs/man3/im_updatehist.3 create mode 100644 libsrc/iofuncs/man3/im_verror.3 create mode 100644 libsrc/iofuncs/man3/im_version.3 create mode 100644 libsrc/iofuncs/man3/im_version_string.3 create mode 100644 libsrc/iofuncs/man3/im_warn.3 create mode 100644 libsrc/iofuncs/man3/im_wrapmany.3 create mode 100644 libsrc/iofuncs/man3/im_wrapone.3 create mode 100644 libsrc/iofuncs/man3/im_writeline.3 create mode 100644 libsrc/iofuncs/memory.c create mode 100644 libsrc/iofuncs/meta.c create mode 100644 libsrc/iofuncs/package.c create mode 100644 libsrc/iofuncs/predicate.c create mode 100644 libsrc/iofuncs/rect.c create mode 100644 libsrc/iofuncs/region.c create mode 100644 libsrc/iofuncs/semaphore.c create mode 100644 libsrc/iofuncs/threadgroup.c create mode 100644 libsrc/iofuncs/time.c create mode 100644 libsrc/iofuncs/util.c create mode 100644 libsrc/iofuncs/vbuf.c create mode 100644 libsrc/iofuncs/window.c create mode 100755 libsrc/makedef.pl create mode 100644 libsrc/matrix/Makefile.am create mode 100644 libsrc/matrix/im_matcat.c create mode 100644 libsrc/matrix/im_matinv.c create mode 100644 libsrc/matrix/im_matmul.c create mode 100644 libsrc/matrix/im_mattrn.c create mode 100644 libsrc/matrix/man3/Makefile.am create mode 100644 libsrc/matrix/man3/im_lu_decomp.3 create mode 100644 libsrc/matrix/man3/im_lu_solve.3 create mode 100644 libsrc/matrix/man3/im_matcat.3 create mode 100644 libsrc/matrix/man3/im_matinv.3 create mode 100644 libsrc/matrix/man3/im_matinv_inplace.3 create mode 100644 libsrc/matrix/man3/im_matmul.3 create mode 100644 libsrc/matrix/man3/im_mattrn.3 create mode 100644 libsrc/matrix/matalloc.c create mode 100644 libsrc/matrix/matrix_dispatch.c create mode 100644 libsrc/morphology/Makefile.am create mode 100644 libsrc/morphology/im_cntlines.c create mode 100644 libsrc/morphology/im_dilate.c create mode 100644 libsrc/morphology/im_erode.c create mode 100644 libsrc/morphology/im_profile.c create mode 100644 libsrc/morphology/man3/Makefile.am create mode 100644 libsrc/morphology/man3/im_cntlines.3 create mode 100644 libsrc/morphology/man3/im_dilate.3 create mode 100644 libsrc/morphology/man3/im_dilate_raw.3 create mode 100644 libsrc/morphology/man3/im_erode.3 create mode 100644 libsrc/morphology/man3/im_erode_raw.3 create mode 100644 libsrc/morphology/man3/im_profile.3 create mode 100644 libsrc/morphology/morph_dispatch.c create mode 100644 libsrc/mosaicing/Makefile.am create mode 100644 libsrc/mosaicing/global_balance.c create mode 100644 libsrc/mosaicing/global_balance.h create mode 100644 libsrc/mosaicing/im_affine.c create mode 100644 libsrc/mosaicing/im_avgdxdy.c create mode 100644 libsrc/mosaicing/im_chkpair.c create mode 100644 libsrc/mosaicing/im_clinear.c create mode 100644 libsrc/mosaicing/im_improve.c create mode 100644 libsrc/mosaicing/im_initialize.c create mode 100644 libsrc/mosaicing/im_lrcalcon.c create mode 100644 libsrc/mosaicing/im_lrmerge.c create mode 100644 libsrc/mosaicing/im_lrmosaic.c create mode 100644 libsrc/mosaicing/im_remosaic.c create mode 100644 libsrc/mosaicing/im_tbcalcon.c create mode 100644 libsrc/mosaicing/im_tbmerge.c create mode 100644 libsrc/mosaicing/im_tbmosaic.c create mode 100644 libsrc/mosaicing/man3/Makefile.am create mode 100644 libsrc/mosaicing/man3/im_affine.3 create mode 100644 libsrc/mosaicing/man3/im_correl.3 create mode 100644 libsrc/mosaicing/man3/im_global_balance.3 create mode 100644 libsrc/mosaicing/man3/im_global_balance_float.3 create mode 100644 libsrc/mosaicing/man3/im_lrmerge.3 create mode 100644 libsrc/mosaicing/man3/im_lrmosaic.3 create mode 100644 libsrc/mosaicing/man3/im_match_linear.3 create mode 100644 libsrc/mosaicing/man3/im_match_linear_search.3 create mode 100644 libsrc/mosaicing/man3/im_remosaic.3 create mode 100644 libsrc/mosaicing/man3/im_similarity.3 create mode 100644 libsrc/mosaicing/man3/im_similarity_area.3 create mode 100644 libsrc/mosaicing/man3/im_tbmerge.3 create mode 100644 libsrc/mosaicing/man3/im_tbmosaic.3 create mode 100644 libsrc/mosaicing/match.c create mode 100644 libsrc/mosaicing/merge.h create mode 100644 libsrc/mosaicing/mosaic.h create mode 100644 libsrc/mosaicing/mosaic1.c create mode 100644 libsrc/mosaicing/mosaicing_dispatch.c create mode 100644 libsrc/mosaicing/similarity.c create mode 100644 libsrc/other/Makefile.am create mode 100644 libsrc/other/cooc_funcs.c create mode 100644 libsrc/other/glds_funcs.c create mode 100644 libsrc/other/im_benchmark.c create mode 100644 libsrc/other/im_dif_std.c create mode 100644 libsrc/other/im_eye.c create mode 100644 libsrc/other/im_grey.c create mode 100644 libsrc/other/im_make_xy.c create mode 100644 libsrc/other/im_meanstd.c create mode 100644 libsrc/other/im_simcontr.c create mode 100644 libsrc/other/im_sines.c create mode 100644 libsrc/other/im_spatres.c create mode 100644 libsrc/other/im_zone.c create mode 100644 libsrc/other/man3/Makefile.am create mode 100644 libsrc/other/man3/im_benchmark.3 create mode 100644 libsrc/other/man3/im_cooc_asm.3 create mode 100644 libsrc/other/man3/im_cooc_contrast.3 create mode 100644 libsrc/other/man3/im_cooc_correlation.3 create mode 100644 libsrc/other/man3/im_cooc_entropy.3 create mode 100644 libsrc/other/man3/im_cooc_matrix.3 create mode 100644 libsrc/other/man3/im_dif_std.3 create mode 100644 libsrc/other/man3/im_eye.3 create mode 100644 libsrc/other/man3/im_feye.3 create mode 100644 libsrc/other/man3/im_fgrey.3 create mode 100644 libsrc/other/man3/im_fzone.3 create mode 100644 libsrc/other/man3/im_glds_asm.3 create mode 100644 libsrc/other/man3/im_glds_contrast.3 create mode 100644 libsrc/other/man3/im_glds_entropy.3 create mode 100644 libsrc/other/man3/im_glds_matrix.3 create mode 100644 libsrc/other/man3/im_glds_mean.3 create mode 100644 libsrc/other/man3/im_grey.3 create mode 100644 libsrc/other/man3/im_make_xy.3 create mode 100644 libsrc/other/man3/im_mean_std_double_buffer.3 create mode 100644 libsrc/other/man3/im_mean_std_int_buffer.3 create mode 100644 libsrc/other/man3/im_quantim.3 create mode 100644 libsrc/other/man3/im_quantlut.3 create mode 100644 libsrc/other/man3/im_simcontr.3 create mode 100644 libsrc/other/man3/im_sines.3 create mode 100644 libsrc/other/man3/im_spatres.3 create mode 100644 libsrc/other/man3/im_zone.3 create mode 100644 libsrc/other/other_dispatch.c create mode 100644 libsrc/relational/Makefile.am create mode 100644 libsrc/relational/im_blend.c create mode 100644 libsrc/relational/im_ifthenelse.c create mode 100644 libsrc/relational/man3/Makefile.am create mode 100644 libsrc/relational/man3/im_blend.3 create mode 100644 libsrc/relational/man3/im_equal.3 create mode 100644 libsrc/relational/man3/im_equal_vec.3 create mode 100644 libsrc/relational/man3/im_equalconst.3 create mode 100644 libsrc/relational/man3/im_ifthenelse.3 create mode 100644 libsrc/relational/man3/im_less.3 create mode 100644 libsrc/relational/man3/im_less_vec.3 create mode 100644 libsrc/relational/man3/im_lessconst.3 create mode 100644 libsrc/relational/man3/im_lesseq.3 create mode 100644 libsrc/relational/man3/im_lesseq_vec.3 create mode 100644 libsrc/relational/man3/im_lesseqconst.3 create mode 100644 libsrc/relational/man3/im_more.3 create mode 100644 libsrc/relational/man3/im_more_vec.3 create mode 100644 libsrc/relational/man3/im_moreconst.3 create mode 100644 libsrc/relational/man3/im_moreeq.3 create mode 100644 libsrc/relational/man3/im_moreeq_vec.3 create mode 100644 libsrc/relational/man3/im_moreeqconst.3 create mode 100644 libsrc/relational/man3/im_notequal.3 create mode 100644 libsrc/relational/man3/im_notequal_vec.3 create mode 100644 libsrc/relational/man3/im_notequalconst.3 create mode 100644 libsrc/relational/relational.c create mode 100644 libsrc/relational/relational_dispatch.c create mode 100644 libsrc/video/Makefile.am create mode 100644 libsrc/video/im_video_test.c create mode 100644 libsrc/video/im_video_v4l1.c create mode 100644 libsrc/video/im_video_v4l1.h create mode 100644 libsrc/video/man3/Makefile.am create mode 100644 libsrc/video/man3/im_video_v4l1.3 create mode 100644 libsrc/video/video_dispatch.c create mode 100644 libsrc/vips.def create mode 100644 libsrcCC/Makefile.am create mode 100644 libsrcCC/VDisplay.cc create mode 100644 libsrcCC/VError.cc create mode 100644 libsrcCC/VImage.cc create mode 100644 libsrcCC/VMask.cc create mode 100644 libsrcCC/vipsc++.cc create mode 100644 po/ChangeLog create mode 100644 po/POTFILES.in create mode 100644 po/POTFILES.skip create mode 100644 po/README create mode 100644 po/en_GB.gmo create mode 100644 po/en_GB.po create mode 100644 po/malkovich.gmo create mode 100644 po/malkovich.po create mode 100644 po/messages create mode 100644 po/missing create mode 100644 python/Makefile.am create mode 100755 python/test/testvipsCC.py create mode 100644 python/vipsCC/Makefile.am create mode 100644 python/vipsCC/VDisplay.i create mode 100644 python/vipsCC/VError.i create mode 100644 python/vipsCC/VImage.i create mode 100644 python/vipsCC/VMask.i create mode 100644 python/vipsCC/__init__.py create mode 100644 src/Makefile.am create mode 100644 src/iofuncs/Makefile.am create mode 100644 src/iofuncs/binfile.c create mode 100644 src/iofuncs/debugim.c create mode 100644 src/iofuncs/edvips.c create mode 100644 src/iofuncs/header.c create mode 100644 src/iofuncs/man1/Makefile.am create mode 100644 src/iofuncs/man1/binfile.1 create mode 100644 src/iofuncs/man1/debugim.1 create mode 100644 src/iofuncs/man1/edvips.1 create mode 100644 src/iofuncs/man1/header.1 create mode 100644 src/iofuncs/man1/printlines.1 create mode 100644 src/iofuncs/man1/vips.1 create mode 100644 src/iofuncs/printlines.c create mode 100644 src/iofuncs/vips.c create mode 100644 src/mosaicing/Makefile.am create mode 100644 src/mosaicing/find_mosaic.c create mode 100644 src/mosaicing/mergeup.c create mode 100644 src/other/Makefile.am create mode 100644 src/other/cooc.c create mode 100644 src/other/cooc_features.c create mode 100644 src/other/glds.c create mode 100644 src/other/glds_features.c create mode 100644 src/other/man1/Makefile.am create mode 100644 src/other/man1/cooc.1 create mode 100644 src/other/man1/cooc_features.1 create mode 100644 src/other/man1/glds.1 create mode 100644 src/other/man1/glds_features.1 create mode 100644 src/other/man1/simcontr.1 create mode 100644 src/other/man1/sines.1 create mode 100644 src/other/man1/squares.1 create mode 100644 src/other/simcontr.c create mode 100644 src/other/sines.c create mode 100644 src/other/spatres.c create mode 100644 src/other/squares.c create mode 100644 src/scripts/Makefile.am create mode 100644 src/scripts/batch_crop.in create mode 100644 src/scripts/batch_image_convert.in create mode 100644 src/scripts/batch_rubber_sheet.in create mode 100644 src/scripts/light_correct.in create mode 100644 src/scripts/man1/Makefile.am create mode 100644 src/scripts/man1/batch_crop.1 create mode 100644 src/scripts/man1/batch_image_convert.1 create mode 100644 src/scripts/man1/batch_rubber_sheet.1 create mode 100644 src/scripts/man1/light_correct.1 create mode 100755 src/scripts/post_install create mode 100644 src/scripts/shrink_width.in create mode 100755 src/scripts/vips-7.12 create mode 100644 vips-7.12.pc.in create mode 100644 vips-7.12.spec.in create mode 100644 vipsCC-7.12.pc.in create mode 100644 win32/README create mode 100644 win32/msvc/config.h create mode 100644 win32/msvc/vips.dsp create mode 100644 win32/msvc/vips.dsw create mode 100644 win32/msvc/vips.opt create mode 100644 win32/proj/README create mode 100644 win32/proj/config.h.win32 create mode 100644 win32/proj/libsrc/acquire/makefile.msc create mode 100644 win32/proj/libsrc/arithmetic/makefile.msc create mode 100644 win32/proj/libsrc/boolean/makefile.msc create mode 100644 win32/proj/libsrc/colour/makefile.msc create mode 100644 win32/proj/libsrc/conversion/makefile.msc create mode 100644 win32/proj/libsrc/convolution/makefile.msc create mode 100644 win32/proj/libsrc/freq_filt/makefile.msc create mode 100644 win32/proj/libsrc/histrograms_lut/makefile.msc create mode 100644 win32/proj/libsrc/inplace/makefile.msc create mode 100644 win32/proj/libsrc/iofuncs/makefile.msc create mode 100644 win32/proj/libsrc/makefile.msc create mode 100644 win32/proj/libsrc/matrix/makefile.msc create mode 100644 win32/proj/libsrc/morphology/makefile.msc create mode 100644 win32/proj/libsrc/mosaicing/makefile.msc create mode 100644 win32/proj/libsrc/other/makefile.msc create mode 100644 win32/proj/libsrc/relational/makefile.msc create mode 100644 win32/proj/libsrc/video/makefile.msc create mode 100644 win32/proj/src/iofuncs/makefile.msc create mode 100644 win32/tmake/README create mode 100644 win32/tmake/config.h create mode 100644 win32/tmake/libsrc/Makefile create mode 100644 win32/tmake/libsrc/acquire/Makefile create mode 100644 win32/tmake/libsrc/acquire/acquire.pro create mode 100644 win32/tmake/libsrc/arithmetic/Makefile create mode 100644 win32/tmake/libsrc/arithmetic/arithmetic.pro create mode 100644 win32/tmake/libsrc/boolean/Makefile create mode 100644 win32/tmake/libsrc/boolean/boolean.pro create mode 100644 win32/tmake/libsrc/colour/Makefile create mode 100644 win32/tmake/libsrc/colour/colour.pro create mode 100644 win32/tmake/libsrc/conversion/Makefile create mode 100644 win32/tmake/libsrc/conversion/conversion.pro create mode 100644 win32/tmake/libsrc/convolution/Makefile create mode 100644 win32/tmake/libsrc/convolution/convolution.pro create mode 100644 win32/tmake/libsrc/freq_filt/Makefile create mode 100644 win32/tmake/libsrc/freq_filt/freq_filt.pro create mode 100644 win32/tmake/libsrc/histograms_lut/Makefile create mode 100644 win32/tmake/libsrc/histograms_lut/histograms_lut.pro create mode 100644 win32/tmake/libsrc/inplace/Makefile create mode 100644 win32/tmake/libsrc/inplace/inplace.pro create mode 100644 win32/tmake/libsrc/iofuncs/Makefile create mode 100644 win32/tmake/libsrc/iofuncs/iofuncs.pro create mode 100644 win32/tmake/libsrc/makefile_todo create mode 100644 win32/tmake/libsrc/matrix/Makefile create mode 100644 win32/tmake/libsrc/matrix/matrix.pro create mode 100644 win32/tmake/libsrc/morphology/Makefile create mode 100644 win32/tmake/libsrc/morphology/morphology.pro create mode 100644 win32/tmake/libsrc/mosaicing/Makefile create mode 100644 win32/tmake/libsrc/mosaicing/mosaicing.pro create mode 100644 win32/tmake/libsrc/other/Makefile create mode 100644 win32/tmake/libsrc/other/other.pro create mode 100644 win32/tmake/libsrc/relational/Makefile create mode 100644 win32/tmake/libsrc/relational/relational.pro create mode 100644 win32/tmake/libsrc/todo.pro create mode 100644 win32/tmake/libsrc/video/Makefile create mode 100644 win32/tmake/libsrc/video/video.pro create mode 100644 win32/tmake/libsrc/vips.pro diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..7b9b342f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,19 @@ +Authors of VIPS + +See also the files THANKS and ChangeLog + +Nicos Dessipris and Kirk Martinez started VIPS in 1990. + +John Cupitt started ip in late 1990, and took over maintenance of the VIPS +library in 1995. + +Ruven Pillay, Steve Perry, Lars Raffelt, David Saunders, Jean-Philippe +Laurant, Ahmed Abood, Helene Chahine, Joe Padfield, Andrey Kiselev, Lev +Serebryakov, Simon Goodall, Konrad Lang, Markus Wollgarten, Jesper Friis +and others contributed patches for the library and ip. + +Hans Breuer contributed many win32 compatibility fixes and a win32 build +system. Dennis Lubert cleaned up the C++ API. + +Jose Manuel Menendez Garcia, Javier Alejandre Arenas, and Juan Torres Arjona +contributed the tmake VIPS.DLL build system and the MSVC project files. diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..b1e3f5a2 --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e382f49e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1511 @@ +1/8/07 started 7.12.5 +- im_embed() is more general ... x and y can be negative +- predicate.c is smaller and cleaner +- libsrcCC link improvement from Pablo +- support 32/64-bit ImageMagick as well (thanks Marcel) +- better im_magick2vips() for Q8 ImageMagick + +27/7/06 started 7.12.4 +- proto.h had vars called small, breaking on win32 +- more python fixing, we now have working matricies too + +17/7/06 started 7.12.3 +- fix to VImage.i for gcc 4.2 (thanks Damir) +- eek, off by 1 for more than 1 band hists +- needed a slightly larger worst-case buffer in im__b64_encode() +- tiny cleanup for make_hI() prevents cond jump on ui in valgrind +- --disable-threads was broken again +- remove .svn dirs from dist +- now passes distcheck again + +17/7/06 started 7.12.2 +- added im_bandmean() +- added support for TIFFTAG_PREDICTOR (Andrey Kiselev) +- fix TIFFOpen() mode snafu (Andrey Kiselev) + +11/5/06 started 7.12.1 +- memory.c abort()s with DEBUG +- oops, im_bits_of_fmt() manpage was not being installed +- im_histcum() can do all image types +- updated NEWS +- added im_csv2vips_header() +- command-line csv read was broken (thanks Tom) +- removed length limit on argument vectors (Tom) +- added IM_PREFIX, configure-time install prefix +- oop, turned off memory.c DEBUG +- fix some bogus gcc 4.1 warnings with im_open_local_array() +- better vips usage message +- oops, IM_ANY missing from im_demand_hint() manpage (thanks Shahid) +- just warn if plugins fail to load in im_init_world() +- expose Vargv and make refblock public rather than private so that + subclasses of VImage can add vips-style member operations (thanks Pablo) +- oops, im_initdesc() needed to have bbits set correctly (thanks Shahid) +- make VError derive from std::exception more officially +- woo, got exceptions working in SWIG +- soname version bumped to 12.x.x +- oops, added vector ops to Python + +26/3/07 started 7.11.21 +- ooo, added %include "std_except.i" & friends to VError.i, VImage.i +- im_init_world() is more lenient about recursive invocation +- im_gbandjoin() falls back to im_copy() for 1 input image +- race condition fixed in im_render.c (thanks Simon) +- bump for 7.12!!! + +26/1/07 started 7.11.20 +- another fix to im_region_image() (thanks Mikkel) +- tiny speed up to im_rect_includesrect() +- avoid recursive invocation in im_init_world() (thanks Christian) +- fix to extract_prefix (thanks Christian) +- buffer cache is now per thread +- combine buffer unref and ref in a single operation to reduce malloc/free + cycles +- new internal API for passing regions between threads means we can remove + buffer locks +- more buffer/region sanity checks, plus a memory barrier +- lock around error buffer changes +- im_vips2mask() was wrong for nx1 m-band images +- liboil back to "test" +- add buffer_cache_list to avoid GHashTable _insert()s +- oop, --vips-concurrency was broken +- renamed (in dispatch layer) im_and/or/eor_const -> im_and/or/eorimageconst + for consistency +- C++ API wraps IMAGEVEC, DOUBLEVEC, INTVEC arguments +- oop, IMAGE % vector was broken + +21/12/06 started 7.11.19 +- added im_isscalar() (Tom) +- added IM_REGION_ADDR_TOPLEFT() (Tom) +- reduce size of im_rightshift_size.c to help compile (Tom) +- added im_linreg() (Tom) +- various C++ API polishes, plus a bugfix (Dennis Lubert) +- vips.spec split to devel and python too (Dennis Lubert) +- be more explicit about sizeof(magic) +- init magic to native order by default (thanks Dennis) +- Hist becomes im_history_get() +- new history mechanism is faster, uses much less memory, and removes + duplicate lines +- added im_get_option_group() +- added official im_concurrency_set()/_get()() +- don't read bbits from vips files ... set ourselves from bandfmt +- oops add RGB16 and GREY16 to C++ header +- --list packages option to vips.c +- updated docs to 7.12 +- oops, im_region_image() snafu was causing a lot of recomputation +- make im_mpercent() suck a little less +- EXIF save was a bit bOrked ... cause of mac crashes? +- im_histgr(), im_heq(), im_hist() all number bands from zero now +- fix stride in liboil calls +- set RGB16 on 16-bit RGB ICC export + +29/11/06 started 7.11.18 +- added im_buffer_t so regions can share calculated pixels: 2-3x speedup on + the benchmark +- im_region_local() -> im_region_buffer() +- im_sharpen() order change to help sharing +- im_invalidate() clears buffer caches +- add sentinel attributes +- add some missing im_demand_hint()s +- paint ops invalidate the output image +- fix nothread eval +- raise threads limit to 1024 (thanks Christian) +- manual redone +- vipsCC python init() hooks +- add liboil dependency +- use liboil for im_abs(), im_add(), im_divide(), im_floor(), im_multiply(), + im_subtract(), im_lintra(), im_avg(),im_deviate() +- quiet libtoolize test (thanks Tom) +- im_benchmarkn now regrows image each time +- strip meta from sample2.v ... saves a lot of mem (esp. Hist) + +24/11/06 started 7.11.17 +- better benchmark script makes graphing easier +- double-buffer image file writes +- reuse write threads +- clean up threadgroup / iterate / generate +- added im_benchmarkn to make it easier to make a CPU-bound op on large + machines +- im_cache() failed for cpus > 1 + +1/11/06 started 7.11.16 +- moved im__convert_saveable() into im_copy() (thanks Christian) +- missing gobject dependency (thanks Christian) +- --enable-threads was broken (thanks Christian) +- eval without theads was broken (thanks Christian) +- LIBADD libvips.la to libvipsCC.la (thanks Simon) +- benchmark.sh is now plain sh, not bash +- set ORIENTATION_TOPLEFT in im_vips2tiff (thanks Josef) +- oops, im_vips2csv() output separator was broken +- added im_benchmark2 +- move XYZ2Lab LUT build outside the eval thread + +30/10/06 started 7.11.15 +- print leaked windows +- oops, race condition in im_window_unref() +- integrated im_region_window() into im_region_image(), tiny speed up + +6/10/06 started 7.11.14 +- ifthenelse and affine dhints revised +- buildlut no longer outputs x cods +- configure asks for glib >= 2.6 +- configure uses AC_TOOL_CHECK to find tool names to help cross-compiling. +- better configure test for libexif +- add C++ include ... include in a namespace +- added im_benchmark / SMP benchmark script + +8/9/06 started 7.11.11 +- add im_norm_dmask() +- removed old code for gradient and lindetect +- internal decls split from proto.h to help SWIG +- test for python and SWIG during configure +- added python dir for the binding +- python binding done! +- oops, --without-python was broken (thanks Tom) +- added python/test + +23/6/06 started 7.11.10 +- still more im_affine() rounding/clipping tweaks +- ignore "--" arguments to vips.c +- im_init_world() also sets g_*_prgname() and loads plugins +- add manpage for im_init_world() (oops) +- error_exit() prints prgname +- various cygwin fixes +- fix cache thread assert failure (thanks Joe) +- "header" now uses GOption, slightly different args, will loop over args +- fixed assert() overenthusiasm in im_prepare() +- im_csv2vips() now has separate whitespace / separator tables + +23/6/06 started 7.11.9 +- back on sourceforge CVS again +- require openexr 1.2.2 or greater +- range check xy on im_insert*() for sanity +- VMask::invertlut decl removed (thanks Jean) +- added \"all\" option to vips.c + +17/5/06 started 7.11.8 +- debrokened openexr read +- added im_tile_cache() +- added tiled read to im_exr2vips() +- im_tiff2vips() now uses im_tile_cache() rather than its own cache ... + faster in some cases, less RAM use in some cases, saves 200+ lines +- removed 'broken' read option from im_tiff2vips() +- read/write doubles with g_ascii_strtod() and friends where appropriate +- add a "thread" member to region to help sanity check region ownership +- saner threadgroup fixes a race problem on gcc 4.0.3 / amd64 +- added im_vips2csv() +- im_open() now does CSV read/write too +- oops, broke vips main prog for function name in argv1 case + +22/4/06 started 7.11.7 +- split vips_png.c to im_vips2png.c and im_png2vips.c +- added OpenEXR dependency +- added im_exr2vips(), im_exr2vips_header() +- added im_isexr(), im_open() knows about OpenEXR +- added im_contrast_surface(), im_contrast_surface_raw() (Tom) +- added im_msb(), im_msb_band() (Tom) +- im_scale() sets Type on output +- added RGB16, GREY16 types +- im_*2vips() set these types if appropriate +- configure fixes for mac +- vips main prog uses GOption +- im_icc_* locks around calls to cmsDoTransform() to avoid corruption on SMP + machines + +10/3/06 started 7.11.6 +- typo in manpage for im_rect_dup() (thanks Tom) +- don't abort image load if XML read fails +- added im_video_test() ... test video source +- oops, lcms .pc finder was not working +- clipping problem in im_affine() fixed (thanks Clare) +- test for attr support in libmagick +- im_text() returns an error for empty string +- im_falsecolour() scale reversed +- im_remosaic() could crash on bad mosaics +- configure changes to fix --without-magick, lcms and fftw (but sadly we now + require .pc files for these libs) +- im_vips2jpeg() automatically converts to 1 or 3 band sRGB uchar for write +- also im_vips2png() +- added im_project() + +20/2/06 started 7.11.5 +- added im_csv2vips() +- commas in filename options can be escaped with a '\' +- raise tile buffer limit (thanks Ruven) +- im_spcor() and im_fastcor() have prettier borders +- im_fastcor() returns sum of squares of differences, not sum of abs of + differences + +18/11/05 started 7.11.4 +- small win32 fixes, thanks Juan +- added im_flood_blob_copy() ... a temporary hack +- much faster im_histplot() +- read RGBA palette-ized PNG images more robustly (thanks Tom) +- turn on -ms-bitfields automatically for mingw + +26/9/05 started 7.11.3 +- better error recovery for im_binfile() file too large +- all raw files now use mmap windows, so (eg.) ppm and analyze reads can go + >2GB +- remove DISABLE_COMPAT ... you now have to define IM_ENABLE_DEPRECATED to get + broken #defines +- fix to build without exif, thanks Chas +- use native win32 API for seek()/truncate() to work with large files +- use attribute to check printf-style args with gcc +- fix gcc4 warnings +- removed ebuild, since it's in gentoo now +- im_magick2vips() sets meta from attributes (good for dicom) +- im_magick2vips() writes many-frame images as tall thin VIPS images +- im_histcum() was broken for vertical histograms +- im_histnorm() is neater +- simpler and faster inner loop for im_conv() and im_convf() avoids gcc4 bug +- appendc() was reading past the end of the buffer on MSB machines + +13/6/05 started 7.11.2 +- im_copy_set() was messed up in 7.11.1 +- put into CVS, phew +- fixed a rounding bug in im_affine() ... should no longer get black edges on + image resize +- if TIFF open fails in im_open(), try going through libmagick +- merge requires all bands == 0 for transparency (used to just check 1st + band) +- 16 bit LAB TIFF read/write was wrong +- new GType for refstring makes it visible from im_header_map() +- jpeg loader attaches exif data (and human-readable meta fields) +- jpeg saver writes any exif data +- meta not wiped by im_*incheck() in a "w" image +- meta keeps traverse order +- now require glib >= 2.4 +- require libxml-2.0 for meta save and new history mechanism +- no more .desc files, history saved in XML after pixel data +- i/s/d meta fields saved there too +- added base64 encode/decode +- added blob header write +- added a save string type: types which define transforms to and from the + save format get serialized +- GValue meta API now exposed, since we can serialise anything +- jpeg loader loads ICC profiles +- jpeg saver saves ICC profiles from the VIPS header +- src/header.c knows about meta system +- added im_analyze2vips(), im_grid(), im_raw2vips() +- extract/grid/replicate/zoom were not setting TRANSFORM flag +- better falsecolour LUT +- less stupid + more portable read/write of VIPS header +- better im_updatehist() +- jpeg load sets vips xres/yres from exif, if possible +- jpeg save sets exif xres/yres from vips, if possible +- icc_export and icc_transform attach profiles and intents to output images +- added im_icc_import_embedded() to import with an embedded profile +- split vips_jpeg.c into two, it was getting too big +- added im_cp_descv(), im_cp_desc_array(), funcs use them +- removed im_append_Hist() from API +- fixed meta copy bug +- better history copy, removed nonsense about 1st line of Hist being special +- tiff read/write now reads/writes ICC profile from meta +- edvips rewritten to remove stupidness, and can now set xml +- header can now print xml extension block +- IM_ prefix for colour temp names + +1/6/05 started 7.11 +- added im_copy_morph() +- im_region_region() allows Bands and BandFmt to differ, provided + sizeof( pel ) is the same ... makes im_copy_morph() work +- added im_meta*() functions (MW) +- im_header_*() rewritten for meta +- added im_header_exists(), im_header_map() +- use pkg-config to find libpng and ImageMagick +- added im_lineset() +- added im_extract_areabands() (JF) +- added im_copy_from() (JF) + +15/4/05 started 7.10.12 +- im_ifthenelse just evals left/right for region all zero/all one +- also im_blend +- swap g_setenv() back to plain setenv() so we work with glib 2.2 + +9/4/05 JC started 7.10.11 +- docs no longer have broken links +- fixed memleak in im_text() + +8/4/05 +- one bit tiff read was sometimes reading a byte too far in each scanline + +14/1/05 started 7.10.9 +- im_filename_split() will now usually work for filenames containing ':' + characters +- added im_render_fade() for fancier nip2 image repaint +- added "ccittfax4" as a TIFF compression option +- fix all 64-bit compiler warnings +- "," allowed as column separator in mask read +- better at spotting singular matricies +- small im_render() tidies +- glib dependancy reduced to just 2.0, but untested ... helps people building + on older systems who aren't interested in nip2 +- removing leading spaces from IMAGEVEC arguments +- load non-interlaced PNGs more efficiently +- 1 point mosaic functions work on more image types +- better memory allocation debugging info +- local memory on regions can shrink as well as grow +- shut down threadgroups on render if no dirty tiles +- limit number of simultaneous renders +- higher mmap window threshold +- allow max == -1 for unlimited render cache +- 'priority' marks non-suspendable renders +- im_embed() mode == 4 paints white pels +- im_tiff2vips() was broken with --disable-threads +- oops, im_errormsg() compat macros were GCC only +- larger default tile size and strip height +- tiff write sets PHOTOMETRIC_CIELAB for vips TYPE_LAB images ... so we can + write float LAB as well as float RGB (thanks Ruven) +- also 16 bit LAB TIFF write +- im_render() rewritten + +20/11/04 started 7.10.8 +- im_sharpen() is ~15% faster +- more quoting for MAGICK finder +- im_XYZ2Lab() uses a LUT rather than cbrt(), 5x faster +- --disable-threads removes gthread dependency completely (thanks Simon) +- intercept TIFF warnings as well as errors ... stops occasional libMagick + exceptions +- add im_init_world() to im_init() as well to help backwards compat (thanks + Simon) +- im_icc_present() function description was broken, thanks Jay +- oops, libtool library versioning was wrong, thanks Jay +- can now make TIFF pyramids of any non-complex image type (was uchar and LAB + only), thanks Ruven +- 1st order mosaic code now works for LABQ too +- build system changes to make "make distcheck" work +- RPM .spec files fixed up and updated by configure (thanks Simon) +- tiny cleanups for vdump +- use g_setenv()/g_getenv() +- tiny improvements to IM_FREE*() +- tiny VImage debug print fixes (thanks Jay) +- swap off_t for gint64 to fix LARGEFILE support on win32 +- computation feedback now uses gint64 for number of pels, so we give feedback + correctly on images with >2**31 pels +- other small fixes for >2**31 pels in an image + +10/11/04 started 7.10.7 +- im_histnD() was not checking BandFmt (thanks Kirk) +- improvements to threading system speed up non-vips output in some cases +- use cbrt(x) where we can ... 10x faster than pow(x,1.0/3) on win32 +- typeo in im_text() when built without PANGOFT2 (thanks Stefan) + +1/11/04 styarted 7.10.6 +- tiny doc fixes +- scripts now only depend on 'vips' program +- im_open( "r" ) is now lazier ... for non-VIPS formats, we delay read until + the first ->generate() +- so im_open_header() now deprecated since im_open("r") is identical +- now looks for fftw3 as well as fftw2 ... slightly faster ffts + +19/10/04 started 7.10.5 +- fix to light_correct (thanks Jay) +- edvips knows about xoffset/yoffset +- better vips enum<->char conversions + +4/10/04 started 7.10.4 +- man page fixes (thanks Jay) +- removed last csh scripts (thanks Jay) +- scripts default VIPSHOME to $prefix (thanks Jay) +- doc build system tidies +- im_rank() edge padding was wrong +- im_vips2tiff() can now embed ICC profiles + +22/9/04 started 7.10.3 +- mildly better im_vips2tiff() +- *, -, +, /, % between two images now work for mixed number of bands +- im_free() was missing a man page +- revised documentation + +1/9/04 started 7.10.2 +- C++ .pc files were still set for 7.9, grr +- im_insertplace() didn't check compatibility of images (thanks Matt) + +27/7/04 started 7.10.1 +- set default stack size explicitly to help platforms with a very low default +- 16 bit RGB tiff read was broken (bug introduced in 7.9.5, thanks Haida) +- !pangoft2 was broken, thanks Kenneth +- win32 build fixes + +12/7/04 renamed as 7.10.0 +- added NOCACHE function flag ... stops nip memoising video & paint ops +- added im_extract_bands() ... takes out many bands from an image +- im_vips2tiff() scanline write speed up for area pipelines + +10/6/04 started 7.9.6 +- tiny polishing of im_ppm2vips() +- im_blend() can now work on labq +- boolean ops all work on float/complex images (by casting to int) +- im_maplut() was broken for 1 band image + many band lut + >8 bit output +- im_lintra_vec() now handles 1 band image and many band vector to make many + band image +- oops, im_lintra_vec() was missing a man page +- im_measure() can work on labq +- im_lhisteq() uses new embed mode, _raw() version is one pixel smaller, sets + Xoffset/Yoffset for new origin scheme +- generalised im_tone_build() to any image type to make im_tone_build_range() + +20/5/04 started 7.9.5 +- tiff output res can be a single number too +- added im_text() to make a bitmap from a string with pango +- im_tiff2vips() does 16 bit RGBA +- im_binfile() was broken since 7.9.2 due to im_mapfile() change +- im_ppm2vips() now works for 16 bit images +- added im_copy_swap() ... copies, reversing byte order +- im_resize_linear() was broken for some images, thanks Javi + +8/3/04 started 7.9.4 +- oops, config.h include missing in a few places +- im_vips2tiff() can now write 1 bit images +- im__find_lr/tboverlap() now exported to nip +- better edge tile handling for tiff read/write (thanks Ruven) +- added extend-pixels-from-edge mode to im_embed() +- im_conv*(), im_rank(), im_stdif(), im_dilate(), im_erode() all use it to + expand their input, so their output now has guess borders, not black borders +- im_fastcor() now does an im_embed( 1 ) on the output ... the zero borders + were very annoying before, since you would usually be searching for the + minimum point +- no change to im_spcor(), since you will usually be searching for the maximum +- better im_render() cache behaviour under heavy loads +- im_affine() revised + * clip, resample and transform is now pixel-perfect for all + inputs (I hope) + * uses the new embed to make sure there are no black borders + from edge interpolation + * about 20 - 30% faster +- policy change: Xoffset and Yoffset are now set by all operations to record + the position of the input origin in the output +- im_replicate() is much faster for some cases +- added tile and mirror flags to im_embed() +- added im_cache() convenience function +- better ETA for image calculation +- im_tiff2vips() now has a "broken" option so it can read tiled tiffs made + with earlier versions of vips +- on convert float to int format, now does floor() not rint() ... more + 'mathematical' +- added im_rint() +- im_sharpen() now uses a gaussian mask +- im_convsep() more resistant to int overflow problems +- added im_make_xy(), avoids rounding problems with the old float-based thing +- im_profile() now makes vertical images for a vertical profile +- added im_vips2tiff() option to set the resolution in inches not cm (thanks + Andrey) +- im_binfile() is now exported + +6/2/04 started 7.9.3 +- added an im_init_world() to im_open(), to help old progs +- renamed VSemaphore as im_semaphore_t +- started using libtool library versioning +- now uses g_module_*() in place of dlopen() +- now uses pkg-config instead of vips-config (thanks Simon) +- fixes to vips.h for _ADDR() with DEBUG on (thanks Konrad) + +10/12/03 started 7.9.2 +- patches for freebsd, thanks Lev Serebryakov +- vips2dj knows about my colour laser printer +- added i18n support, glib/gmodule/gthread dependency +- im_error*() API revised to be more i18n friendly +- List type removed, now uses g_slist +- VBuf added, some more utility funcs pushed down from nip +- im_thread stuff removed, now uses g_thread +- im_lock stuff removed, now uses g_mutex +- im_semaphore_t renamed to VSemaphore, not sure this is a good idea +- build with --disable-threads to turn off threaded render +- #include now pulls in most of the public API, you shouldn't + need other vips includes very often +- im_close() is better at cleaning up if there's an error +- inverse FFTs could fail for wider-than-high images with fftw +- better im_icc_transform error messages +- bug fix in im_render with large caches +- im_binfile() now has an offset parameter +- im_mapfile()/im_unmapfile() now work on IMAGE and record the length of the + file they mapped ... this lets VIPS successfully unmap a file if it changes + size while it's open + +20/10/03 started 7.9.1 +- threadgroups now have their own kill flag +- im_plotmask() now does anti-aliasing +- im_iterate() fix for operations on mmap window images (thanks Clare) +- im_writeline() stops on kill +- fix for im_fwfft() segv for wider-than-high real images (thanks Andrey) +- fix for im_fwfft() to work for non-square real images (thanks Andrey) +- can now read and write 32-bit IEEE float TIFF (Andrey Kiselev) +- clean-ups for colour.c (Andrey Kiselev) +- no longer lets you make an image with width|height|bands == 0 (thanks Joe) +- im_vips2tiff(), im_vips2*jpeg*(), im_vips2png(), im_vips2ppm() could + sometimes fail for mmap window input images (thanks David) +- added IM_RECT_HCENTRE(), IM_RECT_VCENTRE() macros + +20/8/03 JC +- started 7.9.0 +- added im_rank_image() ... im_maxvalue() a special case of this +- im_subtract() goes up to int earlier for better value preserving, thanks + Haida +- im_rank() much faster for large windows, correct result on all platforms + (dratted memcpy() was causing problems before) +- fixed problem with libMagick config if installed somewhere strange +- fixed problem with include order in library compile +- added --without-magick configure option +- added im_render(), threaded background image paint +- added im_replicate(), replicate an image horizontally and vertically + +31/5/03 JC +- started 7.8.11 +- fixed a problem with relational operators and some combinations of input + types (bug introduced in 7.8.9), thanks Haida +- vips-7.8 script overrides VIPSHOME environment variable +- better im_guess_prefix +- stupid light_correct script no longer uses /pics/tmp +- added batch_crop script + +22/5/03 +- started 7.8.10 +- the JPEG writer can embed ICC profiles in output images ... although I've + yet to see it make any difference :-( test this carefully at some point +- fixed a possible coredumper in jpeg write +- jpeg read now spots truncated files +- im_invertlut() now makes an image, not a mask ... sorry :-( +- im_histnD() makes an n-dimensional histogram from an n-band image +- im_col_pythagoras() patch +- IM_NUMBER() now returns int not size_t +- new win32 build system from Juan and friends, based on tmake +- sample project files for MSVC added, thanks Juan +- win32/ subdir now has the win32 build systems +- spec/ subdir now has the spec files for building RPMs +- dist now includes formatted documentation +- license change: VIPS is now LGPL, nip stays GPL ... this means proprietary + programs can link against the vips library +- had a report of a working VIPS build on a 64 bit system (!) +- im_log_dmask() now includes all of the negative lobe, thanks matt +- vips-7.8 start script now auto-relocates +- im_spcor_raw(), im_fastcor_raw() now exported + +29/4/03 +- started 7.8.9 +- changes to build to help MSVC +- oops, makedef.pl missed out function names with an initial cap, and + error_exit() +- im_min() and im_max() gave random wrong results for >1 thread on >1 CPU + machines (bug introduced in 7.7.20), thanks Joe +- vips.c no longer generates C++ wrappers for functions with no image argument + (thanks Haida) +- im_invertlut() now wrapped by hand in VMask.cc +- C++ docs updated +- added im_open_header(), returns an IMAGE with just width/height/etc and no + data +- ... so now "header" will print useful stuff even on truncated files +- tiff writer knows about alpha (thanks Jenny) + +7/2/03 +- started 7.8.8 +- build failed with lcms turned off +- im_spcor() could segv for 16bit images (thanks Joe) +- im_tiff2vips() read resolution expressed as pixels/cm incorrectly +- im_vips2tiff() tries not to write mad resolutions +- header and im_open file type tests reordered for slight speedup +- im_copy_set() had a broken dispatch function for xres/yres +- im_fwfft() exploits libfftw real -> complex transform if possible for a 2x + speed-up (thanks Matt) +- im_invfftr() added for complex -> real inverse transform for 2x speed-up + (thanks Matt) +- im_freqflt() now uses im_invfftr() for real result and speedup +- im_flipver() could segv on some inputs, thanks Clare +- relational operators now work on complex +- relational rewritten ... now fractionally slower, but 1/3 the size +- vips2dj -1:1 produced incorrect height +- better overlap-too-small detection in mosaicing code +- im_system() can have NULL output +- global balance ignores overlaps with only transparent pixels + +3/1/03 +- started 7.8.7 +- worked in patch from Hans Breuer (thanks!) + - png read/write with im_png2vips(), im_png2vips_header(), + im_vips2png(), im_ispng() + - im_errorstring() and im_col_displays() are now functions not externs + (helps DLLs) + - many include fixes to help native win32 build +- added libMagick support, 78 file formats now loadable with + im_magick2vips(), im_magick2vips_header() and im_ismagick(), w00t +- now installs vips.m4 to $prefix/share/aclocal +- added im_icc_export_depth() ... export to device space with a specified bit + depth (8 or 16) +- vips.def now rebuilt with custom rule in libsrc/Makefile.am +- removed externs im_Type, im_BandFmt, im_Coding, im_Compression to simplify + DLL build +- im_mmap() -> im__mmap(), since it's supposed to be an internal function +- new vips-7.8.x/proj directory holds unsupported sample makefiles and + config.h for building with the MSC toolchain +- new scripts batch_image_convert and batch_rubber_sheet (thanks Joe) +- added the RPM .spec files to the main distribution +- InitializeMagic() now passed "" rather than NULL to avoid assert() problems + on some libMagic versions + +2/12/02 +- started 7.8.6 +- now reads 8-bit RGBA tiff +- C++ build guide fixes (thanks fsicre) +- im_Type2char array text slightly messed up +- global_balance is safer for complex mixed mosaics +- removed im_lintra() fallback to im_copy() for scale == 1, offset == 0 ... + too confusing +- im_tiff2vips() now reads 16-bit LAB +- added im_Lab2LabS() and im_LabS2Lab() + +5/11/02 +- started 7.8.5 +- fix for mmap window of local region ... caused im_iterate() to break + sometimes for large images, in turn occasionally breaking + im_max()/im_min()/etc. (thanks Joe) +- tiny speed up for im_rot90()/270() +- on install on win32, add .exe suffix for links +- vips.c knows to remove .exe suffix for linked commands +- added im_errormsg_system() ... decode win32 error codes too +- pagesize calcs for roving mmap windows were messed up on win32 (thanks Kirk) +- some TODO cleanups +- global balance broke horribly if you had filenames with spaces in (thanks + Clare) + +31/10/02 +- started 7.8.4 +- im_unmapfile() includes mixed up on mac os x +- libtool patched for mac os x +- vips.c sets numeric locale to "C" + +27/10/02 +- started 7.8.3 +- configure fixes help mac os x +- im_guess_prefix() adds ".exe" suffix on w32 if not there +- changed im_measure() error messages to number bands from 1 +- added func descriptor for im_read_dmask() to help nip, updated C++ API, docs + +21/10/02 JC +- started 7.8.2 +- tries rand() if random() is not available +- tries mktemp() if mkstemp() is not available +- turns off realpath() if not available +- added IM_DIR_SEP/IM_DIR_SEP_STR directory separator character/string +- added IM_PATH_SEP/IM_PATH_SEP_STR path separator character/string +- added im_path_is_absolute() +- vips.c knows to link to vips.exe on win32 +- spot mingw* and set BINARY_OPEN +- open images in binary too (since we now read() the header) + +10/10/02 JC +- im_lintra() and im_lintra_vec() were broken for complex images :-( thanks + matt +- renamed im_and() as im_andimage(), im_eor() as im_eorimage() and im_or() as + im_orimage() ... avoids breakage in the C++ layer +- added im_dE00_fromLab() +- limited release as vips-7.8.0 + +2/10/02 +- renamed as vips-7.8, woohoo +- revised documentation + +19/9/02 JC +- started sorting out VIPS #defines ... there are now a sensible set of new + names (eg. NOCODING becomes IM_CODING_NONE, LAB becomes IM_TYPE_LAB) +- define IM_NO_VIPS7_COMPAT to turn off the old names +- added im_mmap()/im_munmap() layer for windows portability +- removed the contents of history.h .. obsolete +- added IM_IMAGE_ADDR() macro + +10/9/02 JC +- handle errors from TIFF lib correctly +- configure fixes for cygwin +- CMYK TIFF write fixed +- configure fixes for mingw + +5/9/02 JC +- im_cp_desc() now copies Xoffset/Yoffset + +21/8/02 JC +- started 7.7.24 +- reads CMYK TIFF +- reads dpi from TIFFs +- better float Xres/Yres + +14/8/02 JC +- new header fields Xoffset and Yoffset ... used by functions to hint + the position of the origin in output images +- support added to c++ api and to header +- im__lrmerge(), im__tbmerge(), im__affine(), im_insert(), + set Xoffset/Yoffset +- now uses , not for better suse w0rkage +- better configure for fftw (uses libdfftw name if libfftw not found) + +8/8/02 JC +- large file support with mmap() windows ... had to change + im_prepare_inplace() to im_prepare_to() + + benchmark: + + - system + + hardware: 2 x 2.5GHz P4, 1GB RAM, 15k SCSI, ReiserFS + os: suse 8 (kernel 2.4.18) + compiler: gcc 2.95.3, -O2, threads turned on + images: fred.v, fred2.v; both 4k by 4k LABPACK (64MB) + images: jim.v, jim2.v; both 15k by 15k LABPACK (900MB) + time: smallest real of 5 runs, system idle + vips: 7.7.23, debug on in im_openin.c, window limit set with an + environment variable + + - benchmarks + + cpu-bound: im_sharpen fred.v fred3.v 11 1.5 20 50 1 2 + io-bound: im_insert fred.v fred2.v fred3.v 4000 0 + worst-case: im_rot90 fred.v fred3.v + + - results + + desktop: + + no mmap windows mmap windows + + cpu-bound real 0m3.712s real 0m3.970s + user 0m6.010s user 0m6.390s + sys 0m0.900s sys 0m1.110s + + io-bound real 0m1.813s real 0m1.865s + user 0m0.900s user 0m0.990s + sys 0m1.720s sys 0m1.520s + + worst-case real 0m1.344s real 0m3.039s + user 0m1.270s user 0m2.230s + sys 0m0.850s sys 0m3.050s + + not quite sure why sharpen is a little slower (4%?) ... IO speed is about + the same though ... worst-case is having to constantly move windows about + (500,000 page faults, vs 10,000 for no windows) + + again, with an image larger than RAM + + no mmap windows mmap windows + + io-bound real 2m52.759s real 2m11.172s + user 0m14.940s user 0m14.890s + sys 0m29.940s sys 0m26.560s + + worst-case real 3m35.391s real 3m50.760s + user 0m19.850s user 0m26.600s + sys 0m12.650s sys 0m43.130s + + mmap windows actually slightly faster in this case ... plus they stress the + OS less + +31/7/02 JC +- added -lm for better lcms detect +- README notes for fftw on suse8 +- im_profile() sets HISTOGRAM for output image +- im_copy()/im_copy_set() function descriptor no longer sets PTOP ... helps + avoid LUT problems +- im_subsample()/im_zoom() fall back to im_copy() for shrink/grow == 1 +- im_lintra() falls back to im_copy() for scale == 1, offset == 0 +- no longer use Type == LUT ... all just Type == HISTOGRAM now +- im_blend() was messed up for > 1 band images :( + +16/7/02 JC +- started 7.7.23 +- im_XYZ2sRGB() wasn't setting Type = sRGB +- im_icc_import() was broken for rgb +- im_header_string() had wrong return type in function database + +13/7/02 JC +- added im_flood_blob() +- added im_open_local_array() ... C API convenience function +- oop, im_flood() was missing a man page +- Type == FOURIER added to help visualisation +- released as 7.7.22 + +30/6/02 JC +- JPEG, TIFF and PPM import all now set sRGB Type for RGB import +- im_header_int(), im_header_double() and im_header_string() added to aid + UIs +- now uses gettimeofday(), not time() +- for consistency with other trig functions, im_c2amph() now returns degrees + not radians (ouch) +- added im_c2rect() ... turn (amp, phase) to rectangular +- added im_sign() ... unit vector in direction of value +- better im_scaleps() ... old code was terrible +- rewritten im_rotquad() ... now partial +- im_icc_export()/_import() now do ABSOLUTE correctly +- added im_icc_ac2rc() ... converts absolute to relative colorimetry + +25/6/02 JC +- added im_copy_set(3) ... like im_copy(), but set informational header fields + +20/6/02 JC +- added im_ceil(), im_floor() +- im_Lab2LabQ was not clipping a/b range correctly +- im_icc_export(), own ABSOLUTE mode +- released as 7.7.21 + +28/5/02 JC +- im_remainderconst_vec broken for float/double +- added Yxy colourspace + +16/05/02 JC +- auug, libtool was all messed up ... redone all the autotools stuff +- uses libtool convenience libraries to build vips in sections +- uses config subdir for temp files and .m4 things +- patched stupid suse config.guess +- vips2dj patched for better raw cmyk +- released as 7.7.20 + +12/5/02 JC +- im_vips2jpeg*() and im_vips2ppm() now both partial +- started updating the C++ guide +- had to change the location of the C++ headers :-( all C++ progs should now + have: + + #include + + this is so things can work on systems which do not have case sensitive + file systems + +- changes for Mac OS X + * im_system() TRUE/FALSE removed + * searches /*/[lib|include] to get fink libs for tiff and jpeg + +30/4/02 JC +- several functions were missing IM_FN_PIO in their descriptor ... this was + harmless for nip/ip/C, but broke the ref counting in the C++ layer +- im_system() now defaults "/tmp" for temp files +- STRING input and output args were broken for C++ :-( +- threads exit more quickly on error +- im_min()/im_max() now partial (at last) +- im_remainderconst()/im_remainderconst_vec() added +- --with-dmalloc configure switch +- vips2dj does CMYK and mono too +- im_vips2tiff() allows any number of bands (but not the right way to + write CMYK, see TODO) + +26/4/02 JC +- old ICC profile reader removed +- little cms wrapped ... configure spots it, im_icc_transform() uses it to map + between two images +- also im_icc_import() and im_icc_export() so you can see PCS images +- im_icc_present() to test for existence of lib +- README fixes + +4/4/02 JC +- TODO changes +- oops, DEBUG left on in im_invertlut() + +2/4/02 JC +- im_fwfft.c/im_invfft.c now use libfftw if available ... about 5x speed up + and double precision +- added FIND_FFTW autoconf macro +- include/vips/proto.h changes + +26/3/02 JC +- started 7.7.19 + +25/3/02 JC +- im_log_dmask() was broken (thanks matt) +- casts between VDMask and VIMask were broken (thanks matt) +- various error msgs improvements and tiny man page fixes + +13/3/02 JC +- tb/lr merge first/last cache moved to per-call state for better sharing +- im_remosaic() bails out faster on error and makes better error messages + +13/3/02 ruven +- im_vips2tiff() pyramids stop at tilesize, not 64x64 + +25/02/02 JC +- im_remosaic() is smarter, and works better with im_global_balance() +- im_affine() 2x faster + +14/2/02 JC +- started 7.7.18 +- vips.m4 and libsrc/Makefile.am fixes for IRIX + +11/02/02 JC +- vips/thread.h and vips/threadgroup.h were missing extern "C" for C++ +- VImage::write() now tracks dependencies, so you can write() to a partial + safely ... although it's not a very useful thing to do (thanks Mike) +- new VImage::print() function for debugging +- added im_print() + +22/01/02 JC +- started 7.7.17 + +15/01/02 JC +- im_rect_unionrect() and im_rect_intersectrect() safer for repeated args +- im_video_v4l() no longer perrror()s on ioctl fail for less spam + +03/01/02 JC +- started 7.7.16 +- im_version_string() really does return the date as well now + +12/12/01 JC +- im_guess_prefix() extra smartness for relative path names +- VImage() no longer uses tmpnam() (thanks Paul) + +11/12/01 JC +- renamed im_fexists() as im_existsf() + +7/12/01 JC +- ppm man pages added (doh) + +28/11/01 JC +- warnings on g++ 2.96 fixed + +22/11/01 JC +- started 7.7.15 +- im_video_v4l() failed to compile on non-linux platforms + +7/11/01 JC +- im_remosaic() added +- im_*merge() are more intelligent about transparency in bizarre overlaps +- grr! putenv() semantics change on more recent clibs ... should be safer + now + +19/10/01 JC +- VDisplay( "display name" ) segved on unknown display :-( thanks mike + +26/9/01 JC +- contrib tools get data files from share/vips/xxx area now +- im_vipshome() renamed as im_guess_prefix(), reworked for new package layout +- doc/ build sorted out +- ... but of course, docs still need updating for 7.8 + +20/9/01 JC +- fix to im_vipshome() +- ip2 renamed as nip +- split to library only ... separate ip and nip packages +- new VIPS_VERSION_* macros set from configure.in in vips/version.h.in +- vips.m4 VIPS finder +- reworked README, doc/README and TODO +- now installs to /usr/local/ by default +- fmask4th.c was including varargs.h ... d'oh +- include area reorganised: everything inside now ... hopefully the + only user-visible change is that all plain C progs need to change: + + #include + + to: + + #include + + the C++ API should be unaltered + +21/8/01 ruven +- im_setupout() was missing some #includes + +20/8/01 JC +- started 7.7.14 + +15/8/01 JC +- added libxml dependency for ip2 + +27/7/01 JC +- im_conv(), im_convf(), im_convsep(), im_convsepf() now reject masks with + scale == 0 + +26/7/01 JC +- started 7.7.12 + +25/7/01 JC +- started 7.7.11 +- oop, im_histeq() and im_tonemap() also missed +- better error messages from im_run_command() + +23/7/01 JC +- started 7.7.10 +- im_sharpen() failed due to change in im_band_extract() offset + +20/7/01 JC +- started 7.7.9 + +4/7/01 JC +- im_open(,"w") open() delayed until im_setupout(), very slightly safer +- updated im_open() man page +- im_tiff2vips() now embeds index in filename ... and it's page number (from + 0), not subsample factor +- finally bit the bullet ... im_extract()/im_extract_band() now number from + zero (sorry!) +- and im_lrmosaic()/im_tbmosaic() bandno param too + +29/6/01 JC +- im_region_free() now frees immediately + +27/6/01 JC +- im_vips2tiff() man page updated for deflate, 2 years late + +22/6/01 JC +- oops, limit wrong on im_rank() + +21/6/01 JC +- better post_install for --prefix outside VIPS's tree +- -ltiff needs -lm in acinclude.m4 ... fixes configure on redhat 7.x + +13/6/01 JC +- started 7.7.8 + +6/6/01 JC +- im_invertlut() added + +31/5/01 JC +- im_colour_temperature, im_XYZ2Lab_temp, im_Lab2XYZ_temp added +- ... colour temp stuff needs sorting out properly + +25/5/01 JC +- added vips-config script, cf. gtk-config +- --without-threads option added +- did a bit of work on the C++ API docs + +24/5/01 JC +- added im_tiff2vips_header(), im_jpeg2vips_header() and im_ppm2vips_header() +- header uses these to print fields quickly +- switched to config.h +- configure.in rewritten ... much nicer, fewer options, more automatic + +17/5/01 JC +- im_matinv() didn't free stuff correctly on singular matrix + +16/5/01 JC +- vips2dj now knows about 5000ps printers +- allow RW mode for non-native VIPS image files, for 8 bit images + +2/5/01 JC +- started 7.7.7 + +1/5/01 JC +- im_addgnoise() did not work for >1 band images + +23/4/01 JC +- configure options to remove support for JPEG and TIFF ... helpful for a no- + dependencies build + +20/4/01 JC +- im_(v)snprintf() added +- all sprintf()s removed + +15/4/01 JC +- im_affine() had a rounding problem + +11/4/01 JC +- tiny mosaicing bug fixed in im__lrcalcon +- started 7.7.6 + +21/3/01 JC +- new iblend code in im_tbmerge() was typo-d +- mosaic1 was broken by affine too + +20/3/01 JC +- im_image() failed for FMTUCHAR + +12/3/01 JC +- started 7.7.5 +- im_sharpen() uses seperable convolution for big speed up +- new "Print" menu + +11/3/01 JC +- REALVEC renamed as DOUBLEVEC +- added IMAGEVEC +- added IM_INPUT_IMAGEVEC +- gbandjoin now has function description +- new function im_maxvalue() +- im_compass()/im_lindetect() reimplemented with im_conv()/im_maxvalue(), + about 15% faster, works for any type, partial +- im_gradient() reimplemented with im_conv()/im_abs()/im_add(), about 30% + slower, works for any type, partial + +10/3/01 JC +- new function, im_clip2fmt() converts between any image formats ... + slightly faster than the old im_clip() +- legacy im_clip2us() etc. functions now just call this + +9/3/01 JC +- im_conv() rewritten, simpler, about 10% faster +- im_convsep() rewritten, now does any non-complex type, partial, 20% faster +- new functions: im_convf(), im_convsepf() for DOUBLEMASK +- raw versions of each +- legacy convolvers (eg. im_convbi()) removed + +8/3/01 JC +- new function im_blend() +- new function im_lab_morph() +- speed up to im_ifthenelse() +- speed up to im_*merge() (uses integer arithmetic for integer blends) + +4/3/01 JC +- tiny speed ups to im_histgr() +- speed ups to im_maplut() + +3/3/01 JC +- new functions: im_histnorm(), im_histcum() +- im_histeq() more general +- im_vipshome() --- better behaviour for relative paths + +2/3/01 JC +- new video package +- im_video_v4l1() (video for linux) added +- configure.in switches to turn v4l1 on and off + +1/3/01 JC +- new im_histspec() implementation ... more general, bugs removed + +14/2/01 JC +- better vips2dj usage message + +13/2/01 JC +- im_image_sanity() added, called in various places in iofuncs + +9/2/01 JC +- added 'check' and 'name' class member stuff to ip from ip_gtk2 +- new "Plot" menu +- new "Overlay" menu +- more stuff in _stdenv/_list ... curried forms of head/tail etc. + +7/2/01 JC +- started 7.7.4 +- vips2dj and vdump now use im_vipshome() + +5/2/01 JC +- new im_vipshome() function +- min()/max() macros renamed as MIN()/MAX() +- new im_load_plugins() function +- vips.exe and ip now load $VIPSHOME/lib plugins at startup + +2/2/01 JC +- mosaicing functions now have an extra max blend width parameter + +30/1/01 JC +- fixed tbmerge no overlap detect + +13/12/00 JC +- started 7.7.3 + +30/12/00 JC +- vips.h fixes for cygwin/wingdi conflict + +27/11/00 JC +- added im_vips2ppm(), im_open() imports and exports it +- fixed nasty implicit output conversion problem for PIO dispatch() calls + +21/11/00 JC +- added im_ppm2vips() + +16/11/00 JC +- configure.in fixes ... jpeg found correctly now +- searches for libz as well + +16/11/00 JC +- started 7.7.2 + +5/11/00 JC +- speed up to lab2labq + +19/10/00 JC +- started 7.7.1 + +13/1/00 JC +- oops, im_open() was missing an 'else' in jpeg/tiff load + +5/8/00 JC +- im_vips2tiff() now has mode string embedded in output filename +- im_vips2jpeg() now has qfac in output filename +- im_open() understands this + +11/7/00 JC +- new im_image() function ... wraps a VIPS image around a memory buffer +- C++ layer changes: + * now use #include + * error renamed as VError + * new VMask() constructors + * new VImage() constructor + * new VImage::data() access member + * more operator equivalences: <, >, <=, >=, ==, !=, &, |, ^, % + +17/6/00 JC +- more consts added to vips protos + +10/5/00 JC +- minor configure.in changes to help solaris +- removed _TIFFmalloc() and _TIFFfree() calls + +9/3/00 JC +- fixed rounding problem in generate grid + +8/3/00 JC +- fixup to im_system() temp dir + +7/3/00 JC +- added im_system() +- small tidies + +1/3/00 JC +- better plugin test in configure.in + +26/2/00 JC +- fixes to Makefile.am in ip/src* and configure.in, to help ip find the right + gtk includes on systems with more than one gtk-xxx installed + +21/2/00 JC +- now builds ip and ip_gtk2 + +16/2/00 JC +- configure.in fixes for xil and some TIFF/JPEG strangeness + +15/2/00 JC +- im_global_balancef() was broken! d'oh +- note in README about enabling video cards + +10/2/00 JC +- configure now searches for xil ... FIND_XIL macro +- im_zoom() spots integer overflow +- better shape set on region drag end in ip +- better zoom-too-far handling in ip + +13/1/00 JC +- fixes to configure etc. to help cygwin +- jpeg is now searched for too ... FIND_JPEG macro + +10/1/00 JC +- global_balance() now uses new affine() atuff + +27/12/99 JC +- mosaic1 stuff now uses new affine() funcs + +21/12/99 JC +- added Joe's docs +- im_LabS2LabQ() rounding on a/b slightly broken for a/b == 0 + +20/12/99 JC +- new function: im_affine() +- similarity*() now in terms of affine() +- tests for error return in reduce.c from maplut +- small clean-ups + +15/12/99 +- im_version() added, new iofuncs package for it +- ip did not call zero-input-arg vips functions +- vips.c did not like zero-input-arg functions + +6/12/99 JC +- Sobel filter was a bit broken + +3/12/99 JC +- menu reorganisation +- generate gauss mask dialog + +2/12/99 JC +- reworked text file IO, better error messages + +1/12/99 JC +- reworked .iprc filename stuff, cleaner + +30/11/99 JC +- better menu set switcher in calc preferences +- ip now thinks it's 7.7 +- did a spellcheck on the ip guide + +29/11/99 JC +- 7.7 started! +- srgb D65->D50 converter +- Negate added to arith +- find similar pixel value dialog +- find similar colour dialog +- paste into background dialog + +19/11/99 JC +- adjust labq was mising +- shrink image defaulted to /2 not /1 +- sharpdropshadow broken +- Rotate.* broken +- vips-7.6.3 release + +18/11/99 JC +- ip didn't report space free on >2GB filesystems correctly +- ip did not link statically against libXpm on solaris7 + +17/11/99 JC +- im_histplot() failed for all 0's histogram +- new profile_image dialog +- man pages for im_sRGB2XYZ/im_XYZ2sRGB were broken +- new colourize image dialog +- new shrink image image dialog +- new expand image image dialog +- better doc Makefiles + +16/11/99 JC +- more menu reorganising +- new image_to_mask/mask_to_image buttons +- new match_two_images dialog +- new measure_colour_chart dialog + +15/11/99 JC +- new custom LABQ sharpen in Image menu +- configure support for SGI video (thanks Ruven) +- menu fiddling in ip +- new "resize canvas" menu item +- im_insert_noexpand() added, no docs tho' + +11/11/99 JC +- new ip menu item: Image=>Adjust white/black/saturation of LabQ +- fixed bug in decompose complex number +- browse-icons now ignores errors +- better error msg for "12 12" etc cases + +10/11/99 JC +- open hi-res gives proper error msg if no file found +- made file-select boxes a bit more compact and clearer +- updated configure.in for vips-examples-7.6 + +9/11/99 JC +- new column start point moved +- vdump now defaults to subsample 1, portrait +- navigation boxes were broken +- no longer shrink-to-fit if loading as high res +- better positioning of zoom windows +- removed some old cruft + +8/11/99 JC +- ooops, im_remainder() got lost somehow +- refguide converted to latex and updated +- cppguide updated + +5/11/99 JC +- libguide converted to latex and updated + +4/11/99 JC +- ip guide now latex2htmls cleanly ... still needs updating tho' +- cpp/app guide latex2html redone + +2/11/99 JC +- configure no longer adds -32 to IRIX builds for you +- some more stuff in README +- simple DEBUGM malloc tracking, made libsrc/iofuncs/memory.c +- changed im_malloc() to return void * +- removed duplicate im_malloc() proto from util.h +- changed all malloc/free to go through im_malloc()/im_free() + +23/10/99 JC +- double-click on error image now pops a load browser and an error dialog +- add-new-column no longer scrolls to right edge of workspace +- better scroll-to-bottom on item add +- load ws twice does not cause 'already open' errors +- memorise directory button in fsb + +18/10/99 JC +- new FIND_TIFF/FIND_MOTIF macros for acinclude.m4 +- new ./configure switches, see ./configure --help +- enabled static libs + +8/10/99 +- broke action_proc_bop() into smaller functions, stops bad code gen on + gcc2.95.1 (and others, prolly) +- restored old Makefiles in doc/src/ipguide +- vips7.6 script renamed as vips-7.6 + +5/10/99 +- replace image was broken +- some menu reorganisation +- 'reload all menus' button + +4/10/99 +- computed regions in ip were broken +- updated system.iprc defaults +- new menu item: adjust white/black points + +2/10/99 +- browse icons was broken +- greyscale 16-bit tiled tiff was broken +- extra mutex locks for TIFF*() in im_tiff2vips() + +1/10/99 +- some automake probs fixed +- squished two ip bugs + +30/9/99 +- fixed problem with pthreads, now works on suse6.2 as well +- new API stuff for threaded evaluation with im_threadgroup_t +- new public interface provide platform independent threads/locks/semaphores +- no error box if you zoom out too far now + +26/9/99 +- lr/tb merge blend was not quite right ... should be smoother now +- histplot broken for float images + +24/9/99 +- better mono->labq converter +- more portable ispoweroftwo detect for freq filter stuff + +23/9/99 +- better graphics expose handling + +17/9/99 +- >/< stuff in ip was a bit mixed up +- ink preview fixed for mono images +- help popup fixed + +15/9/99 +- linedetect and sobel filters for ip, thnx Kirk + +14/9/99 +- Find_histogram was broken +- im_profile() man pages was broken +- ooops, ip had old set of macros + +10/9/99 +- im_and/im_or/im_eor now work for any integer type + +Summer hols: (2nd half August '99) +- initial heap block larger to avoid start gcs +- def slicer fixed +- larger max heap +- toolkits with initial '_' hidden by default +- custom recomb +- dialog.def removed +- ... other menu fixes +- generate grid menu +- im_remainder() added +- new cursor change code, hglass rotates during comp! +- dialog.c handles cursor changes better +- rubber band in paintbox displays +- mag widget stays on right! +- undo/redo single pixel paint ops +- heap size control from prefs +- cancel for reductions as well as for image calc +- browse stuff reworked, no more .icon.v files +- save stops you overwriting open .v files +- better animate_countdown() handling +- better resize behaviour for bars added to images +- better code generation, bug fixed in state tracking +- auto recover from crash diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..b42a17ac --- /dev/null +++ b/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..26c657e7 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,56 @@ +# only build in the python dir if we can +if HAVE_PYTHON +P_COMPILE_DIR = python +P_DIST_DIR = +else +P_COMPILE_DIR = +P_DIST_DIR = python +endif + +SUBDIRS = \ + libsrc \ + src \ + include \ + libsrcCC \ + contrib \ + po \ + $(P_COMPILE_DIR) + +EXTRA_DIST = \ + doc \ + benchmark \ + bootstrap.sh \ + win32 \ + vipsCC-7.12.pc.in \ + vips-7.12.pc.in \ + vips-7.12.spec.in \ + acinclude.m4 \ + depcomp \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in \ + $(P_DIST_DIR) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = vips-7.12.pc vipsCC-7.12.pc + +install-exec-hook: + -rm -rf ${DESTDIR}$(datadir)/doc/vips + $(mkinstalldirs) ${DESTDIR}$(datadir)/doc/vips + -cp -r ${top_srcdir}/doc/html ${top_srcdir}/doc/pdf ${DESTDIR}$(datadir)/doc/vips + +dist-hook: +# make sure we don't get any .svn dirs from EXTRA_DIST + -find $(distdir) -name .svn -exec rm -rf {} \; + +uninstall-hook: +# make sure we have write permission for 'rm' + -chmod -R u+w ${DESTDIR}$(datadir)/doc/vips + -rm -rf ${DESTDIR}$(datadir)/doc/vips + +distclean-local: +# need to remove expanded intltool files in build area to pass distcheck + -rm ${top_builddir}/intltool-extract \ + ${top_builddir}/intltool-merge \ + ${top_builddir}/intltool-update + diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..ac7ec9f3 --- /dev/null +++ b/NEWS @@ -0,0 +1,232 @@ +VIPS changed from 7.10 to 7.12 (not exhaustive, see ChangeLog for details) + +Non-backwards compatible changes +================================ + +- IMAGE->Hist is deprecated, use im_history_get() instead +- im_append_Hist() removed +- IMAGE->Bbits is deprecated (now ignored) +- im_region_local() replaced by im_region_buffer() + +VIPS enhancements +================= + +- new backwards and forwards compatible VIPS file format: it has a new + metadata system that efficiently shares and propogates ICC profiles, + EXIF data, etc. & whatever +- speed ups: new pixel buffer cache saves and reuses computations, uses liboil + where possible for a small speedup +- SMP scaling: simplified lock system improves SMP scaling, double-buffered + image writes overlap computation +- lower mem use: new mmap window cache shares file mappings, history buffers + share and reuse image history metadata +- built-in support for new image formats: OpenEXR, Analyze7, CSV +- RGB16 and GREY16 image hints +- uses GOption for much better command-line argument handling +- better C++ binding: namespaces, VError derives from std::exception, image + and number vector constants +- python binding +- gcc sentinel attributes added +- added GValue as an arg type +- added im_benchmark() and friends for testing +- new functions: + im_analyze2vips(), im_benchmark*(), im_buildlut(), + im_concurrency_get/set(), im_contrast_surface(), + im_contrast_surface_raw(), im_copy_from(), im_copy_morph(), + im_cp_desc_array(), im_cp_descv(), im_extract_areabands(), + im_flood_blob_copy(), im_get_option_group(), im_grid(), + im_header_exists(), im_header_map(), im_history_get(), + im_invalidate(), im_isscalar(), im_lineset(), im_linreg(), + im_meta*(), im_msb(), im_norm_dmask(), im_project(), + im_raw2vips(), IM_REGION_ADDR_TOPLEFT(), im_tile_cache(), + im_video_test() + + +VIPS changed from 7.8 to 7.10 (not exhaustive, see ChangeLog for details) + +Non-backwards compatible changes +================================ + +- no longer generates vips-config script, instead it makes .pc files for + pkg-config ... replace any "vips-config" with "pkg-config vips-7.10" +- origin hint semantics changed ... it now records the position in the output + image of the origin of the input image +- all float to int casts on pixel values are now floor() rather than rint(), + for consistency with C casting semantics + +VIPS enhancements +================= + +- nip reworked for gtk+-2.4, now called nip2 +- now based on glib, so VIPS no longer has it's own thread, plugin and data + structure libraries +- i18n support, although not quite complete yet +- new error message API to make i18n possible (compatibility macros mean the + old API still works) +- new 'start up VIPS' call, im_init_world(), sets up i18n and threads + (optional to call it, but i18n won't fully work unless you do) +- C programs now just need "#include ", no other includes + required +- wraps the Pango font rendering library, see im_text() +- new function flag: NOCACHE ... means the result of the call should not be + cached, useful for video grab functions and paintbox functions +- im_embed() now supports a wider range of embedding styles, including a fast + edge stretch +- all area operations use the new embed style to expand their input rather + than their output, so there are no more black borders on filtered images +- other new functions: im_render(), im_cache(), im_extract_bands(), + im_copy_swap(), im_rint(), im_make_xy(), im_init_world(), im_error(), + im_warn(), im_diag(), im_rank_image(), im_replicate() +- many fixes and improvements to old funcs +- configure and portability improvements + +Bug fixes +========= + +- all serious bug fixes got back-ported to 7.8, I think + + +VIPS changes from 7.6 to 7.8 (again, not exhaustive, see ChangeLog for details) + +Non-backwards compatible changes +================================ + +- output format options specified in filenames: new prototypes for load and + save tiff and jpeg +- C++ API name changes ... error => VError +- include path change + - C includes + - C++ includes +- im_extract*() numbers bands from zero, not from 1 ... also other funcs which + refer to bands (eg. im_lrmosaic() etc.) +- mosaic funcs have extra max blend width parameter + +VIPS enhancements +================= + +- rewritten and much fancier ip (see separate docs), now called nip ... old ip + (slightly fixed) still there +- mmap windows remove 2GB image size limit +- images have an origin hint field +- icc transforms supported via wrapping of Little CMS +- much, much faster Fourier transforms via wrapping of libfwfft +- ppm/pgm/pbm read/write +- C++ API enhancements: new constructors, more operator equivalences +- new colourspace: Yxy +- support for D50 colour temperature images +- new functions: im_image(), im_system(), im_version*(), im_blend(), + im_copy_set(), im_flood_blob(), im_icc_*(), im_open_local_array(), + im_header_*(), im_sign(), im_ceil(), im_floor(), im_remainderconst*(), + im_print(), im_guess_prefix(), im_remosaic(), im_invertlut(), Yxy funcs, + color temperature funcs, im_clip2fmt(), im_blend(), im_lab_morph(), + im_histnorm(), im_histcum(), im_video_v4l(), im_LabS2Lab(), im_Lab2LabS() +- new type: IMAGEVEC +- header is much faster +- ip/nip split to separate packages +- better vips2dj +- better cygwin support, mingw support too +- Mac OS X and Windows build support +- new set of sensible #define names, old names can be turned off with + -DIM_NO_VIPS7_COMPAT +- many configure improvements + +Bug fixes +========= + +- speed ups +- fixes to implicit image format conversions +- im_zoom() spots integer overflow +- im_LabS2LabQ() rounding on a/b slightly broken for a/b == 0 +- fixes to refcounting in C++ API +- mask casts in C++ were broken + + + + +VIPS Changes for 7.4 to 7.6 + +Non-backwards compatible changes +================================ + +- im_histplot() has new (and better) rules +- im_andconst(), im_orconst() and im_eorconst() now take a + double, not an unsigned char, as their constant argument type +- im_global_balance_float() renamed as im_global_balancef() to be + more consistent with other functions +- im_global_balance_search() removed ... you can do this efficiently + with an ip macro now +- new parameter "gamma" to im_global_balance() and + im_global_balancef() ... sets the gamma for the input device with + which the images were captured, set to 1.0 for old behaviour +- im_malloc() now returns void *, not char * + +Bug fixes +========= + +- tiny memory leak in im_list_remove() fixed +- oops, the value for sRGB in the header was wrong, now 22 +- missing function descriptor for im_rank_raw() +- im_dECMC_fromLab() was broken +- im_erode() and im_dilate() output incorrect error messages + if mask elements were not one of 0, 128, 255 +- im_rotate_*mask*() functions were not preserving scale and offset + values + +Package enhancements +==================== + +The whole thing is now GPL, with GNU configure + +ip changes +========== + +- better display control bar +- matrix operations now appear in the workspace +- new UI elements: input options and toggles +- better LUT optimisation --- arithmetic operations on UCHAR images should be + much faster +- new macro language --- same as old one, but faster and much more powerful +- all standard menus rewritten in new macro language, much nicer! +- batch operations with Group/Ungroup +- now uses GDK for drawing in image windows, much better colour handling on + 8/16-bit graphics cards +- image repaint is now threaded for an about-linear speedup as you add + more CPUs +- many interface improvements and polishes + +VIPS enhancements +================= + +- VIPS now uses POSIX threads (7.4 used Solaris threads) and has been rejigged + to provide a nice threading API to functions which call the library +- im_open() now knows about LSB- and MSB- first VIPS images, and + silently converts them to native order ... it also now ignores case when + deciding what format to write +- new parameter type IM_INPUT_REALVEC, a vector of doubles +- new set of functions with vector constants: im_lintra_vec(), + im_powtra_vec(), im_expntra_vec(), all relational + (im_equal_vec(), im_notequal_vec(), etc.), all boolean + (im_and_vec() etc.) +- new flag for function descriptors: "IM_FN_PTOP" set for point-to-point + functions ... makes ip use LUTs for operation +- im_tiff2vips() now reads and writes 16-bit images, and knows about zip + (deflate) compression +- convenience functions im_extract_band() extracts a band from an image; + im_extract_area() extracts an area +- im_list_member() tests for list contains object +- new functions im_write_*mask_name(), im_create_*maskv() +_ new functions im_remainder() and im_profile() +- fourier filtering, forward transform, reverse transform, make power spectrum + all free memory more quickly, making them suitable for very large images +- new functions im_isMSBfirst() and im_amiMSBfirst() test images + and this processor for MSB first byte order +- im_malloc() now prints low-on-memory warnings to stderr with + im_warning() for easier bug catching +- D65_X0 updated for latest recommedations, D60, D93, + D55, D50, A, B, C added +- minor fixes to the C++ interface to bring it in line with the newer ANSI + standards +- more and more comprehensive prototypes in "proto.h" to aid C++ (use of + "const" etc.) +- im_and*(), im_or*() and im_eor*() can now work on any + integer image diff --git a/README b/README new file mode 100644 index 00000000..e2573272 --- /dev/null +++ b/README @@ -0,0 +1,143 @@ +VIPS 7.12 +========= + +VIPS is an image processing library. It's good for large images and for +colour. There's a GUI as well, see the VIPS website: + + http://www.vips.ecs.soton.ac.uk + +Getting VIPS from SVN +===================== + +Enter: + + svn co https://vips.svn.sourceforge.net/svnroot/vips/vips7 + +Building VIPS from source +========================= + +In the VIPS directory, you should just be able to do: + + user% ./configure + user% make + +then as root: + + root% make install + +By default this will install files to /usr/local/bin, /usr/local/share/vips, +/usr/local/include, /usr/local/lib and /usr/local/man. + +If you have problems, read on. + +Building VIPS on win32 +---------------------- + +Probably the easiest route is to use mingw/msys. This provides a GNU-style +build environment for win32. + + http://www.vips.ecs.soton.ac.uk/index.php?title=Build_on_windows + +Alternatively, vips-7.x/win32 contains sample build systems using the +Microsoft toolchain. See the README in there for details. + +Building VIPS on OS X +--------------------- + +I used macports to install all the dependencies. Fink also works. + + http://www.macports.org + +You need to tell configure to use the msgfmt from macports. Something like: + + GMSGFMT="/opt/local/bin/msgfmt" ./configure --prefix=/Users/john/vips + +Dependencies +============ + +VIPS has to have glib-2.x and libxml-2.0. The build system needs perl, +pkg-config and gnu make. + +Optional dependencies +--------------------- + +Optional support libraries ... also try ./configure --help to see flags for +controlling these libs. By default, if suitable versions are found, VIPS will +build support for them automatically. + +VIPS looks for stuff in /usr. If you have installed you own versions of these +libraries to /usr/local, vips will not see them. Use switches to VIPS +configure like: + + ./configure --prefix=/home/john/vips \ + --with-tiff-includes=/home/john/vips/include \ + --with-tiff-libraries=/home/john/vips/lib + +to get VIPS to see your builds. + +libjpeg + The IJG JPEG library. We use 6b, but 6a works too. + +libexif + if available, VIPS adds support for EXIF metadata in JPEG files + +libtiff + The TIFF library. It needs to be built with support for JPEG and + ZIP compression. 3.4b037 and later are known to be OK. + You'll need libz for this too. We use 1.1.3, others should work. + +libz + If your TIFF library includes ZIP compression, you'll need this too. + +videodev.h + If VIPS finds linux/videodev.h, you get support for Linux video + grabbing. + +fftw3 + If VIPS finds this library, it uses it for fourier transforms. It can + also use fftw2, but 3 is faster and more accurate. + + If the library is not found, VIPS falls back to it's own internal FFT + routines which are slower and less accurate. + +lcms + If present, im_icc_import(), _export() and _transform() are available + for transforming images with ICC profiles. + +large files + VIPS uses the standard autoconf tests to work out how to support large + files (>2GB) on your system. Any reasonably recent *nix should be OK. + +libpng + if present, VIPS can load and save png files. Version 1.2+ preferred. + +libMagick + if available, VIPS adds support for loading all libMagick supported + image file types (about 80 different formats). No saving though. + +pango +freetype2 +fontconfig + if available, VIPS adds support for text rendering. You need the + package pangoft2 in "pkg-config --list-all" + +liboil + if available, you get some inner loops replcaed by calls to liboil's + library of optimised inner loops + +OpenEXR + if available, VIPS will directly read (but not write, sadly) OpenEXR + images + +swig +python +python-dev + if available, we build the python binding too + +Disclaimer +---------- + +Disclaimer: No guarantees of performance accompany this software, nor is any +responsibility assumed on the part of the authors. Please read the licence +agreement. + diff --git a/THANKS b/THANKS new file mode 100644 index 00000000..9d6e6396 --- /dev/null +++ b/THANKS @@ -0,0 +1,25 @@ +VIPS THANKS file + +VIPS was originally written by Nicos Dessipris, Kirk Martinez and John Cupitt. + +Many people have contributed to VIPS by reporting problems, suggesting +improvements, or offering code. + + Matthew Hanson + Joe Padfield + Haida Liang + Ian Clarke + Steve Perry + Stephen Chang + David Saunders + Mike Westmacott + Chris Hurst + Jim Coddington + Lou + Rachel Billinge + Colin White + ENST + Thomson-CSF + +We've also had very helpful funding from the European Commission and +Hewlett-Packard. diff --git a/TODO b/TODO new file mode 100644 index 00000000..ea99d302 --- /dev/null +++ b/TODO @@ -0,0 +1,147 @@ +- when we fork for 7.13 do this stuff ... don't do now, we'll break the other + packages + + http://www.freshports.org/graphics/vips + + freebsd packaging now does: + + In both: + - use explict --mandir=${PREFIX}/man to avoid man-pages getting + into ${PREFIX}/shared/man incorrectly + - deal with the NOPORTDOCS situation by simply not-extracting + the extra documentation from the distribution tarball + - parallelize the build to scale with the number of CPUs + + In vips: + - move the (giant) list of man-pages into a separate Makefile.man + - turn the pages, which contain only `.so other-page', into + MANLINKS (specified in Makefile.man) + - provide a "maintainance target" to regenerate the Makefile.man + during the next upgrade + - do not install the HTML-ized versions of man-pages + - create OPTIONS for use of devel/liboil and graphics/ImageMagick + (OPTION to use PYTHON awaits portmgr's decision/action) + + In nip2: + - do install the HTML pages regardless of NOPORTDOCS -- these + are accessible to the user through the application GUI + - arrange for update-mime-database and update-desktop-database + to be run upon install (@exec) and uninstall (@unexec) + - LIB_DEPEND on math/gsl, which nip2 can use for extra functionality + +Python binding +============== + +- python startup fails with plugins in vipslib: + + Fatal Python error: can't initialise module vips + plugin: unable to open plugin "/home/john/vips/lib/resample.plg" + plugin: /home/john/vips/lib/resample.plg: undefined symbol: im_start_one + + do we need to build plugins with -rpath etc. and more libs? + + or do we need to make sure our python modules export all their im_ symbols? + +WONTFIX +======= + +- TIFF load/save should use meta system for unknown tags + +- balance should use new meta stuff + +- magick2vips should spot ICC profiles and attach them as meta + +- magick should set some header field for n_frames and frame_height? see also + analyze + +- see TODO notes in openexr read (though they all need more openexr C API) + + consider openexr write + +- matrix invert is a copypaste of NR :-( fix this + +- add GREYCstoration filter + + http://www.haypocalc.com/wiki/Gimp_Plugin_GREYCstoration + + actually, it has to be a plugin, since their code is GPL + + and very memory-hungry for large images :-( needs 20x size of image to be + processed + + could we rewrite with VIPS? how much more stuff would we need to add? + + try again using the current version of the filter from the suthors rather + than the gimp plugin + +- im_csv2vips() could use "-" for filename to mean stdin + + but then we'd have to read to a malloced buffer of some sort rather than an + image, since we might need to grow it during the read, since we couldn't + then seek + +- add erode/dilate 3x3 special case using a 512-bit LUT + + ... nope, actually slower :-( we end up doing an inner loop like + + for( i = 0; i < 9; i++ ) + bits |= (p[o[i]] != 0) << i; + + which is horrible. Maybe if we had a one band true 1 bit image it'd be + quicker since we could get bits out in parallel and wouldn't have to worry + about converting non-zero to 1 + + could have a Coding type for bitpack? eg. relational produces a bitpack + image by default, boolean & morph can work on bitpack etc + + maybe something for vips8 ... we could have a flag on operations for the + coding types they can accept, and if they are passed other type, they are + automatically converted + +- non-linear sharpen: replace each pixel by the lightest or darkest neighbour + depending on which is closer in value + +- can wrap other inplace funcs which use ink now we have vector_to_ink() in + inplace_dispatch.c + + see also comments in nip2 TODO ... we could auto-wrap in vips_call.c + + cleaner! + +- on win32, should not write matrix files in binary mode, we want CR/LF chars + so we can load into excel etc easily + + how odd, we're doing + + if( !(fp = fopen( name, "w" )) ) { + + shouldn't be binary ... hmm + +Build +===== + +- xmlFree() is still broken :-( + + maybe we are not importing it correctly? im_readhist.c references + + _imp__xmlFree + + how is this made? look at gcc -E output ... maybe there's an extra define we + need to make it generate the right link code? + + see what libxml2.dll.a is exporting that looks anything like xmlFree + +- can we make a fftw3.dll? also, magick.dll? + + maybe just build with no-undefined? can we then link the DLL against the + static lib? + +- update gtk/glib/etc. on the PC to the latest versions, apparently much + quicker (esp. pango) + + + + +This TODO list is now held on the VIPS Wiki + + http://wiki.vips.ecs.soton.ac.uk/bin/view/Vips/TodoVips7 diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 00000000..5a2fd214 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,584 @@ +dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding +dnl +dnl FIND_ZIP[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] +dnl ------------------------------------------------ +dnl +dnl Find ZIP libraries and headers +dnl +dnl Put includes stuff in ZIP_INCLUDES +dnl Put link stuff in ZIP_LIBS +dnl +dnl Default ACTION-IF-FOUND defines HAVE_ZIP +dnl +AC_DEFUN([FIND_ZIP], [ +AC_REQUIRE([AC_PATH_XTRA]) + +ZIP_INCLUDES="" +ZIP_LIBS="" + +AC_ARG_WITH(zip, +[ --without-zip do not use libz]) +# Treat --without-zip like --without-zip-includes --without-zip-libraries. +if test "$with_zip" = "no"; then + ZIP_INCLUDES=no + ZIP_LIBS=no +fi + +AC_ARG_WITH(zip-includes, +[ --with-zip-includes=DIR ZIP include files are in DIR], +ZIP_INCLUDES="-I$withval") +AC_ARG_WITH(zip-libraries, +[ --with-zip-libraries=DIR ZIP libraries are in DIR], +ZIP_LIBS="-L$withval -lz") + +AC_MSG_CHECKING(for ZIP) + +# Look for zlib.h +if test "$ZIP_INCLUDES" = ""; then + zip_save_LIBS="$LIBS" + + LIBS="-lz $LIBS" + + # Check the standard search path + AC_TRY_COMPILE([#include ],[int a;],[ + ZIP_INCLUDES="" + ], [ + # zlib.h is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/include /usr/*/include \ + /usr/local/*/include \ + "${prefix}"/include/* \ + /usr/include/* /usr/local/include/* /*/include; do + if test -f "$dir/zlib.h"; then + ZIP_INCLUDES="-I$dir" + break + fi + done + + if test "$ZIP_INCLUDES" = ""; then + ZIP_INCLUDES=no + fi + ]) + + LIBS="$zip_save_LIBS" +fi + +# Now for the libraries +if test "$ZIP_LIBS" = ""; then + zip_save_LIBS="$LIBS" + zip_save_INCLUDES="$INCLUDES" + + LIBS="-lz $LIBS" + INCLUDES="$ZIP_INCLUDES $INCLUDES" + + # Try the standard search path first + AC_TRY_LINK([#include ],[zlibVersion()], [ + ZIP_LIBS="-lz" + ], [ + # libz is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/lib /usr/*/lib /usr/local/*/lib \ + "${prefix}"/lib/* /usr/lib/* \ + /usr/local/lib/* /*/lib; do + if test -d "$dir" && test "`ls $dir/libz.* 2> /dev/null`" != ""; then + ZIP_LIBS="-L$dir -lz" + break + fi + done + + if test "$ZIP_LIBS" = ""; then + ZIP_LIBS=no + fi + ]) + + LIBS="$zip_save_LIBS" + INCLUDES="$zip_save_INCLUDES" +fi + +AC_SUBST(ZIP_LIBS) +AC_SUBST(ZIP_INCLUDES) + +# Print a helpful message +zip_libraries_result="$ZIP_LIBS" +zip_includes_result="$ZIP_INCLUDES" + +if test x"$zip_libraries_result" = x""; then + zip_libraries_result="in default path" +fi +if test x"$zip_includes_result" = x""; then + zip_includes_result="in default path" +fi + +if test "$zip_libraries_result" = "no"; then + zip_libraries_result="(none)" +fi +if test "$zip_includes_result" = "no"; then + zip_includes_result="(none)" +fi + +AC_MSG_RESULT( + [libraries $zip_libraries_result, headers $zip_includes_result]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "$ZIP_INCLUDES" != "no" && test "$ZIP_LIBS" != "no"; then + ifelse([$1],,AC_DEFINE(HAVE_ZIP,1,[Define if you have libz libraries and header files.]),[$1]) + : +else + ZIP_LIBS="" + ZIP_INCLUDES="" + $2 +fi + +])dnl + +dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding +dnl +dnl FIND_TIFF[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] +dnl ------------------------------------------------ +dnl +dnl Find TIFF libraries and headers +dnl +dnl Put compile stuff in TIFF_INCLUDES +dnl Put link stuff in TIFF_LIBS +dnl +dnl Default ACTION-IF-FOUND defines HAVE_TIFF +dnl +AC_DEFUN([FIND_TIFF], [ +AC_REQUIRE([AC_PATH_XTRA]) + +TIFF_INCLUDES="" +TIFF_LIBS="" + +AC_ARG_WITH(tiff, +[ --without-tiff do not use libtiff]) +# Treat --without-tiff like --without-tiff-includes --without-tiff-libraries. +if test "$with_tiff" = "no"; then + TIFF_INCLUDES=no + TIFF_LIBS=no +fi + +AC_ARG_WITH(tiff-includes, +[ --with-tiff-includes=DIR TIFF include files are in DIR], +TIFF_INCLUDES="-I$withval") +AC_ARG_WITH(tiff-libraries, +[ --with-tiff-libraries=DIR TIFF libraries are in DIR], +TIFF_LIBS="-L$withval -ltiff") + +AC_MSG_CHECKING(for TIFF) + +# Look for tiff.h +if test "$TIFF_INCLUDES" = ""; then + # Check the standard search path + AC_TRY_COMPILE([#include ],[int a;],[ + TIFF_INCLUDES="" + ], [ + # tiff.h is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/include /usr/*/include \ + /usr/local/*/include "${prefix}"/include/* \ + /usr/include/* /usr/local/include/* \ + /opt/include /opt/*/include /*/include; do + if test -f "$dir/tiff.h"; then + TIFF_INCLUDES="-I$dir" + break + fi + done + + if test "$TIFF_INCLUDES" = ""; then + TIFF_INCLUDES=no + fi + ]) +fi + +# Now for the libraries +if test "$TIFF_LIBS" = ""; then + tiff_save_LIBS="$LIBS" + tiff_save_INCLUDES="$INCLUDES" + + LIBS="-ltiff -lm $LIBS" + INCLUDES="$TIFF_INCLUDES $INCLUDES" + + # Try the standard search path first + AC_TRY_LINK([#include ],[TIFFGetVersion();], [ + TIFF_LIBS="-ltiff" + ], [ + # libtiff is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/lib /usr/*/lib /usr/local/*/lib \ + "${prefix}"/lib/* /usr/lib/* \ + /usr/local/lib/* \ + /opt/lib /opt/*/lib /*/lib; do + if test -d "$dir" && test "`ls $dir/libtiff.* 2> /dev/null`" != ""; then + TIFF_LIBS="-L$dir -ltiff" + break + fi + done + + if test "$TIFF_LIBS" = ""; then + TIFF_LIBS=no + fi + ]) + + LIBS="$tiff_save_LIBS" + INCLUDES="$tiff_save_INCLUDES" +fi + +AC_SUBST(TIFF_LIBS) +AC_SUBST(TIFF_INCLUDES) + +# Print a helpful message +tiff_libraries_result="$TIFF_LIBS" +tiff_includes_result="$TIFF_INCLUDES" + +if test x"$tiff_libraries_result" = x""; then + tiff_libraries_result="in default path" +fi +if test x"$tiff_includes_result" = x""; then + tiff_includes_result="in default path" +fi + +if test "$tiff_libraries_result" = "no"; then + tiff_libraries_result="(none)" +fi +if test "$tiff_includes_result" = "no"; then + tiff_includes_result="(none)" +fi + +AC_MSG_RESULT( + [libraries $tiff_libraries_result, headers $tiff_includes_result]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "$TIFF_INCLUDES" != "no" && test "$TIFF_LIBS" != "no"; then + ifelse([$1],,AC_DEFINE(HAVE_TIFF,1,[Define if you have tiff libraries and header files.]),[$1]) + : +else + TIFF_INCLUDES="" + TIFF_LIBS="" + $2 +fi + +])dnl + +dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding +dnl +dnl FIND_JPEG[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] +dnl ------------------------------------------------ +dnl +dnl Find JPEG libraries and headers +dnl +dnl Put compile stuff in JPEG_INCLUDES +dnl Put link stuff in JPEG_LIBS +dnl +dnl Default ACTION-IF-FOUND defines HAVE_JPEG +dnl +AC_DEFUN([FIND_JPEG], [ +AC_REQUIRE([AC_PATH_XTRA]) + +JPEG_INCLUDES="" +JPEG_LIBS="" + +AC_ARG_WITH(jpeg, +[ --without-jpeg do not use libjpeg]) +# Treat --without-jpeg like --without-jpeg-includes --without-jpeg-libraries. +if test "$with_jpeg" = "no"; then + JPEG_INCLUDES=no + JPEG_LIBS=no +fi + +AC_ARG_WITH(jpeg-includes, +[ --with-jpeg-includes=DIR JPEG include files are in DIR], +JPEG_INCLUDES="-I$withval") +AC_ARG_WITH(jpeg-libraries, +[ --with-jpeg-libraries=DIR JPEG libraries are in DIR], +JPEG_LIBS="-L$withval -ljpeg") + +AC_MSG_CHECKING(for JPEG) + +# Look for jpeg.h +if test "$JPEG_INCLUDES" = ""; then + jpeg_save_LIBS="$LIBS" + + LIBS="-ljpeg $LIBS" + + # Check the standard search path + AC_TRY_COMPILE([ + #include + #include ],[int a],[ + JPEG_INCLUDES="" + ], [ + # jpeg.h is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/include \ + /usr/local/include \ + /usr/*/include \ + /usr/local/*/include /usr/*/include \ + "${prefix}"/include/* \ + /usr/include/* /usr/local/include/* \ + /opt/include /opt/*/include /*/include; do + if test -f "$dir/jpeglib.h"; then + JPEG_INCLUDES="-I$dir" + break + fi + done + + if test "$JPEG_INCLUDES" = ""; then + JPEG_INCLUDES=no + fi + ]) + + LIBS="$jpeg_save_LIBS" +fi + +# Now for the libraries +if test "$JPEG_LIBS" = ""; then + jpeg_save_LIBS="$LIBS" + jpeg_save_INCLUDES="$INCLUDES" + + LIBS="-ljpeg $LIBS" + INCLUDES="$JPEG_INCLUDES $INCLUDES" + + # Try the standard search path first + AC_TRY_LINK([ + #include + #include ],[jpeg_abort((void*)0)], [ + JPEG_LIBS="-ljpeg" + ], [ + # libjpeg is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/lib \ + /usr/local/lib \ + /usr/*/lib \ + "${prefix}"/lib/* /usr/lib/* \ + /usr/local/lib/* \ + /opt/lib /opt/*/lib /*/lib; do + if test -d "$dir" && test "`ls $dir/libjpeg.* 2> /dev/null`" != ""; then + JPEG_LIBS="-L$dir -ljpeg" + break + fi + done + + if test "$JPEG_LIBS" = ""; then + JPEG_LIBS=no + fi + ]) + + LIBS="$jpeg_save_LIBS" + INCLUDES="$jpeg_save_INCLUDES" +fi + +AC_SUBST(JPEG_LIBS) +AC_SUBST(JPEG_INCLUDES) + +# Print a helpful message +jpeg_libraries_result="$JPEG_LIBS" +jpeg_includes_result="$JPEG_INCLUDES" + +if test x"$jpeg_libraries_result" = x""; then + jpeg_libraries_result="in default path" +fi +if test x"$jpeg_includes_result" = x""; then + jpeg_includes_result="in default path" +fi + +if test "$jpeg_libraries_result" = "no"; then + jpeg_libraries_result="(none)" +fi +if test "$jpeg_includes_result" = "no"; then + jpeg_includes_result="(none)" +fi + +AC_MSG_RESULT( + [libraries $jpeg_libraries_result, headers $jpeg_includes_result]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "$JPEG_INCLUDES" != "no" && test "$JPEG_LIBS" != "no"; then + ifelse([$1],,AC_DEFINE(HAVE_JPEG,1,[Define if you have jpeg libraries and header files.]),[$1]) + : +else + JPEG_INCLUDES="" + JPEG_LIBS="" + $2 +fi + +])dnl + +dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding +dnl +dnl FIND_PNG[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]] +dnl ------------------------------------------------ +dnl +dnl Find PNG libraries and headers +dnl +dnl Put compile stuff in PNG_INCLUDES +dnl Put link stuff in PNG_LIBS +dnl +dnl Default ACTION-IF-FOUND defines HAVE_PNG +dnl +AC_DEFUN([FIND_PNG], [ +AC_REQUIRE([AC_PATH_XTRA]) + +PNG_INCLUDES="" +PNG_LIBS="" + +AC_ARG_WITH(png, +[ --without-png do not use libpng]) +# Treat --without-png like --without-png-includes --without-png-libraries. +if test "$with_png" = "no"; then + PNG_INCLUDES=no + PNG_LIBS=no +fi + +AC_ARG_WITH(png-includes, +[ --with-png-includes=DIR PNG include files are in DIR], +PNG_INCLUDES="-I$withval") +AC_ARG_WITH(png-libraries, +[ --with-png-libraries=DIR PNG libraries are in DIR], +PNG_LIBS="-L$withval -lpng") + +AC_MSG_CHECKING(for PNG) + +# Look for png.h +if test "$PNG_INCLUDES" = ""; then + png_save_LIBS="$LIBS" + + LIBS="-lpng $LIBS" + + # Check the standard search path + AC_TRY_COMPILE([ + #include ],[int a],[ + PNG_INCLUDES="" + ], [ + # png.h is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/include \ + /usr/local/include \ + /usr/*/include \ + /usr/local/*/include /usr/*/include \ + "${prefix}"/include/* \ + /usr/include/* /usr/local/include/* /*/include; do + if test -f "$dir/png.h"; then + PNG_INCLUDES="-I$dir" + break + fi + done + + if test "$PNG_INCLUDES" = ""; then + PNG_INCLUDES=no + fi + ]) + + LIBS="$png_save_LIBS" +fi + +# Now for the libraries +if test "$PNG_LIBS" = ""; then + png_save_LIBS="$LIBS" + png_save_INCLUDES="$INCLUDES" + + LIBS="-lpng $LIBS" + INCLUDES="$PNG_INCLUDES $INCLUDES" + + # Try the standard search path first + AC_TRY_LINK([ + #include ],[png_access_version_number()], [ + PNG_LIBS="-lpng" + ], [ + # libpng is not in the standard search path. + + # A whole bunch of guesses + for dir in \ + "${prefix}"/*/lib \ + /usr/local/lib \ + /usr/*/lib \ + "${prefix}"/lib/* /usr/lib/* \ + /usr/local/lib/* /*/lib; do + if test -d "$dir" && test "`ls $dir/libpng.* 2> /dev/null`" != ""; then + PNG_LIBS="-L$dir -lpng" + break + fi + done + + if test "$PNG_LIBS" = ""; then + PNG_LIBS=no + fi + ]) + + LIBS="$png_save_LIBS" + INCLUDES="$png_save_INCLUDES" +fi + +AC_SUBST(PNG_LIBS) +AC_SUBST(PNG_INCLUDES) + +# Print a helpful message +png_libraries_result="$PNG_LIBS" +png_includes_result="$PNG_INCLUDES" + +if test x"$png_libraries_result" = x""; then + png_libraries_result="in default path" +fi +if test x"$png_includes_result" = x""; then + png_includes_result="in default path" +fi + +if test "$png_libraries_result" = "no"; then + png_libraries_result="(none)" +fi +if test "$png_includes_result" = "no"; then + png_includes_result="(none)" +fi + +AC_MSG_RESULT( + [libraries $png_libraries_result, headers $png_includes_result]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "$PNG_INCLUDES" != "no" && test "$PNG_LIBS" != "no"; then + ifelse([$1],,AC_DEFINE(HAVE_PNG,1,[Define if you have png libraries and header files.]),[$1]) + : +else + PNG_INCLUDES="" + PNG_LIBS="" + $2 +fi + +])dnl + +dnl a macro to check for ability to create python extensions +dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) +dnl function also defines PYTHON_INCLUDES +AC_DEFUN([AM_CHECK_PYTHON_HEADERS], +[AC_REQUIRE([AM_PATH_PYTHON]) +AC_MSG_CHECKING(for headers required to compile python extensions) +dnl deduce PYTHON_INCLUDES +py_prefix=`$PYTHON -c "import sys; print sys.prefix"` +py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"` +PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" +if test "$py_prefix" != "$py_exec_prefix"; then + PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" +fi +AC_SUBST(PYTHON_INCLUDES) +dnl check if the headers exist: +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" +AC_TRY_CPP([#include ],dnl +[AC_MSG_RESULT(found) +$1],dnl +[AC_MSG_RESULT(not found) +$2]) +CPPFLAGS="$save_CPPFLAGS" +]) + diff --git a/benchmark/README b/benchmark/README new file mode 100644 index 00000000..788edeb1 --- /dev/null +++ b/benchmark/README @@ -0,0 +1,19 @@ +VIPS SMP benchmark +------------------ + +This is adapted from the system used to generate images for POD: + + http://cima.ng-london.org.uk/~john/POD + +Images from a 10k by 10k studio digital camera are colour processed, resized, +cropped and sharpened. + +This thing was originally processing images off a remote server over a 100mbit +network. No attempt was made to make it quick (there was no point): you +could make it a lot faster very easily. + +See + + http://www.vips.ecs.soton.ac.uk/index.php?title=Benchmarks + +for results. diff --git a/benchmark/benchmarkn.sh b/benchmark/benchmarkn.sh new file mode 100755 index 00000000..cb7aaf31 --- /dev/null +++ b/benchmark/benchmarkn.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +uname -a +gcc --version +vips --version + +# how large an image do you want to process? +# sample2.v is 290x442 pixels ... replicate this many times horizontally and +# vertically to get a highres image for the benchmark +tile=13 + +# how complex an operation do you want to run? +# this sets the number of copies of the benchmark we chain together: +# higher values run more slowly and are more likely to be CPU-bound +chain=1 + +echo building test image ... +echo "tile=$tile" +vips im_replicate sample2.v temp.v $tile $tile +if [ $? != 0 ]; then + echo "build of test image failed -- out of disc space?" + exit 1 +fi +echo -n "test image is" `header -f Xsize temp.v` +echo " by" `header -f Ysize temp.v` "pixels" + +echo "starting benchmark ..." +echo "chain=$chain" + +for cpus in 1 2 3 ; do + export IM_CONCURRENCY=$cpus + + echo IM_CONCURRENCY=$IM_CONCURRENCY + echo time -p vips im_benchmarkn temp.v temp2.v $chain + time -p vips im_benchmarkn temp.v temp2.v $chain + time -p vips im_benchmarkn temp.v temp2.v $chain + + if [ $? != 0 ]; then + echo "benchmark failed -- install problem?" + exit 1 + fi + + # find pixel average ... should be the same for all IM_CONCURRENCY settings + # or we have some kind of terrible bug + echo vips im_avg temp2.v + vips im_avg temp2.v +done diff --git a/benchmark/sample2.v b/benchmark/sample2.v new file mode 100644 index 0000000000000000000000000000000000000000..2f0749d087f5a00674c68c6481ac57977c94d3e5 GIT binary patch literal 512784 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þUe*3Zinslk;&roF4mg)5o2tN}uU5-Gfy-1caX$Wi zi#3mwrLu1ezQt`zbOC`2w1A+Glz>O+qJJh{M_<9SpnnXS2kxYzl`GySTAnTtgV0_F zKa;~;z^#fQ1AmK{e8KqPUaswXWy=NTq&=|>J>M?H|2e$dWa5}*c!#Ofz?sA5I~}e# zV5@5EhZsBcLgqH%^oe1LnFw+ z&@Y*j^A*$I`AX0~~T9@vMe`|0tf?{Y{_ubpa)#G87+A~$0aUSW%=)uHz^mmL&>pW-#m;N_Vt z`s6cIzoGcwn$z%cqVHE%B$!@1AezdV<7Uj20-Bl4IKNQwzq3d7n}bejNtPNgd7I)F z2)BF&J=fE@vTqcS8|@@^&fMmPNL1YhdxN3=?cioaroP| zI(;1UL-)*ax#@ShgkgJ_)m30~bt|w6KKt8U4-dArUD;-{zP{3>{y9<5F2+brFAdhp z`;5?6uZmD>-iXjDJ{{%$aAt)2!^L2bGeWKCJ5sH*Q-66nN?$sWn$|mT%6+-Mj)2#> z1`hd=M_gn32d&(b@X9{?=80WO@AJQYJV%JvKj;ZoB(v73roSuy zi{7&AaX!cJXsP<2SP3pfY;=v5TEf91W)0cTCduOeqC_D#!s+~NxXslV>yVbjx?Eui zf^|)z+_q$_CTb~)t|?g+9!qg+YoDlB!bWRKb)4X|;pzJBV8u3|pRRT3n_B(0x247x zhC14sV(r#x@pk*{IHB#CNU1Rker*A3{>3q3?In6#d&9?r9c5p0?#jT*JkHVctoy&w z9L)qrJ>sP5OtvUEx&Fj=XSmIXRmvY{YXY+pIiJsSd~z z#x3Ard9D!nDSe=u@W{ElpLww4C8y{Co3lqW+|CvK1w2iQ@b{`o*Swt>I-g(A!HD@n z&=_!NB;3qQ`ena^PZ7u$zurr|eJgW}FsNX=V4*%0*3K;YkTjit#Ugz` zr_b|qN7TSxdt_5|wj8G4D~B9IKeXeRKJ@((eb5$g$c~R#w8*d&Jix=ab~iJ$%h=B9mkL^Ymf zhGolHDd@xnG3Y<~0B6ISt!Ad(vAv?dl%aT!-=ul`xDD?_a447Ao8K}|cN6~FgR@-5 z<;>>(C)XLE-{SJhf}4IOM{teVDOd;Wc3S(>xA7GGwD-O?SLW{awjV#Vx4bq{wik|6 z+SlQUczU>2d2XauGjNnrmrPG&2)S9$2=_-$1Mr1+S@N z{kMZVW7q`pt03Z)Xt?G}JbskCXgR;{UGUQ?IYuWxeU;b)@1fEGY}cq&9fHG|OYG6< zan^$vD}()(fG4To(phlG$}Jl0vIWul2YtZ2jjW?TSx5AwSN_EH`uRw?`dozDqcq-^ zn*O66;Lzth{~NekGqt0tIH6`w z9C3oR*w~3feBP_@OOMCNRaat_nqI_2O>uJL=dohj^$}vnZ^H%0y7wGH*?U&k6GJ+j z=SJFufRT3Bnh3l7$S9|ENwnxJ9U(}L5u*M0drFHkO16(5A_$2Q4twA*M_cV+u`TIU zrSX0rrJ?ODx#`sV_O^s5d&kmfyR|La?mQ6fY&f1E)@?!KG@Lr$+jz7|^rrfBdKeHp zR&xB)!HG-oNEyTLR_cxDC=Kdz0+sTRli)L~AXrX!Cct^{}V_b~}KG(B}V*MTT z0qn1`fY+s; zeat|gQ-E#`KMLQ!GeqBr)sp9_g{qsxc6qVlagbi@{b{QA%ggnF12!@5wb1D|wMa6Y zqz~c^`cFS%meAjLb>tOE7V0yG8+cFLrAOmfkz&MKDDcM;&9CPXozaUPjkngyAx<0h|p*{igG74zK!zoiy7cCQvV93RydG&zIm#TZ6k#_%Khkh#TfmUa7d=IkBx z_pa7V7nf;axvdf`c;^(YIML` zRiGz|$aV$l<1(`LJj3D00BIYkxZ@wmpANG9%9a5Z6-O%- zrSvqo^SggU{B$VR{oZ0?sm>fvh5PyJ|Mz{BS>Z66!J=6Brdrm@$_Y~S^2uUdE*$gI z5$^ZD=6xUVe&`&_{^7Iz1in2+T<|B_oA*b%>hF+?1<{Z2Ep_G``a{nr*{ptJ+gwKz z?Cq80HD9sLH}l#HT-(QjedG9CyQq0@B`%yErPPj!6dRYlCo~@z=;*l6&*oh9uGP_f zfX${KXt%#R*lsTx?CAJrkh9G>SnS9fAX*FiyV{E1bGAjleM4RlgbMYOa}Nub;5i+5XK!p)EVHGd86k zgBWy3Bs?7FC5_|%tFuM`Zl2&cONm|Hq5ggV{XswQAs4OaP5yTlObWP0;U^5u~D zJYtnSa`;=Dl`a-E(Bekb?+^N=s&>eRarhIFCx#rP*7v^xC8$1M3ONhj6s%VQZmw1Q zsy9o9kJiau1~bcgH*+#l(Oi2`n>wDR8lEe{%Pe0rK7$`y!7jM^LN(-r5`FM5#6n9- z+{`zNb*2xPN1K?T3;cb#KA;4T{jmj-@5TL!uR?5eXp3ZgXT4;q+N_vVxc)DhiFkdt z7@9_2*q?au%N>g0+12#buUE|>>58SxX4#mZEd@>{mRpPG8@(BRS-WNbmzZlkzSHBL z+%t8R;uA1W=Wkkw*EMrAhTvoS+ZLVCKU*`D(d+v2MZqxYlx$p)D+dkPBl%?Q65XAv zb^e)4b%sdl`P9h_wY*R8dL__3L-M)3M(`b*e4{_WD{F6*QrC+fr9ODMevZf3 z4E*W?M#**mgNrF=jdpsKodY*k^Lu@P`}hNuW<`=SaJ}9O=A^?bKV)s7#i?8fHY{Rh z*iC->mx*KWM|;!?rYu4e^c`6Ah`cP9Yxo|v=U5MyquuZ4v8D3d&K^_?`)}boJ{s%` z=dpP9cbm0VG)1cUda_hakNp4jj90UtLwugDe728x&KF$Md-54K^Eo~twpoFP#$V`N zbcs@ZK%%Q9ZCsn9b!@AvG|AqUi*|V=@ACxprI*+@GZ`z;4U|>E{odj;E{N2X?&f@` zqr`^ihC15I2U~5A4Yb#YV9|`)%IFnXY0^+ zoNba`w5@5k=ohcV9g^uZoTj-I*)eoIKCZ|IrdKO zr!Ti%c<$$5& zd5T}Jh3m3JcjQT9)r;7CrIHgKRk@Yq!|#SG-9+U|!mJ{NLA} z&_bsl)52C#bC2Tlt!Iwo;v>2)oAcG6-@u-XEwcXynTqdQ=>DqF7@R0pLVstrwnR@D zGjzIa*(95pi{pQb{5S zT3{5O_pcq+o1Mja)8{36Q!GAbv%t15voz!0G`(N$YW&qUsR6slEi>0^CgMa>92(57 z^sK^(`wv6w>`rgmT(phbct1aCZF%dIptWh5<=fTF_Q0!Y5jDOAXiGzlL zNBGUx{e{2rVLZ-c`UIZiIt!OjX=OW3kH^>Wp)XOhy1{S0AF<8MDBYv~ah=X)ot)?T z+&5OPdV&9K<{I1!9O=b6m_-b=oZRdZx!q&rVKL-DCGgYT*lgg;l{ohYDd5&t9*5^C zAIG}5%swiJwGQzAv+Vl=*2ye5*iX5Ji##WqxanWs?-Y-9>K^l1qdVB(KPsZYl$(6U zDz4k^^gawqlT9ww9XnJG#3mKTfQ48w(fXWY~A0_ z)$+ywXWIvG(Kp|g8{7s-jVIrg>sR(wYQOC-Hatn6f zo1v^t$Z}+sk4^Z>5cL~FlZUE&@%8!2CL}hD61fL+z!`t5*+%Q`{=kzm$HBQl*{nX zr2km0fS;;;l;CPkjkP=9kFYtyW9{v)aDM^wHuvYJqsKG>yHcOt5*ecv{pN6Aa1v0vFw#XKRzdi zs~<~U@Hq;*ig!-MQO(kWUe{CfLWCXF!e^cA)b(j0|Iq8E&sO}$t=Igv=g6iz^rya* zFNRlV30?1Ql)65-MhR_U*3SzY6u$y`T4t0eUj5;Sx6xlU>9l6y^2ZeOrXm{^Llks$i6^!VAndUzy9C!ad;7qzJ8g`v_fCJN={6S({IjYenN7+L=SmhbLabt^fbwkXjKOrIik3 z8_RE*oQ&QudOpws--(gYIclZ7dHg+ecP`e^WoqNi)WkY*XIy8V!Eb*Xu~cua&5iKc zpOEJTfG^Y764?wqXCJ)yV4i2@{#4dUEZ8#wUFAyZJt_PhPrc|>a+J=sH;!w)8I4^6 zc~u8%vjsfb!eiHX9^SLniVK1x(r{LBfTc>*c4(HYDu8JNgL9>cw(+d2{ zrsE0u5Uo+j+9UdAlXHzjb5+AWneh}PX zXbgvy4qVj{Lz+$1nXIwqx(P+EV&U%|ix=Ey-{{bKVu3>-*!+-%oD3 z4Gv8Pe_GzwY7f0DHx3!#Y|R)fw9I%{R~tM^uJHq__kb-=Cn)88!HF5fUh~0{!K2lR ztEqDBgCwoA8l33Exrjuo`8TnT8D4q?K14Ukj}|2g4c*W}8L9upfW6=Gyw13B65mzl zIULJAPr$`{QyUCSlq>0DsY0J!b(>tR3)hAp!KH4T&(0bpe!gWz@Zw*>{p^i$HJ3&@ ztZ7j;+sEje(h_YQb@zisp!z7Hq3nJof-Z7BU`wP9C3HdWpAw93923nB)`P8BF`YlC1iipq)=R`G7Yigy3TyAk0W~y$`G_}mi>B*2 zVpuFaX!qzty>>te%tgC6gI@F(3ng;{um2HmjFrdq!NyZ=7X2x``2ad<8698=98o4Z zp(V_6#Fv&?i;U<-+o&;<`Zc^g*5bV=;5D5GSAG;;`ZjZ8b;MNvpaV)JPVu2n6_l@<&M+@170+$gan1BMy>}}Q zXG8MIh=P)DHCO#q`}Y;R(K(e6twuIrpt%z`AUgY0YlIzvltbKZLqkCVI~8 z`&Hu-<*!$_-OKjx$rM##2R0-cF{SbEz(Mh;CJ^WCb&zXCvhTlG-}InV-2rDl<$ehsKHXWv z-ACKovQpZe$CIrt_Y|A$^JKBnlm3oRS(E+IEc}Ij>NXf=CMLO#$3{1LzjQHTQ{-@0 z+r#&q*5$-Rrv|#(ZT+O?Y;b5X_4r!mi`~K-(wo~BX0NX7tJR$upfoIbPi*QnNNVMrNQn;7vPF-#1)%vaEhSEZ=ssnntpIJVQ|m7(A*I8?ox z`ux*y@I`ROFC+*}yOYo~;vq}UR|2iiTjD5{b2$SX`j2yYj6C^&oU3tg(9cm@ z>rEfX#dxVx8&OdT4w?9FW^#^ifWM*O&|@)D-4pOvx51%Ty zV|GY0xtode*y(Mzo7Z&CVmd>-{Kpu%+JC&X zo!k&T>BK=H`qTQ5NM<6x4G!(c-!Ow&7B}&5{G54Rz2K4#bFEIvr^l>wP6czwZnJ%e zUr1<+-LHcOT?ZhAh3RLrjg8#qYTj$l_Coig@e&lNfXVkF1C$+Ghht%*KciZ8)+r?4*by-d^}d88+r{*)=+xomQr_gosdI5!_)qqBXZCR;?BQLDaIvd)Bvv% z&Hw6d^hX)8&*XLJJMl;OVuujWuu}}k-7f|tW$FN$Qg4}6VCjQ=`1+(%$S0oaiRcDw)vRkIf6;HCT~>jU4! za_0DyQMai$2#)yjcL%xKll%{EwTk%2(BbY=?WsP7~8u8Z|j%5OC^;D!6O7^bI4$+At}b$F*nI(dzk2|-$)<) z25MVLXwBDgtWSXN?{XY}fQ7w?DVOni&%npb0DB%#n~H%${vledwSzm0lf?S_31a=T z#6n(Zh|qpktY*`*b>};Jn|;qF|9XR*xrO8S9K0wXXT1w2|7eU{wUxZ?CC)`5$EI?O z3;2I2*OX4pL^N}|cSVRztD^oNN#`9G#o2b@ow}gXm!*Ry#>B*!o>*g#jow5NdyAUb zV(*P2c0olzM2d(g7VL>J)kLG|CPt0MLT}5`+45ccet*nwDa-84FwdF0ockP8$AfXq zMn|`s^P}3#rU~tq*Jrkw9!)bfodgrN@PA*+|LhK5pW?dzP8>IY&p1lG+Av2Ewyd}4 z^?1JqpO?I{@oo;mSLPC4_NNME|Bh_g|64RP*1~f>ig#a+(~94#cvMUyciKsQ;djXD zQ48nhFZ^43GhaWD{N^lq-C625Z=Tfp{c=Y2d;2unu;9{Va*1usoK>FCdXx}j)E||7 z?=dI4h_!e2u;lA}O7=T;Uh-=pewefi4P|ENkCUrZ(L=L4fSw?866;PV`q$AbDP|_+ z6}&qBz*jNjtQ_#=DJ39<&+l|Z3K+s%>HTcg@6JiP7aYzg-A{=bw6{&Nc@-+}3h z*Lb|{`|h;&2}@IbZgL)jPcTo0W~3YE_B=)M>YJ$g+@cp{%vRlu_-b4wt{BUDMi1ZP zT$ZZ8jYj1r{No>hL9ZN?yuL#RDB+0gH2MsA>LuB|_Z7uG_=46g=!~87qCBl*39~TT zG}SX;FaE6P@@LUY>vI@B0=kgt%)NNyvF3m_ZVh<&AVYG$gRbrD0?Fej{l}Z=x#17w z_Y2R22L&9pTuninAJ~kRQw?CyL}ACT4iHfEv@k*`)6%Y+6weFs$bx~ zo&`7B=1sGneGLHH{*3Nab{#CS@_r#q`+ONORyG`?9mF0Vu#W!b8E)eN9b|1f@w30c zp}nlFEv&1H;LmE-P70U@23CzF$C}1E_?Z~!8tZrv9$a6eEfQ~wuh0d%X{$lg$1j6( z(}|y?B+%)2tv=B@4>9MAcfZ^8m+8!GCT5OUz*UjsNNIs%0C%dLK4D zF`meI6q37^*y3a6J7#mO4}f{hmX)LDQXWT6xRYzs5DmuR+4wZ9qhn=6hbf7;XiZ2* zM;rB`aLQb!IwE&C-F%f5dwlRl7p|2^`wGITEzPOAQDo)mDJ{*PCN;PLqZ!ShvcD3$!f zgZWi6vCDMk$%0`}=tNk(B&cVbFp3IfyGtd8hiR@c>PWG)jr}*I~ z>-Xs?`f>Q4PA-rG0@1pjh<0mGkzGL9DQ(wx&PiP_T@(V|yCC|Z#o-llO!3%g^L*rd zUV-oQ1b;?!V!g{s6yL$bVLhld-$qx}^QfXrf$x-hOmcbYgzQ>zT>Sisk(f( zO?I0=kN!V&bpx_w-Lqq=i_>|U3Fg`Sya=W&K$@myLOJ+@`u`;VJDw?eo;xaey>~?Q z($X8W&yu}VuyY4U;Ld(YOQsd$z*?2XP*95WAxAVic@9I~0L!ZN;UPYbgWQbDT&iCPb9T!e5gFNU9?%ylu_qo|Na)zyZuOYs_MgR6I z=8n)duCV1iW68%X^p{Gg|FrP6In3_a2h=>#!9g2Hp?{^Ik*rqk0at9Ajl?wN|3s+e zHUD3`DYey{lgR;9e&WD)2Y2b0*k(kK$9?3s2>qzY4sRl zY4IB@Hb(ZB8q&a_(7~ed=3uGe`$3Xn!XUZ+NASozOg4V_kz~Aq5A@AgO?~1_bNli6 zV*AipQmy|qv3dpb^-;_bH*vjggOSYT!7)_I@fNE{C%4UquMNfk{ z#_w&P@dvs~Z}2(@9CGJ(k0w|R+b4Fkv_*B8M@6)o&xW>lJP$Rs_Xsw%r^5d(oYdZ- zOz&u};kumTZ<@fNiG1ccbU`FI;FpL)ug7WXN~W8ecE)K;L)KYbe@-{MzEy11FFq%D z|4}S?_rpWFkhrD~H63CS&u!q*BlHCi!4V9hzdj6ppciW=gLOR>e~mkA&xm!dorZ&h z{&e3Gsq39{(hFam75$f^k$M{qqI>X?4>EV^Uu5rduR!a0pBegSW=!*nWbel%vR~H= zlHX@|J-$gz+z~GwJa~K_;Q5z&TK3ICzoYcL==T8Kq=)Ba|1f%Pm#Jaf;+QYd9qUQn z_~e+7@Y z8Ur$BWCJLezQs#L1P;|8{|(Q@r~G|W58^774uTWO?IXFh)z$ZGl#YV?77JEv=>-2 zo9!WA!@owK+V1HxddoBTyb;7cr^v(pqTd_~{(R0F^&u`Q3RlZsV9gu?=WH`*qy8VC zD&m@@;z6A5o0;_yRe0G8S>~nIpK(I7mim7q#98>eK^_G_J zHg+^UP3mafv!}Hj{g;iIpq*;dgSD=Y$K_vOCx6HL#8Td-XA5 zPG(=r?dgZU#Lsi7sXdO>)V(#Sy{!QZ>KTS6Rfws5U#PjmH`3G*KC!*+8gnxF=DeTbf#=U3D|s6Tr-5d(cruj%A*JN<-0)o(Mo(iL=# z0?}EUM(sJ1zWY;j4bfZk`zKf1H8M}@^ADWHYpnD4$RGaZ^V~|%cBMY_2D9;dvPGZi z@PCZdX|vGhnRs6I`xVRqtGdZ&Z%?!o= z9(m`8JXH^-xH}QMXu*|Ba1P&Cs_Jr?fnh$*@fJKIr-NF z(m$957qc}(bai2lIEI$-zztT?2rvVC#rQB z3EKMTM5W>7MAszv9FpsR$H|K9r=H#3E}x%yD>;<*b}H4b_d#5 z2L;+!716IBj31aEam#7$t7>8g_$3vI)TKvL8~P7C%HSD0lIQR^`ovA#@ARF}$E8{ot};{DO|%SJ{?6<1js<-{**cz+Z;#xu;q=8rsejiTX4ZV?P&3Fcli&mc#$2^0Z{fs{PbKA_?aQ4#~th)&&&{ z^4d7DZd8<}wiMqWe|XRB;FKqI^h|2=?%>ZmhB*)UE^JIsI@-@jpGq7Br(y%6Lucwz*q^clN+^} z#KW?F#zEQpCVH|9@sxBr56}6M=&rvY>i#^XI_Kuvb@5429Gtgk9d7KFyUfj#oaWFQ zbWaog)}Vp^PloL6mZf<3pg$BvESGW+&Dvzey>Y45EoHOZd1|KE>Ca3!OnVi5ZK~{X z0gmHaZ~=zXho71WSA37^Ge6$WJ(+o#A2zDa#Yw7@v|Z8d;M(89)Aj5j$$1cSc#p`# z>M|wwRm{#EI3oM}oh$1XXUneWs<<7>lH8Zz6ZC^LaVoPz>v(KN#iB_i4maec&CHMXJvec$QBDhnCQr`O051I z%;+2~R(?sYmO@N)3SPlCoSRLX4&GwrLUhskQ9o*>zMnzwtd!cE1crah|Hwp6_W?Lk zh6Z+b@IwGQ?8$8_z?H%D1rLwYG}u=D)V0 zhsGR(*SX`W+t4erPFJMrMRd4W_Sr|89#cAggswcfo7wO(VWQ~ydlq@I8;i5C9O z62&VM9J*rT1Z7B`mBdWJC9-$)RVhFyR=UAw?y)If>G=RZ+H`yvU!gALp2a+Tn(PlZ zz>m6v7 z>oc@zRL)9-F#*!UM8OTd5zY?AH6h=(M`eI_N6Sv zXBhhUOUcjn^O^6VU)xIUY$JX8&FBpHZBSiP_KThUi$&)nXsle_t$K`0QaxAWCH5P7 zB#-gL{X0YP%BOet(oR`#K-=Rvv%y99syJp!-lbrq3;41gkCjtdlIzIBlFQmm*)0yw z+?SYP`7&SCN276aKOe1aVzIS&0C>o^ch*~h)#%zb!{_Y{F!%fXdL zX6%K582$gLkmSN_e9+qwH zUbX3mlzqdxcm~!jroa3lYpaM{#15=E45sLL-wUj>*Eycctg#Pyy#>ZBX1`f%Ip7by zyXW=b**-MC+}O7te|wKw%TnI&%=15*zkhqOweFo+X2UOQ+S`|>h%LMKNVOxAn;PO1 zo13T1)znRnlxwb$FWLA!+nF-~XDg;so6RNvDGjq4J`d9vN`fuM;<1*d&xcu?R#G3* z4ilO-4Hg>5@U=QrXmS`PG!7gg8y!cBjf0t0yR(KEW~bHoVxpz#mrYXh(zWK6&P%P$ zz7ys05pad%iE3&3B)KY(T1y-po!2KQwFimc{$>6j{Hc3D{WycXxR7h`6L_*1obC-j zh`wO8KXDIU1m(Z5(Z8y+@yyRr-$Dzd!h`+U>U!;9n=i3e4|2Xw*@vxn*vhqy;Z@H@ zPqOj@&T%=M&Bsxe)-jRh)@#(OPexc;e}UT=0#~VPoTZ_iK4dpyphB*3De)U~!c`JD zi3wOYwU5!$G$-FDx_AFOrJjPgq{9qzN&iRO&Bb^A11?lzXZu;!@jrq5AbwakI7NW+U7jM;M9u3JCq)5n;# zU4ho(Gq{-a@Z86uwfG`+Ut2$K5iye^vl=^&;fqb|wEckSvudB{i8rA~{aMxR{$)iM zcR_KFJ%yg+X?(=a%R2hx?gd9>{V8~Ze=W1~&RA{lGiSf*d4)NUOK>3*i5WK&8y17h z4(L?g$`kbK3PpWiYByh_>HQU2B~|C?#TO~=z4j~a6}we^#9_t54NlcsX4=v-wfYPD z;9a5P>IeSpII7VvE|k26z=QY?oIJ*C+L$~=?{-9Xd+)H~`eT;j_9wipw>Ynh%(0w= zTQvkP5BZqZJvvWy--XY~FKL2LQhT=V;<2FahrL zbM#uv(3gQfEmywDJzGjlPc5=+A8Yu1?o&L^D+A!g^+NxdK6BYKv^O@g&ST)C(5J8b zh8gg>D5174T&!uQ=k}C%XaF_eiPTrntEgm7qK{*2Xc1P-8m5 zeu(3$xAWeUoWm#VI|hs$LLGb{^^G@Yw6``c?`ZSdW@#>8VX8Tt+|t-9zO`}ibfK~} z*sj#3fjgPL6Ll}WBh)hruDTL$@n*OyZvM3@}wzp z*`-g!+MaWT+PFBe`Y-aV^U+dWDCfH_jF=}%Q*R*-Nh8L2jq|)n>_R`X@)fYAH<%L+ z4#mI~eFE;daLuMrNB#f|=lWH@55Hn98q+s;&jo(p7Yy4&e0hWC$6aEX&cs^b#ILu( zH8Zi+iAZbX{ZMPm*JCVA6Uoblfkn|1EX@linHxVtHwE35Iun2AMLuoi8HKiPRT|fs zx>U74vDf=x(9H}TXY{Vi7Q4<)6JB^3E}?y*=-jbO?(#>x z>e#wlajC$gW)eMp*VC%krVDsQpH~8=o|XObnSr&EH+`Khcq+_wc;FvDC|}WaXU*x3 z$?pH+DKZb8>Bb_-=kig}HyaJcwfjZSgXlCaBj>V`FIv&a9*!Qr!)d#&pA~iT?*e!G zIr~D}$Lmk#v8oRVUhig!UCZ-j|2}w<{Yfpo3tIhy4+_4E_G-L)?Lp%@Q}9(vR9E_W zx>?kYn341O4lU@BC92z(=TzM+YDf-yMPH|lvTwgtcAkfrF{`1@b)C=kf^WSI{$&vH z-52P9T;}?%MBD8K{&4*diH?&B1?TfuGeQi5AT)=J_ zpGwJf4jL)vb0vLZk?dJZFRzUnRv})F$>3&Cj_UqZw&L28O#+8{P}BN}{=qD8uzQ}? zBR9v+{rOQl-Iy$`qko#De48p--rOy;q|zUYPZpc5CP@wRiHAHBgr-m8HEqtz<@#WH z*L_3mYn;Z~S8oootNfQ9eFS~5FG8uK;(Pu_s8VkSchSaU?1Sg{n|G;$$Zesh)kcPT&XGexYLJ71{2FjX#B!_+c2@|9`u>gF-OzJYb4 zfIIKwuXQa#fzvEkUX75dZ$*gJaqx29fzPrnTzL+krL-$}^e=1W3hQYpSh13=AMp^H zU8VQQPo`7zdBFZwQdjXs0KMgwtB@eoV% z(qY!-pyAf0;~!a@n@0$Zx0vI2X*lOTTT@fH$*nEV((UB;r zd_QeZ+ed9=zNq%@c*7c5A+@0lS<5W7mP>qgWj4@ zw>`>gTpwy_jS4Zh>Vr(JWfA6<3$fekv)fBkiG3L%Kjl2b_!u&g_pAvsIOC70u9l^sKidwPQ6IWSrFO~sQo3;f;4_VGU1Yh|*k z6PVBFhYlk?^!i_qt6l>56OF&p&V0Dz_%TigZ>&cZ{Ukh%&^OV~Cl3q*&o-h1vL{E? z7opAANUU@wLvi@+pe($+S8RQnEH%v7t~9_CGM-G78eWbU8y{{JTa;x|>(w~5>RakS zgMyXDe!*&ee`-SysZZYy#(ykCG9Cekj#Dc-K`fL=&-ffz!TnPK@2$KW{c%0H%m~&( zSMKBASj%bnYYe5AvxnTI6n^nd?!5+{dH>Eq>w(yNBF|-~FtKiav|w8?{*o<5 zyGIQy0vrmapY-trb7M=C89!E2(+TEZdyKX=?;9*Md44E06bup@3PxF5r!q@+Xt>z; z{YXt?#2BGL93@s(EVVQU8%(XYX0^BcH{RM(8YVR45Qq5N>SnCrU!w%W0(1c$b3NV! zcg98wby?x$UBqFv#9%9;HOBqp1jBXWkpsNvWzLPc*D8Dz%a;=8m1dJeC6Z_LriQO4?%YNU>KlchYpA*9 zg|Qv2`oNBM`$$vUo6H8UpW4yfg&14fJAQCb3f~?SJ^w^&XHcr>dz|{}KPwd1WvexAC$?+4tYOA|NwK6~ zb5Ziny-5G-qU1k_UfLhvP!Hx`yOBc`G86wzqU7@pT0uYLi2C*Ll71qOIebC#`hb|| z3En#Mj>ztV$)5tq`+mer@H`r>P1JtgJS}@|!smU>IXl0H#htyopXluIZ?@{y4^KqR zF46JSK}k<7#^xXEA6%>rn1UDB&uBGP(pSqlDtOH=5cNqJ8vUBnlI!&%#pR`R)qVdq z)%%Ans&^j#sULzBX&l!|u7eS8!@Kn3yX7j5?iXaY?$or(slk&Y`S~!Tdx&|}67*WN zX_DXJeX?)weX{-yX5uXe(DcBc^5I5B=f73<9*mAs6kO+bvt{>IbSE>=-@Sq^{}7_=~x@s$W9B>i1!u8c+$Rdj)vk9j&G&IN2xR7mP_4bw3}L-L!{g*Bi{M z)TK*Z9;HckU!+LpnaqwkCd&2HqYR6awDp<VIp*jT0M z`WV%46wYD;oS_2x_EL~imx~wt>!E7h&2Xg#y}k;WeB%N6)r)Me6G!B74}Hmcwyoh$ zz@dF?Uc?2d;7}eL{J_fVtV^4o@n!0(1@Nw&!bQW1a7)u=ypAi`&PR&1p5zkltmk8) zO4ZO1>cSynZ7Z?Sa@HJ}P}-I46>w-8`>=UV<7Z#F0j&6$eWSBgzL%QxGwRiGte02F z7j4IxOI)*%9{e+~;Q;uP!kSylIxYogrg2<0?+!Ra<-ydu^TE@vIFD4;cRp(sE_KCR zIMK{!+G?^D&8*QOtiM$gG{!$CSsH(sV{LYtC>q~}vk;3{KX^DwI2?V8jg*y#XzFr{wPi3 zg0UJyJJ;cxWmbd3LbLG}>$y0_(mEqds{Mv@{*`O;1bi)|22?-{G>+Wp3N^TP@>?tZ zirRDo`kiTR=^wDJgKAg+c;D6Ka))@6DOKmsiKidPELe1okf_CtJ_| zurJv3H9E5MCke(ErdvANCY#JlHh0+f$ZXeTA2aI>1(JTtZe};vs?KXxXtlp3OO7W} z1>HZps9jU5UQW+AsX%c@uS54M>u)W3%DtH}?VPB(PDY1o_TDyMaYs|P0QkJii_lx7 z=9~`?`Vw5Bh36#SLB)#qyTnt0)T+N^2Fw?~vM;7sk*|BS??9_XQ;+B`7x|-0K z4&F<>oH-6_vg~;ry|jfH}PN%+Nk&25umH2K?7N{-U0AD@}CWb4YT!azyTunXTzunJU^< z?~^qFd!;tl9inkN@z68kA^&8hUY97>o!u%n{k&XmJsqt!1TaItC{V5)5TqFH!42xi zoW?3RjPLWc2w!?OuLr`=_JBiO${P5WHCD&E)sq`sC3gRuy#4_B%|CFU?Rmy+ByWfz zu6Y3rSk3$4wp6?iM(N0}4zotzW#x>*#4JbsFs1&^FsZI_1pWPyg7I*eXpDm= zba}4DxMQBBar6Xh=Ger#KbigQ3&!0h_UaREY00KGlnfp%jx@Ed zu<13$nVUyUv^4LGHMeF`FPb<-V{jm@JWalv3{O4-jm9Iy)$56ClX#we$n)i&SjjMb znx*5l$)=7)OIx*DQ#zdgI%d(k6^NcQ4oG^pb*f|MC92(#HF76BJe+?@R`j1I$=(sf zCjRM?{upcNWSXo`q;HgwBKq7&Q1oAIRon}Yw)Z||x{OKqSH(@x9YlL}Q|J$){C zKb~JP=kYbXMJs-Ym(fW41P$t^DXPoTY(@9?8AUG@s~*gqdQHn$Ts`w7my#s-lnd?M zZZA>X{=;+d6Le#rqY=H8b^G)zdK~oj6VGY8{&HUHTL)+R68*6Cc-Q7qb6U;kmYi0* z?MAzZ88+Y6ovM2n8jP7sv~Ew(FuA);(tog9aebbox*Xi9x~O|3hdh?YH=VN#ii^*N1iOs&+uQ-f3tk@sO6Sb8)CC%^2 zN?Yg-xpDe7$vay-9-ja9@`wz;bm>drms$9>Pbehd5x8I4qgE#c&P^m6@s8Dxvq*_%s3SPxHYumvornaSX z%pKR!7+E*LYWy893^+lR%B{7H9rKzGc(Yhv!vxfSDRN4*#qDTqi%XWHZS6#bCKqjUxQ68MN#Iqqx8-8M4Ow}jug9!4yxExr`Gish1j{RYAH;yN?};nfY!7W9+RSnN2V1iS#pSy!m)#}BRm0A8iXrb4sg8Wa_}6HqX-%-)P#7fFtwz(;OdV(sSaco?N@HeY9ljgOBb3^& z@mydgtYRAX4q7#(aCIuap*GrcqF6m*vZm%`W}j^Jp~+}poS;tfDp+)!b^Ib*ALi+| zf(3AsE9&TfC9@{x67!4+w>FOnGqv|)b7T(SbeN^(w-9UdYhl*rSC~)V5Q?@cHKOsv zPmS@o2_Nl{VeAKk@9^L5$af7-;__O^q!c+Qj+Va-ja?mihcm6SENO zCz_j+sde-Nt9~R-=tXQ80gtB+44_w7DRK=0sPn!BK6l|7{RnQ8hg6ONKlIG(uOUC1 zjc)8S^o4fzm+Q;>$#wk)2?oPpq5keLseaE$xmFi!ZStCEYTqB%(eWtO)ZR>N(;E!* zCf8cbHN+dPY!25To_zDQ2)W`DynsBxltwrb#T@rv)NNix7od8)xy1lRz=x>J=RBCF zDZd5=AK{vv;Qh>}f&o%hB5_n1ULGHVJrmigxt4!Ww|YGR#5vlu z3jTp3FazI#@1sOG*&6c3_O@Q*I@;31JKBFiuWbf(>Y4~k^Gi{drc5yC02<#{&}r<> z@dScxGWW!1^i6HLBdd9S8TeTq7}S^j9*DCxcAsW$jhtm|&!5q*`FB&h;}?fby8hHn z#-T5f#9Zd%LmHh4UB=HdG)^xZ65Mmp*IL8ty$sRWgqPOq^y~{WB|qw;IxXHgh9v68 zOBMIu*2)gAGHZ|lW^6t#c_h%Q??o>P4xab4~u3tu=m?9beRP=xg$$-%s3=Ezr%UW*eSXHjs|2`Ji$De zn;p7K@$I!)^{d(}>yB*yAD`L%U%1wGCq?&1pIi0TbE3Nn#wM?oT(2!qouXH%4oZsX zoP!rhmw5ae!JrrS3vPC~vfm~=tUHct{R`2>?gqDLHMMHI9DUZ%m#l{u{snV3seJ82 zUFkQtM7QBkkKcm_NP^tyk%tWihmxpW-$e)Z2D4HHtfvRu`$yp`4kMOkeOEZK zmJRT5udv4c<^IRlzH)XXGqC6}_T~3BPggx#q%BSX6Z&z_#dCj8j$rO7OsJn6Y-!a5 zncE^l&25jv%x!6s}H5dbedzadAc%3 zQhpEakd3o9m~*t@#RW73ZSxP`vVLFVJgDDRpC3 zR(P_Oexm<_2M=)*TF}(xwZwpEsg;|^pYE`}ZQdFyBE|Ymp&H{C!^MUV21pHaJ`{~H zgEWn)^v&XJIv-;-#*A_1mS@x2I<`+~>sT4jnspC6T5r>mf)3Ld4TN( z$C$!rOeU^-PHskztokwQcxR%u0ZylFhNA2i=a)nb^%?(%FTvCq9M4_+N!H_2 z>%_iYz`0|5{RRIYdY(2dr}EGEf1=4$z8wtO&3T?9e_BmFa3FC@0x{nNVxsV9X6$24 zZ68l-Gwou=eq2bqX$&z@aD=sCU5M5AYow`Z+&D|q2ykc)9PaBeg266Eu7#6T$xID8 zYHGz8?w!M2)3xYdN?>RkUWY)SV!d_LbTvUlWaYRel$ zw>-2-&z%t5E?tmxdkYl(ZTyN}KBDpNc1rL*#Z3I2^J2iN63N$_{@9^n$*16$#OF6(#!uWN=)LjYD%iq$-61(z$wy|O+mi>UU^UpL-G^R%qU`kkR@wFCEt1D2^hjnE z;Sp4<_33@e&g&8NpkjEATZu8&;EOl|&3Zd@Z)c;~YzG%%>PgAH{~5`re}dLY+NwBa z#mf#$;*~A~l4Qq9^3Y=DTC35qAD<&R-(;3OIZfj>AHAP<*UKK=mZO=!%FbiNN;}t= zR;iAg)=Mr65@mfb+K^%Bsr+(5<8c|!fc0CYfJIAX{m})o^OtMo&PuA-<+n`H89Z_+ z*eyEzby#q%LIcTqQu{*k32iqW$7|DnjAYvg{=7jQE|X_M8MADw=#7ofRy-of;~e*h zt|OA zF|uI=y3%RzgKFU@e#2`ayhUQ98Y}tO6fgyijq?A96&kpYZJMj&`2Ga<{p+mR8{C&p z+*1p|fUmiynD4A42dcE`LfGcWs@RyZE)C#sy74SuMsNNXctQ_?tj4S$OWWYl=GIw( z=GMv(Ys+n7q0K=WV`hl8aUHo!A9#P6)HH1Vhi~E2NL{Mj4lL|S4t0@ND|JrhVk_wZ zSK9O+SCBXS%zCrMC>D;zfn!`i-~V6Mp3MiaKgYpLTInIy68++0(Sd!B94nkU%zNO)18Q%!ctl0-r#M2cL7$}3roH6Ku@!(p zMH4i2ePYdxH>S3?EQc5IGS{x2W6a?go598991nR_btpA34LCIbu5&&3^C5L5H=ZkK zI99?fr&gwYZkypdz-_ zgD&g_a_xD+Lf!o_mb%Lm&Bp8rrslWDnHx<}*7_FmG6ysoov4F5QDfS~|JByd42~7+ zbQ3kkJ$Nye!7b>{@0(_7nwETQF^~F0P%AcAyL`A?&>g4$okx9QEeUph8+t^} z-y9H}dS?qxZFsu&VjivVoaDEXIBI={sB=n|owAc8hhB-2!~Wfh|PE3Xn(rs-T-&N@3iD^-E8NmZq#;qZJkzIyg}dfn9{g3Mvm!n`%Uvfr2G`wf6 zQaoDFhTKjMeX7KSagE|LcLCRAxyJ2sg4OZKevRWZ{3*XcBl7Pw!RcEt|KSP6 zUm$l}U#NW{t3Vs@alY2Oj5*j1@UWjGYoo&-zH5%lHn1j`03 zxmlmFO2d}1a>GinNJ7&iCtR+1K&*jIShC)TKud)bp3C>k73W5F-D+7&~1)_zWnVl4f!h(JrzU+Bnx8f5N>2sU+8 zhgn)jpkK2qL}PqE$l9D4WNDE?H4TS|ogRZH7pYgnXRKhxwH(dYvV%5%A+TmQYm(Te zY7fVBm*@2fFvh04cp6OVM9edgbyx&0RkN1x;VOFej`O>{#6-_nyXix%#uI~uhP%TwO+$v6TEG9u+Vu5E zv2HZ;Uw=(*ZytmO^^UmqmUUBG+ao5mw`5GPHl)Ifw)qK^aa<#b%Svo@8REb;*6m#K zsuJ?dKICp!CR!Tqk~=2g9r-KQ`ziSw9s`vM*kR*Pea}96@b$EDV*PdYV+7yA$;EuZ zgBjpC9w}wNbL@ENmM#K+mfQ5f$z9RTuQ*6=eKnsqp7&F8su@lH?@pw->C!Ay%SY2Y z+8xpQ=#Q56q%qd|^SSpv5iO?qcM>|Ot$Uwj5l zptIDNyB*Vdl`_|no2z(>CsfaE@Qc&YE8B;L%2hfFXy~7rs5Dy!f1%xcBL?*=I{01^7Dl&5`^^FvDB9v6Iug@ty7Cc4+Nl_sX3w z(({T*k#&3Vg4VB6UE($?u76YO8gyK8NXZqPPN03bgsl1l%B_q!yY>#G&dqM8hT^i^9`!y~C9`xGv_zngBp-9$yjD?%EVFAG zv`K6D1PnUAU9A_lsr4!73+2LFoDE;-=1QskWE4Fz;-1&WNXDLlV)F|jQlmTZP$j+j zUzw+WIZ&XumOs;*AdpL(Ypo*Fay>qyHYS|dpaVK7xnJ)j0ducoxK`(#}BZz<6 zxmTBiIU`v255cXek;=1G_-&B)pg}EHjbWV}pl9UDy}LQo+Snz~rpaSz31TL4Q=p}> zAy_c<3la@G12s+NKuvR2kYM~fM5?O>i~3Mgf}dSQoK-%PI!`R|&>zHDEnv`}9EStg zaD`)<#QOOQjIsH9!09f(%lh*HlkDMIqI+7ggY{QTJmbMWT#2Uy8x|2m&E$Q%;rYy@ z$CL{PhuMW{FF3=)=?TpNfBsjsO4R|g8l`6EQbs9>vMah%B* zIJuSDbgL;ay1ng>SQ9;_*4DZhi(wYmFN@Czr=C277-j%5(D&4YTG(#@*N47Y&DY@3 zU%cjkEmg!k+j)NgxtoDwx=1W~AKdB%pZXYmKHGavfN%5Zx7qsnfAGJw=XrrITRDF8 zr2=v1*IdV1V%l}w1LS>G56Q#RxE812T)z`8)aoKE#-~xHmRDw)TkL0=J0|g5`F*6N zX$oA;y`k-`L#DO2jlg5X1HYpu)R8n?r#s}u&*+_6!Mi}PZ6()=8d3EWa8c(Y=CuvD2|*lH-6vRp(ZqxV9e@ z9m6+BZdFUQ9>H5w-NZD}sp*L5e2-kpOl=BYrTgFTHeEkc-R~{Xx>}cLonKxjdwsH7 z)8oZdP1g^$3%*mfiu&I-ORhZ8&lUTL8Ekx;AJ8`I8%I9CTx&9G-=4LAwPOn{JKY`#-0r9}BNU3%*b)Ut-)`pqnW_y|a9EG0fD`;X24HWAv zL1KMtplEm_NNlJ9lPW{R`b*>>GgwnLK0tSB;5WH{(ex-^&6+`1y0VgIcmfz>s|o+X zhVCN$a&77N9PcyYnNaX_I{8};dh)E>+V12=zq2p;W)<&&Rd2ENXC3?*XEA2PS{fRd zGnoy~4~=JLL8NL6>)gi2zQwuY&r$jn=eh|FfNidp&o6zC*TI}GHMIX}jKDEwCQGiw zr;*%G`#g05+$X-j|Gz$*R9zS;*2!Z@UoaE;>9n?%y;E8` zqGDUy=8x-WT0OzklsUD%Ib^)0;Z%&K{xI=GJ~JG@#Y&ZtJR=5hT=rodNfzg1B`#V*-Q0!qy+aO4?pp;Ptg@V#axJwj zv}emk5DSeY7t7|{-e-gFSUHUAQWK+;^#o5&z~9yr3%wa>YN?8{G~bCbnSTj2nH~k1 zTYQ7eO;3VNP2Ho+EexO&YTU}HzYa(hU0%r)U8j)GR3xdo zKFO*}U;5MwQ$_F2DWd;>yQP3Px68iIQv@GVuITg5amD*SGYiw0k1b@*>N(obpU@A( zW5V@XyyD)GCb@1ut-2g2(Yn8T1`UjJqQ9kB@!M7;`M-syCm33NvG(KFjh?`#fQ z**BSO4bN5m_c9Cj3GrJmaA*~=SzmPM`{#?Esmx+*g?q4#c~(=g=(6Fw=(_Q|?CL`< zRCYpe`uU*6&6*(k?S)5lZJ(^ONAo8$TXKFcTXw0yJFRZLqDx(+y5+1_oZ2@^U0RZ@ z`n7wlFZ@AFcjSOZe=gJNJm;{+`C7K=?ThOEV6J+p-kS4XKvmte7RVX)8~5F)lDqNVs7a~u8=lv_JwZ*K>81xat zS*ITo|Deak{FuyoP)oDOQF6%*s^Pv2fZKAO8LK&P7<L( zaY0j1spt>>EpEEG{hz5O)As3YLhcfivN6fpY49G63%;OkM_6w?;L`lFS8^MhEW5TRtM*?V zk~+hwb6=Dy`5xXS`A^;<`32#bF(Zq*5xx98>Lh>ckaV|LM+!d5`%^{d5c~&An7P;u zH)#dFq+b`=d8MDx`n-Bx?izy@s0`%=qX#M^>W#xAsmc2;sZct+!zl4o@b z$gp}A!x6$4O_xqA^hTcS_*0(R<>+p$!@Tuc=Y)9l_xEX-a~JIIq{`09LDBa*KHHz< z3hvRTG_HAP1lP6sGFn08V=0nLFf+qzlLY6#;w9$^VDeYTweIo7T8|&l2)8o-I}mLo z6|C*b+}dUGVfvJMGde>d2Sk^F+hylln`PzAc%}6(bj_ZjD|9DGG1w6o-Ahm!R&LSO zcVDX-UYo7fO5v*UjnPWu-9WKPVfz)&!+>C^ej$DOz2stV2dcG4s0%Hn{@IIJYyViA zE~vKjE$+o>tnty@Gj7aqzK&+td#t@8)_V>=v*jmY#PBRbD zsiW_GoE~jRDE?t^rgMmgwuE=I8p7M#P2uL2gb=~7K2U53rDkqFMy|gfB-Iy#M^A%f z{D_pQo;=s@5sz7@Q`>x#Q~17|dl%orvLmef8+_J4Jglk5mhWP{xN{sgh=-`pl>JQp z^g0-11Q*YNt;}6iF<)K@kGNtSYv~JKUngex8q92A9R#ud@M)}C$@VgJsW#j1S(o(W zswRSKLy3R>im-bU4NsD~$+LH(IzRm_%KnKBp9Yb?42Exc7#*bd;WkX=I9Bqr6)|>C z!L26;Sbs)hkPql3^%-kwc@_MbjaL56fuf-no(XZ+mj{Y{WU~05b^Z7(mhxo<+bp1rw8R#Rgf1O5b1bNrrpp-KF1CAIXYGcAn+ zW?5RhPPVkY9cSr~=9xR3lg)^`SX?fld(&&b=(29F+9efk-?+nq%XRp>XYwTbzjGC* zI{XE{*(drJp)FmVCh1Mokk;6=DbTQJrw>0nQ}lRBe`kBT=AMv7O(jVmsgBQ;P3E>Lf#n1D2xyjEB6ZH=G3SgeG)SfzW5Y|@ulWZhU3k9W@JFuXHKsuFTtYvvGg^zCIMzY=veR#1(w=<5eF1vUujGJL zd7{UfLdk7Tfz;)}A+a;H7-w`}U318tW}Mf!94XN_4nHlqI3AOIuN+hSMiJX&m56%n z8T@;4WVd(I6r0y8K4hZK7pt#oOavC#t3yFuu;*s(MUCIFR z$Jyvt56Ms**YDRlJW5nM1#VKzrQ75tV=}d%ZED@8NlJYO8jJJUmM2KYQLCl8_hQs) zuRx`-N08L~Nr=$0I7n=42$2m^h+Mx9jeC6S>*fY2HKp|F@t>%6p{8Nu+cPg#j+a9P zx;*7J&Ei4CM;^og=(U!?YpzHnHW|qJU&y+*&0-%wJ35Tqe>wPbk>8ov9-%vZiy6?r zn5BOyT&y;QSnHERJ6g*lI@)tWElmN`g|-p{?I9O?D@d-NHHNwVK)J48uv{A-CRYy) zlPhgJ?k}k6d`b-RDOlvn_XBKoD2`(rYlL1@CG(dRHvNw|9BWToEt#JWV}lb^wUU@< zH?czzn6w8>vhkdw*j5uuN$g`dUpKI3w=sYAIq!4eeGm8<{j%~R)`zWrlFj;dB+lB% zoLe(-;3BZ57C!+ziJq?GXUyi8!h3w4fSzg%#|f|dS?>t@C(Q9bON+64HiUJ&oc!r2 zIz=^OOl{r9TH40pAGD~yQWy1+&|oBo`3M{ejO%D}8*ecliMAMx;Kz>5A-HJn;7={qoMF?FDY(Uyi6n7?=A z^KJe8;pl<<&78^C#6$tqjsBp=d5iN$kAU1&t}F&;TFLj|^Hh-oSBzmB%JKIjFJl(1 zf?8Fz8BEQMu^P(8o0^-V&8?bnQ)_>4s7r{cMGP`EwV`pcI@H``jW9Q#jIlIzrzU=e zyzCywp3gmF9y}F)QD2%UX#7Z?Mrdf<9s5_mRY3lfElBMnI`EBNn zJ59>oBNm5wM+LV93*LUDN>%bug*t8QK2A0uh(#k zo~UdBy&NCXySh6L-3okYV63Czl^A<7emsr2F+%O@kF|EKYO6VMcm;`^l0XLXgwCV;2;@Vj&9gVONU=r%{H z{CAXG%J(n&8s0M=&XWi3e!%gV11`-0lVo_wFYHSlxCK5J@5X2H3AMt-_(IMGpWtG} zEnMrzpu@h5eslU@hwUeDsJ6FgxzSsx`Wg5AKMPFw9*yH--rohDUEqDd?UEn) zjW_c8SE=&{!-ejn3z|II*0^$ny)Ji%!`eLp-?k`Q!?H+QV-%QF9cyoN;+a1_+1`>e z+R<GlhuZSZES%oKyx?+}b1ZIFWJZ;--|Zoyk)jbvQ0LJ8c0@9L}M zgx@kpT8#fUd>YsJ;-ZY)1|QE@QcZZ#L!E7 zq~Jey$cD@GcfEa8_S==O1a#l3gpFL{5!`-}F6g~=QplBbP0)X`JH*0G)fJ2$7+A3{|S{=7RsoA(nsWs1+>f_MFSO=hOiIA+f=^=|` zop+{xY$tv77W}5C^M5hhA^OZ-i&N44(>o6a4fr1i!T%5r)QUVuCvKU+j2{;97>^~h zp&2T!g!{baj$!CY-NSmBz&4H8X%#pmb2|_X(i*h4vBVXtV#$|AYiepEoYnv^=$i;v zLra8MyC71oT^*@d`ccaZgA4sPR;o$@hw91cy1gqF(}&p;JfqL4RDoYzVKX?E5Le?F8 z3Li*a@m>DT<-53hxA2-RTmok4d4CIWsA-B+ahIIflLXzfCB$GSsUbDlrxm z6n-MZQ*Q8g9$N#vZ3eiZuwO6Ir`yP9&x0Sf;q$?jQv9gP=7BAT+1I7;DZJ6h*H8-u zd&%X2t#WxC^)5y9q(v-brSjSSvK{RX?Atn8jaVD?E_NS6Jd{U1)foN3i0?qthNC zt$nCZ8OlL0{Mj&%~e8Rnq}tj}f~hzkORJGq$vX{PulRd{MPJcJ`sNYIyo~iflKzWrTV#XR zVac?YSganej0pU-&BTS_2gKlzeX`-jy>j3)eA~V`qWBNng6GC+UGR}rI`hBuojKAq z`Z334!&P*Izv2-|92!z^Pzrs#Su&-ql!7{}R}A<^8vJurL+kTeQ`=mR&@a(#e?f2R zQ}R)LvXoYvs4IRzAL}vnkA>R=V}J6@1JE1XAve7qo#I092G7N?Uk{35qtfvJcVibg zR86mc95Xwt%%2{4L@>17DEXb5uN7`C^{DHE&vYz2s2lbAw^Fs$0`theTQ6E2D>O~j zGejHx3w2=;a?O`$ggU^3@HngZ798qKPyIA%^%Ll;zl)YS8qaC}p*ms@ZOJJ(kDYa# z! zI`YhKqOJLg{6yDyXPuo|nJvOuw=de!&@D=1Jrp5X5~F0xKZBK;=XkDNVIPyDr0RXq z^g0YBH-dLX1n2J|Vyk^%8~xtofK>F7a=Cj(Y}g34QA#PZUrTG5@wgT|LH|&XV~0&ABCLz3|`v`PIQR-eumFgu;;7xF*7teixz zdvg%J^b;`^dP{OM?z!6NzUWi^lUQ{~oJYa9c#ongu0!;jRX*i4cnp>81Bd<#N9hP3 zdc=EQ;(lg;RKAQhd|;xpu|1eMG0s^#CeCS{5bLlmpjP&4l&$H7=$7W$#NVxwZ1#T1 zE%rS6&Ay>F-=BTm%dvKQ&0XU+sD!tAl9z5XN>e*`v`~YORmFYy(POyd>Cvt_`&efq ze8{HEa0p{(3F7s+iaL6u>T`m8~89Uz^CWoS#8M6aH0;Z)6Hi*Le1dNfUAo6XFU1eU~LOK zl@PA$rtv$KfUoI)51Wrt|)nI?v`Sw+0{i5!@}R0{B1D;ksyQXrZ|!@xC4Ao(HV z;?;^_(Js;V!A`-i-A*BFIJxo*^x{8HQ;i?4)tdIKRl*GTqOE2o<4inyj^`+P1N|zC z_lc&_czWEzud)9o)o8)1e#ds%6w18A(tY@s;K7=6MDj~Kt>{xPqq}BXMqK8DzjQqw z1NQBb;n-%`FpZp8KjvtRWghj`lREQpyhWsZDX`UP$!JWM!Vjm&Z4GKR@8v|!w#{DnRL%EpzaRzxDAL{Ih;Ki%d-UpHw`InxC!%;%b z4^hs#8=S9iM+voQeD*f-LMf4QO-&>-1^N4ZaOgx7Gr`D@JRjk#Pp2Q?A+?{!Xam5Q zV&<^Z=c<;r0|Soxf|HP~1r>Q?%xz55D3V#6y=))?Yrd6PcSD8yhKP%YDjKE@d z9~Qk$W!u1%*H{zHU`QwUL05E%rEV@meX9%K;S^ga-@7m0e?X$=lj=mzr=f5q58|^I z;6^`@ufD=;5|MZ{fYZFm(jT|MFB~EnJJRRn$kB+g~ z+s4}LztY!o?*pebiCpfa;kL%M_!E_*J?>6!XjYP|)(Gc#OkDaGnxIqQ$t8L!zGLmi z@pl-QaFsj`{j+Xe0rMW+-jiZQT9QyPAI|atxaICieVNT2>+C_p6w3Xd(?@?BY<$kX zouf{5jl5F#k?crP8`VqU{1Q)LpBYqYy)JDWR znvc*!)-ldm*NdF=sz^s&?&OCfd%o=dtdVFsMoZPHXwRdQ@tcJwO&_VL{SklJ<>PI(3sW7A`?E9}=4Z>x zm=AxOd~WzrDQG_HtH))@@Zm*C@3<)InRgb@NDk(cd?k1*eVYEvRrJS8@Fq2?EP5(d zomE0RotML^nQL~F9P-MO9-&ovx{%vw4AY{lo~ zw;$ktc97ioN&4`|os$9=oD_}b?NYdPk=knhIw|y>Jwi|db^M%tXreMiKL}9JjOm|N`{sAy$0=)JfEgYuCJ-f?@y8H+NMa> zmp3YPe~=fNx=yW8mq|6%lVz(V5ie@uncmE&-%j6Z4)w56V9-F;x|VhSh;<(V2ZB}L1fjMn+0ne7x)C19<#)&todFxKfIanmmIwW>3Ggoq z*z*#-`6c91lBrj}#=cQwrjJaE2gv{OIfdlZi@s(3x%;PP5~qMaB|m|&zk_SktntYv zH_dm6hHC`ErDlRpHEh)63#!00Vzq*!;LddZ--92|$IJ+c;{G?#P_^eZ%)>7EI8iLU znV=Nj;d$>mPm}|?j`#aJ>z`--^Nyg zf8|2zW?spfsz7>j;WTg`xh#b5mBQNhgY(cQQ+AaYn&VX*%rU$RuJr}4(0i9uaUa*9 zGJempYA~leI5d{qK={h=5mMDO-uoh4iadFF2AYQ-hidAE#n_r}puKoM+F3g@#%Z0% z_2oslZUS8QcYfc`qn!0C@FKdI++qvH2V@8Dk-=xp;P@`$w`uD>hxqOO0*|I}oD}i_ zXVHUAB|jMo7ETzZR4F5cy00eLn_rx2x0ik75ZcXi==A7wPSdCHEj6X{%pB`@Nesxo zB>A7YEcrj6HdKCA3H&Qx3HpdW`flW)JAgTJ(C!Rk8&6-wyUY%H6Th&T{I8tWqE*y| z&c3La8m`Gfk8jAPR@Y?HHoQI(sTsY2#%BYu-5&Bnd4~l<>m5SKD=A{=;}lI$o2?rC z9C*cK^3(|%6w|*cQqba6a!B4rvGsa9+72HPf_^zEg|;yBY}HlC7;;|rJ(Dl{_RNz5 zCeTmv0kyl^+vGrRd>Z$p$N`Oe_NxNm!>I3%@N5EOFsN- z{Lz~5yPty>zYjj#tumDW`egzJ?U#LrrO9&tZL+;xDmo#0>0hRgH3_dp72o@pS1Ojw zxngzCv0}sb!vyPltj!J4YH2lntNV$+vcREr)XMZ=(5^vhMKhYAws53}_`XgDLq>BB z%m#-V=(WzkFLo@D#=mr)YfKx5!TJ1kvTXvUyW4=dOEOAOISV6o}skn%b162FEg6qm-<%b{^<#>_Kncy#6gsYm!>*OGY=@@h4N|% z=(ltG7z~6%y-xkAC%3=x{+q$SO7@{A9#Q-8fh~z~SZ`As+Zp4iUxIc>_ol}Zln!aNsj0wUiKfJR$KMR(}pcPqYEx!4*W)N z$iiBm`l%cgd`&UmKcfp-hX0^@UXe(R>Cth?)GJ*GySzgR@3LJq?>e9fxN$@@d{14y zavSjr^Fu~$5n8{oO%s}zD(ek+ZWQ2I-{HJ$D!M5K-nuU7Yp+WFSFVZy-A>EKw)D56 z0W@viCI#)=DI0g~l>=0G-s|w2cX!C9Uc^_))c&LJnVbqwoA{X&^ul#1IN*|G{_KJh z@HkiNKNes6C)?GqV|Xrh&!y&;Ck0J8C7at*>&n2>Wcm@g&8-aP&Em;@2>tjUYt^8P zrK+i4ni#$=TNBAxh-PCF7&44z9;gJr`=VI)JTj%`bNJ5#@-gqRrNfJ+!HaV7J3ADoEpei8J<56cF}mFC)Kq;~ zugn@NEa2MzG4Ti*qGIx|#l#~;1G&A3?)p#q26})yk+Hf`Ay!wyT%OWFod4xK_TvzD z->X=?b11z+_++KTIhwWf*N+2-$_F_c<_;3@f0QhD+49&Ag>5JNNE)Z^2@A#kVIADnxi zYd5n~i=Lq$>l=mwv67#w<=Uh*4rY<0Hv z&gv-#CCeZCG!3sTaMX8Isz6az;V%lmbiIOYJymv!((r8PJEBg zRA+i$oaC5#qaQdF?`)VE8iaKq<;EnH=>Ju&8IWqkb`b>05M; z=ZN3BgPHGeOds)k-Q~FUYI7&5VwVvGVQYf9j`8;W7dbvd+i zK6A>5sYX#p8iAMLZhFD`o{+c4?KHH`ZI*};{>^rR(-?^X$8ZWDX-=aV2eoSd&CGQ-bt(sQs#}jgk+IkE6 zrmZ&xL-RdB??ZlU0Qi}jt%Mh4$zcoeC3?6|HVxV(nQiDk$Kd%miX3#!F{!O4L(|&2 z*JYlwUh-YGMDhG=p6d743Q14i-MEu}+NC>H|840~fDxU*DY(&Z8EB$@A_S zrDgYerS=9~=-DRO^2eMXVGCH|j?p^%MTT&SSq3m;E(VsG)z}dT$7`1j!LK zM7yk==&etS5G*~}2OIrv6RFuv8mv~dMaQ@>TC96U%{M>E-c&o%*7Q=6%bHAo(of_I z5=P=}jjwS4c=r~%r0wieGCA4zxt6>AIj)i8@PeChUYGpNda`o9Z;AJOwwZiRC2Qar z*Z5c6x)uJ$kF@v%`J!IjzMm);FNV{&{fHK_9`=!2ZszZe{C#pbKHI#H28{UzOv8uv zIoI)mwP4X#aUM^#U{e#%y~&K~{z;zCvxj*U@cBg_CTq&)QTKU1$8Pz0t6cN;R=M_J zn#1wI7Q3Tlgsu6#0e0Jv{`TeriFSKCVwVq+8*T3;H#glTPo0*ishor!Xe&Cy7vLeS zS;ITX71hCmCXk=0V*c1RFapp2iYMgY&X8mI5Da2IQbm7a>R>p}ZSr?xz@c^E&a1>^ zRp45CUK7saXhTc-fkUs+*OATpUIy0=z=?LTjR&jUe)@mIeb7Nxz5v&nHAYi2kU5HP zfLnvnXa9xXX#+acU0@L2|8@UHI_lr04hq+8r~!+nqHA2leO2JmJp72f98GMc;LGtkIg&eh zo5)QK}%D8N*nTkey<(pRrBO)N>Hm?icxn{G2FbW80TM9%(c|;|3$|*o?2Ij z9sl#a4(?6P$umz4>vKSC8?{Rauh}mII@2|#mofzNO6Dxn+uk~4mlS*)eRdx(<-su} z5dV7rfj1=m&8w0j?}8LCKVQ&S;A!2TSs%B45kt-iZQz=dx`6)7s9wvQv^?gt&COB56Vs*E`*w-p!_gkbeWB48KNJFf zy($MUJf?>2$J^+8_uOsf95$gRABs-$&><>aPFr)q`TqBF?#ur^6ch3_l=$;*P_ffi*?&jbw5}-wgFAVzzVXCwL4U;vAaC zxzwF_=o_@ob?9~yqICryvHiyR-GRLETIy$=;YovH$*H2_JqVAV3b!(^u7%oKIUYi9WRgw37vsITk#ZeiB__D!RpDp6BlC-2~qu z$5D6*917#wJ`!)F_Fz#89O)k(8%iJ8iV4o@&NJ=R%TmPJJL!r&aET~y+tTFo$EX&^ z>jUksUV|MiQTWPVA7*PBHnOF09lc~e%)n*#WSM7zQ2q)0BNq&bf%~{ML`CR@dK1HZ z308bX%?yrLN~~2zY*p^=o<{zRab4;eg=Z znYn&n91?@?&|6h?TJoB6LGsN!tMwUvO3|MnW@F}kkRw~xe|Ag`7>tKt5*Re(w1*L` zkhzpR(bCgOo9FPM(dV_sjB9E@>R7ZS=c^)($r}*e#gQGveVWw|Xm0Y15h6^pD72^~96yB3hq4XJq|b#A27h zprLt+U(89-kA5}7>K#&W7`6IJa!^l>sjY{A9k1f8Va6+9C9`{)_AygPQp`r#0k-`{jz9CBEQ9a*bul<$U5eY;NcdWpCy|= zV|H|AmKxMOTMfK>S_x`{Pf8NoH%G;g8T$p(lg(m4+G5#v;$p>X_HxzZ%6!H5)0MJM z3o{TE;;}-!PbQug!`d(pEn<(xt8k;}D%dDBRN?b_k2&=LQDO@BN3qLo9zAvh27HA)szC(C2qen5>ZYlyDk4fH4b z;VB`+WGUbf`js*taA+a8XWC!r5Xt$J z1j2<(!*zwl9Eac7i2sVfJA2d$>)6v9-$d1$RJPj`Y^Hd z+9;u-dV-^R&3s3#yx!5$XS2gmJK1IZCC+KzJ<#d;Ai~zNJlhJVWOt0 zD>(Ipyz~g}YXpOm;b(`@3rz!qy5e8A2~Fz9c%PMkH*1*9_8~a5CfZ>=73HvwigYyo z8slimigMMDz?1wN`kd)SLEoW8+sQl^IQN@qz4E}IYrOXqF!BsIG?(+R5l)m!|Gj36 zy{T|wv+c%2Tl4ZM_LkyVHs^m9+I2Iw3HtYUNahFBy8k&U`^217JnrQy-Y?~8{a-~} zAf!q`n{(`zni(D->jp8ZwH7xvLM8=cgp z^J>t}Ts7ECZS2f3(O|(BaTPirW>kmXBrioA725HL6gC*Gb_5zqB}+B_drUPHo>l|B zFEWGVGX5vzlMI>o_ovDJ_m)Tj8y3m_nTu4v{U2%d!b+2w#>ICxB=g18`SS!_UU#@AIH`QgCfPQ}e5T#}YyoY>H zjemqxyNMpIJM^_riq$k~qXp|q>g7??&y*pu+vlQ+`8j3i?~0F)z*~d*{F|&NW&#y| zOYg&d&Y3yHKX^fx>p72xa*qEB4mrqC!;uQdp*?<#7ov^L-J5?EUQ$htdJz3#`Xl&S0eVZ&FxIRlPJ9}rX}B1tslP*Q zY0yYp^SenJtDc(KIQ-&2AFipLjizWc{ocL7xo7AD-r@RMjED6VdOyD7|NpSBVfgD4 zKNX$;Tdoq9?c|m@)y3r43&7Tb?MZ4uc!F9`$8+$2C;y`@_z!C+E`-TWjb#aud<`e2>kMOtq z1{`{6h^z4p`0!KW^V4Wj$MaeA*_Zr7jjSVjf(2mEgAp1_E^*jZGy-wd@I~}aIm`q3 znRy>qC$+R31&6|>IP3+}?V4X_ik{gkB|mYi5-=ED&6aFM-{Yj>yEIn`9Ele18lK7B z@#tE+N71|Uzo+)NS`ADU+P}J239=qhneVKc3QlXyDa@587i~OoMH`rYLkSviNi}|b zQ8DD_YYh+RclBoW*Z_LzJDt#)v$9pglXO`hogteWm}?q+L=GQBKmS#FE(S0Y==dJd zv=BXX;C7|$7xcuAWw!^5H1&wzt3~H(4!MVQ0wC^W5^L#>#T#Cwr|ki5qU~7 z{Dj|IJM`LqcyHWCBi(wh9IRue_HDG0)#SSa&_|Y}OXlu)$o&H+`k7g*bLg!-01kDy zD4Y76kxdhhh=KUd`uEx<1O%;>^qm)}{)G!w1372kwB3Tw~ zOAaxa0w1~%gMOLbjw055&=95aE*isJ`0yBb&|6$br<2=t=Wg9|ECwf|p9Y(H@%Lfy zCm5cSMNAXRx^R0>Zy^_&%ikSX^B3Vn$A{<&bKykUY&VB!OItCUx`^6WW~5Mie4t>R z*3@rSLjPO0`9q^rudTD~;xLTj5E|+3Y-S8m=uMNB>;M zV|tFmw4q*4kD+O#zw9|ZB!zRRxy6D#i{VQT`TsOnRL%Rn4^OHj$Fv8H@R9K@%PY*P z_?7GXOXRrj$2nTA4RE$p_kk}Av9*{JZT7p=>~G_}SVAuPZPsNVvDA9F3AyC5D_kRn z5$h`C;WtoA&&MBtnekPNiJg{#DS`YB?ip~y!J!UlnEs3YyBojTOXQsk(Yd?x*!Y7~ z`4j6klAm#VY>H@x=7K-W5h%V#E(x!qvOmG7#o*8H_#Kf7F}xP5HJf5fk?Q}ZSaR!v;HV^5E7`}7vT4s3l6PiHp?7x zQS0DU=)No7`TrWr%N(DWF)qtuygu%s59q`H%+9U+?`WZ_54z94CfFN>O|~_4onULe zIniM|HNz%$o*^nfE*7-^rYgSc52``yn1#`NTrqjG?i6yozVtc7)3Y^wj}ovGz2MJ# zl~%cXh)omC8l%;aF!k9p5Y z&)|4sxRwiwsXgE_?f(k^>)}mqYqoW+oE3s{42o z{e_qQ{G)jwnAi2ue}ybw>MN#4yX z6`zz8*dopkBJ-8Bis=!<6 z8ejcEr+?-)NuRhv(c_El-y5IWGZ`Ac@YACHn^PLYl5A%{`eBXVyL(k--440s>L#^z z4Z7)WXcu=;GrNFx$ZLsGTQ^m#|BjgpzOi!EFOk}+4qX7|W^&N85PtY>% zCPw=+MyfdwFIo;J3pF#*)F0#8|A_dDp3VPx@BB#4@G95UJDks*IX`!D?u_A_z6IYo z0VR#v}Lz8%r`J_ib5G}7UpKCybSoa<0 zZ%^v;o$28SMPHdRSgB5h6Yb^yf8l8%@FhF@_7J^He|ojYCOaCY<1rXE(bizX%k(ho zbYh%TevAApJr=!(-uYfI0LP|FQ`hquudWr7dWz>rF?;eB`ly&)ycrY?>H zC-9Ig`WS3+d+NLW8u5%RJk59h4h+Ueq~QKA-Lo|E(esJhp2C$3=&{`~-C2A;CQ_5{ zoaFKB=Kr{luAm(_w}ALh;_-!C=iU8Z?wson9JhPqse4Rt)|yynKQYU!6Y~VilUz*? z`fKVl-q+L)M00FQMDqs@jUDN<%B=ly#7TR=gInN@77l~AV#P%2*ll^g6>L6uGrq#} z+QM5(ck{P)WTxH78e}qTd;|qH)`;5`5n%QHE`HQfHh76u-@OP+c45V6Le-)9=SkFA_f69$VlN#y(< zf=3nPh&<7X+oD_Sv!d8xPpn+FJv-OM`bUK%uQ@&tc zdO{3hZepO0&#Owi!R&Nv{qj;GE)iTu6uMCkU9wIMZnI4F|8B10_u3lSZ|*iJP~0s9n-4Lkn%O_E zuzq+R)2uUM>##M-%+zKL(iyPtke~c zP^$0H$90@Fy?L-w+cZd9ogSg96v3f(gOr+SgA_{y9HbYuv&xKej;>pHKC8zNdt@VuxP=CXz+JFSaGqA?!pu&%&2{t@frAJ)|ZYDFP%uL^Lm zjD2mNpq97C*WRsn{DVBteekCXxbZtUv4Yt01A6|)!sqJP_d?>y2jIqA)ZBOSyhq?s zFn?8f@jUDc& zzdxFpiOiv%53hNZ-0@CuhI~vVvr{W`z>^@hTli#1;M1GraPgI|TuknIEp;t-?cIyd zUIUhFW}Uld1iAgIehXAQt|JcZhnGPQKKC825&vcTj$@q$=GT!2GV)zosDu6rmp{reIt?C8 z9U)a~Mr-Pd$7t#jMl;`*^Yq&ZPV2%cj+P;_?JnyAm(IFf^67k1Ynq;?3nP~kdh4(p zVkR#ek}ZY4&0H{MgPBeolmb8AEd?IKS5-$pc>_J~ZL$^9i)U2BQSv}#mz2Ph`LaPr zES7*?`3xS4)A7MSx>5}a*`y76yiYV7Mqm6&t{hf#KnY(&A4cR!DS+9`ftS%r_C2kd z<{i_Tz3I{DLtp;lBT8`EX|3sgu4=;jD5PblNL(cZ_#77leVMW5w^I!2&YYetnWFLF zX(?dh8ESla%+g@?&aF&s$d_4Kb0;|4aC*E(?@zWfEBkL1gZ1Rj%9(fc??K5_%k1rN zK6g2}q)z7*W7|B{e;u=_OX%mHwp=y;LSJ0>IXeAs3)H~UwRov9!{Y?~du!<kCoKyoonTKubDcFZ<1O)7roGD=oDk4 zW$T0p#jX`+6y^Wr`@8wjb@D{fXoFI~oIm;h6gbuz z?Q<>Hj#E5G&)@7rg%w<4es8gk^$*7_oy`AMt}AX`z;d43m)D=?x5i(jf;wI0dAQd@ z@X%`%Sjara8}$3V8Y@~}fVa+pS07{!uyv4(ubW`$AM3P6M%x>O0xd^mvBD`OYj+`V1fXkW9stnyU?7c2aAOLgRx**&If1)oAiM z8F-s6!r!MaJyAQ+CCA}a{nBA6?B)^C^bMMvPUltA$$Zu5?zzZhM(fIxa8&1x}`{lCG@?wtoLXexJ?Z#JfL`gc~lAL2iAOr$H=>Ad6=nV_Cz0W z+1+=_T*gs&ZTvvJJPl3J6#7DRhlJ2A^mPqip@xl5k%Kp62tkb}CF9@|%x^s*`QL*3 zjKmA*!y`)2Tl8qGr#F7*UdfcYUKgg{We>{V)8KFVh?onkSP9;|yhsj8n=J>`&J_cH z-y|>tRxpiC6OI3Dk&GXvFq4+v{Qqu|P08p76!c6F*N`V#D~Ie(lUl#BTMX}n_VO-z z?yvD3YS_#?>^VAP<{VwX_tfuiqzV3AcWLzR5&I=>W@h(hjmc}3#&^LIbZia=(Lg)on-AVRJyVgjWB9E*PFZ|Y{3;YVLb3iU2x##zh@*~bj!35oPR5|jPG42l)h z?hnA<`f|3$C0X!8=)f}3!r1=Q|-2G6PC(bwkr6n~Q9Y2@Fich~$tFX#O+w)*;PEQm+d?7qXzs5Wur!4cA1!N9`&w0@tVrfyuO70skN0Kq1Uz-d7-7$+n67O zuN1TG!J^~DbUVShhdhs&LS^_?mZD`XT?U3t0HYeA z_^QC6znEM09lz=25kgI5vQWK}-_|x#u;AfnjVDiiX1uMj*LZv5T(+lE?9GAG9nOIh zovxTA4u8!aW&ojC%s8c*V$t5LW_~kX8o@`&0d+c#XT}*hWHEUW-C;4T6*FFg_e$pA z1CrrYyod_UDb#?JkYV_ky_PP779JMNpYPWgN_Pu>^pF`IEkJ)fPtiwyr24-z+bi(r zIo?4ZuF>l6rl{Vp;zQeZ7urB_QtjxYy-xpZGd}rm9aF-odxga0D#3g4aZKMUhScGI z-{*)Ba&9~MqjjoD-Jk^BrH^ae5g~XSbtnmb^(0f&&q&<{CBVKE5O?nMd!BreW9tN&n9##UQSc{kx`0 zK7)4&zSFjdzT-BEKBKm1^irzRuxN?XxAzRe`S$|m?lGI*YqMNqrH^AZIHYWlErDwk z%l8XK%OAs)>bdmHcZpHTTd0XwGJ9hSUexamR;!i{P%3)kv-))eeL`HvzF>{p;oGOj zX(}gA&{P@5ik0r(R{YXRUSM7Sf!_t3q3COJCOuiJGssD=q6Q`~v#%36x)u1D9R`cq zz~Q^Vi4xeRkQ+{+e)SjLuPykwK7`l&++VJKyO&y#+()TgK0v8a`bo9P10}15xJ-hd z&4Anc4Ahl%8SGKomRwRZ=k_M9wM+0o`+bO19l)GvKW3;t28*ts$GuIRY$Wro9>XtU z$$5C9*~=LtRc?kO;ju+uEVFc2pH*m5Qif|wzk?(72Pdb31>3;PKj3ThixuDDyKRR5 z?1TpuCwe~X1ID~fFPH%wdY^n4`Rk&i+>YnFUk8I)5A%HXVUpL=KH$(tU{4(wv>C30 zCx5{Mwx$2e52*#0!JeNIyr1+-@_afI{&bGVw!pi*;S|@wpvqB>#!-Bi$I14lqw!AL zzXLQ4b$AX~;+@v-lO4n#j@p^ST{So8vlrnv#o)_saK9})^dK05?*_gsU=G+(4$o?V zPq^c--QdYQaHR~Mqw;*_hLnGitf^dv{s_Hvc>y*0W$>JRyw5eb({$czG`RgauVofO zIX#G_%*ZW4>sav>uYJzeiOmEybRaJ@i>)A0{9pfZI{Ca&cxQeD7LB9Ea41r#c#qiI z5zXwQXpQARP+hOap2IgIE`fz@#6&gfr!cI-NVz0&tAyyr=#}g_Wzs< zCmJ%=S^J+c&f0guBLm#`B>A$psF&UwXKP9rZ)>_S+17FgUG)po9Cj5?qVuzy>b-S> ze#v3cxRd$Em+1rZM~{{eVq9or_|P+v(?s@veZ`X;9@Q0t0E6eA^H0y^LO;)zLX;cojD{L z-^KT1^H$Zb;||&X(jiT79^TpO^4YIbvf*2D>v&d&x_zrBGMhGjy&5=Vwbp;vTE*x4 zt&&f~I>kGAq3Si2S!N;dHqV2apk6yQrfF=~@njpfOEf3!kOI4;DgoMc%!%G71THux z1V$cq8Ru@+1oU3v4D7K&6a4*BH3%OOL;oF$9{&-;M|gG5+#&~LZIt{kZxelg-%Q`k zD%tasm7@34EiT_(Tb(`y8w6FiNwBSCKJ+)Kay4@stFy@oeGM-f0~hMNLblAAE>;sq zRr^KiDpTnrJ4CKGfgI7_M}}s`dnj{v9t=C}YL)U2g9T zw5^{JU&X+ORL)6ix~24zmAHKjE#wr#IaiO8Unw7oHw`_N%eba>CdYDksP1_qb^Dw6 zP}lSlYV+H$?M6e1kY#Q;8hMdwi7(+%;T+jy<1=O zkpGDXpL>H>Tfn84;YMy8qwl@sCV61Af3=QL_J$Mio@?l$4AHLUz`mOL<%3+-j`(GK zJj`j0h5x<83=Qz0Qly8=&T)75pzffyJ`!Cey>3M_$!T>)PlS(jX&k?emhTV9WbpPbap{Z0?@b#k{6B$E6I8GZl{W27YNP;Z{0cAH#Jfook8(E=A8q zG<>AZwi!+|V4S@%exj|(G|Ar7VT!XUa9WFP+hnKX)H0W^vQJ}L zf|vcLC#2Rlsa5@&E}Bemkt^9!cvpOgzD3hKhQ2L&tc*X98#1|j&X{jM_L3B`8U0N% zb@))~+(+OfM{`6U>p97{AYbwwnIrf|t3T`TPSeOih*j^~{(K zJ0yfrUvF2DqqO-aOKI(qrG~|$LmUZ~pmh#g%f7@PlY+AFczu&uYK{1*795fd;|@vw z<1)nn>j}yHIA1o2^qUu3G14x4gY`kU&u1azwIK` ztKSycyZt^v{|~jYFLr8zt=mNN>uIvV#C*twJH$Zd$Omjpkpim@X$-HQ5(3{jtTC?L zpfUAcWosR*?*XX$uG?g}bFcZj=e8sx!f?jA9 zYwSCESx0gHTUpa-TqBv$M_xjzyi4wB6~2n@xied-3A<}$R-6FD)?hB=G=ZdSXcgKl(wuM zzV{7gLxRTge3ZR$%}86zDm1yzlIZQAe&PidQBSThq0c?UdSxC`>2K_F0&DR- zxWO{k+{F>PqKpY@`Rn9$TcOch#bfm87f}-|Dk5KWkGzpP&%PfFd5i{Hy8H2BZ93RGj$9o1 z<%-|IuUhJE?s<%EA0RkdiGklKnD-RHj0U((Jn`0HuterL58y|unFY`Wo{>(B`|J)!aRL<|mOtYFyazVe~8Rdz- z`x0|G#-eZENnYBY-)b^3^hN5PDO}rP!J%$&-#%bZRkWt|Gcc!fjK=aNaT>iI4Hjx> zv*^ul!}W{#DWwCzxH{%V9_RBrgG0yYX-wsKCeX(bOwR8)9PBi)SvmZ%$4E!Z4RZMN z(QmYjchqm3=xFFa$8=s?}$)(4mHeWTDUY3~wt26eeA9h%V5{zCsFq0bf-Rm0t zzc)1c+Dn38*kO&2Emic*TrPU9Tp%e~%Qar>HwxbG>=L}avjqPoZ zIkNeilX7Tst{Pf?$mzXugBUPpp{QTHQSb`gsqy(RP2<~ZtKbvNocka43%;K+fAya< z!PqTDFg5NJ0!@eM|34rGe93I2+)a}4;$e;9K&}wDJWDV&fk9c9;6bKjar)bN4ub zLtK9=cktRB#AsgNP-k%HIC|>I@F6tR#nXw^j&N>&f){+@K(Vg6r&=|sm$vdXIFM(5 zypG6K?L)`3ftew{P$&BxkB`}4BQ^W#MeO4ydjDg{Rga66EEPipOFQaH1L*7cAlcS* z2`=Qpy66CJUpUHTachI!e$UKTD}MrJGMj_hJxVDvu}a+YMBXO0@h7nH;Ws?WXT5@t8GRj92jMl76NL)*JZk1%mwCdYqIupC)_FJh zRw`Je=eHu~R5b$3N&=5Aay<7k`{PZps1_g0zY<;b61_EItlfH(p1@@CcyF?g2Z^bx zsFNKUDpz+ILjI3=Pvyj5OYu(#!snNnEOi1NApgRLj-a#tJkC|un(M{#c&V&AzH2eO zcRTQqxsK&cNjiKmJq!APMa*U@52H5g%|2cl=FT;^n(#oeEe40CjB(WOnqaSAik_%& zqP=k{b+hAh9Fo^;jqWRY5;8J{kek_3n1OyO^1Q(xXUU=c$=iHOFYLkNviWa(1a}g* z?MG+aHCr39KgYu?4fb(5K3d7}Th zOpWiT9WI}Vt3_STBGGqks^BvqUE_m~eZX$~j0Q2gej)QupVL!&4ZojYG};f*0vYKu zeFsgUK1(zuWs4^GnmP8A9J~t+&z4*zAU$6WNIox`W?vD5Kez-Q(0kGWk8H18)!gfl z%Qto-b5)lMrt#}V?~U6v-q-fIe0%P8`Cr+p32>yMVZuJP}QZXsi@|k?CkmEr zXiG}bxebQT1;ZEKadUj)!4`063OMu*=kpq{1RvOXZG zsE^3|ECYX@gC(=Uk=Oa|xx_6qISz-wq+fXtYRpC6yaztA_=hM(AG zU6jRp;oHO9NNwRRUIWGz1i+8JLf`Gl{RfCUf{BYB;|tU!*;zX>(QfU5c3Q{GvNaz{ z)n5&kt4rtue4p(PuqZ1^u8bhhH5N=UfE%6oj@|LUxC>V}$l7%f8*e1f^D8`vdD7+I zvmFPY+%b;Zx8sRBPft!h9NtmOV|#htKJr0{#Bj_|DxJ;q7IU0t@*Ca`eiZM>s#9ldWIV{DCI&{t1R$jz@S{J5Uga$Oq}qgHi zj-bIPL>E@c^+jbCR2DqwC452j_>ljISL1T#i5Ii){WzC6-%3YwOjqz7n>f$fa?A=i zeyQ9y9n73h?pmRT@Xuk+rt3*|+sI^FqjQ3--VYq|n9|bp&IIPej&(G?GQ;6|Ylh^p zd4pi+pH9y>zB_+_H%{`@`I(Zr2bfcNQVBMpJv?_p4o#u|-t(B;sy0V%dni+Fwda&a zkoJVm{KX-yNk~ymUAB>@$rVknUe|;iyDpkiu8IaHePOMrtuKRXgr;ir=a&lRy(xk@ zWxvLlaZ)l>T#~~&T$Wo;J+HKy%B&A&wT4`!Zl92+Gyi#14SnOF7}7IcFxlu=-+EjQ zio?UBOTHQ$g%;`E3rb+WtCDF0ULGQzBU*auC!NunM$&VVw8N$Ue3=lExl{_C$edMa zx$JFTD*4^sBZhq||AA;t?jzL}_LXa1@2AwP86aE!!sl!z*V6@Tx7m*o^tmqJz7)27{J#wB zv@=WOc$8Ql%RVn6_c@Ad>CJeT)g!@WH4oL)G%;sv3O$>}tWjUi$HUd9PEk|;H6gJ-0}Z``fLTY$yvCT8}LLE z;vF?w>b<0)=r)Hc)jxtkE$sSD0f*6^*Bm6{><0gn!ptfO9CD%N*~;Xbi-n=5Qqf#L=34#)&ZRT+%`$Mu$(;@1=aGw8s`5WAgd_VM+oC0@ZEYA3-PC+Py1A(hebzPZZ{LAG2lcjFtK(+w5FkNl)d3EFsOT!wK<<%v~Q`w4sZ@da!;7S?7oiY zkGzqR&#Axv;`wJkrf4STVHtH>DRa=3_$~d7huXpftL^K=ChL1s8yl}oX==DWy}79i zp4Fo#IO=bjv~LYy2}s|Kd|pfY&)mKkr!nYFUV?y-wAsEtQ<7uB3X;)l%VOy)R686l#l{;OowH;_;)}v z*&hB|zfaKLA?xE1IF!h&U%)G};j|KPjXjdh?D-G9E*ZvLm;AG?D1NK*6~p55YM^hj z#`n^fg8qZWia|41>oeyg7x#M~>f9DAk-c6{5xjHu3Vy}-URQx5mo5n9_n-J)`-(j3{KohWYrzWU*n_#F*mc9O5rgiDLR&6zyybgTM0=~iT@$N?U zVb{psZp`Vg9MX7CJ|_5d+#`6m-!8R&Ws8L9MV8_=C{52cs^&4yevBP^%SN%Ta-C`! zyj)lN^9;qb7|q_jNL}H_cuv!gnT0UbbOSB*K=xSs!^5!SYW|9SGAla4Dbz(7`lT0Ky(IQr0s^_5K*2FW%3*ePqr@fh5x;+{Vgelvx+7=IhS z{ZdUJ{K9qqwoLekKhYNzgP(3_GW3jz^z9d_qbNB^8Q}FAI`VPJhmPSv6w22=;Cg!kejXUDv8eQxL-AU4k7V{2F4b1>8U=7IpXb^(p{MX+ z)~JbLROb@QxF6?F**{>j@yNXPWim&eLp9*(Of*s^{+Dd(LvJ)aJ*b&XV2~HbC5~)nb+5)~s@BEW z%m-qv^&dw!H$K5v=r%o0Pxubmj79NQTaQuAwoQ|o8bA54si}NMbMyF#wuaZI*qVAw5$tQH+jWNuy_S$+)^jVxQczK`FxIaB1dIs}*Hf$3D-rFe# zzI;LnG~~-c56`NB85!Cj_f#e1{vjpsZTvZ04$1+^dj!Mwy&Au&Owljmn5<7lD}9+g z*@0wCyWoj^`?Bi4^*VW}*CqXR{MUM3QS^bA0D70#)Ao8;lb zj4m!&R?;^~j%CSmqYwG4tJvRYLp@Z+em%dl?#ne|Q}Z;{^ag%AJ`uX&d-!|22vf`_ zhO0H}sfA|3vn=6UIRpOu!Co1fBGWpKiRg1~GEW;5Ay-fnRUTz#y4i_0Tw|U8<5txo zE9da?B&p(M?yHe-oKCG<2l}xZ?q?V1#qi81a%z&3=-aw*jl2#%MWPQ3fOGzb-fK!9 zrTV4bc<}dAs~>($EKa}GGfjII1ReO7lK*Q3)RL${FZm6A|! z=pZ;W9^N~b@81qa;xSZ`!^h_G{sTCsE?}86*DRJ9+GluY{v2;NhsIm$H^f;RC3fkR z;f}h`s8vhC)T)cjKPHfMXXO5o$t;5VoB0KE``7tcK$uefkb20IkAKbgJf<%bqnfQT z(d^^%b06a2HWQrD@cYNZSvT-EkQ-Z6$Q})Qfkg+vbv)S0*0AT%dW^k(6j{~lV_R(Z z;;k))vDSvAQ=1zMQ=1$2O=@ZCN1l-PbccQDRE;=qzPt(dQuEtTWIUU*9GMPA1o)08IJmoHn36 zdgzySDxp#M%=?q`I{koXShYvv|7@4wE0PPQ9+d($WD?>xALy2?2JgnJ|9ZY^{N{>m zYQG+kBwqpv82{Ojzg9u-@=9Tmw55q+1>R(;Ol!;!y2a=W-v^bX%B=sWJz7`l^L zbalOC*v1~)ppDwV^lggaNv7yC3vJ{D{6=;hVvm)*j<)Gyz+GylyYz8??2vpXp%H(^ z-}CxjA;^&`2Cv#K__slm^lYW$6~mTIfeGC@ad*xGzG}ICPj;w-+pO371UM!A*`jEe9Tr_(tMS zghp<8q|JPdJ*BF#yT#*8xQwT0gRk=aPG1o|U-Xo}fjs-7PI#at#=AbdI?DCg=VZC|0^_DJ*D7Nk ze;JSb)$}Og@V`Q|y}mn{NlU1Oyy53n^a{7pe`%tWiq8_1ivI9C=#I@Wfv>`-VfO##WZlSR%fp-Tr>K_ZFQXl` zqa^-(ck z1|EvTj^hnyI|0Rm=hz+vu@65#ZZ4iA2 zfG^v&DaOb&!Ef|2$!{$E-)(Z}SEk9n!_Y1jWQvC6;Ltem>cM73zZSogRl9_M$>iP7 z+bsqiCTDxcdfENQMUuy@6^i>`t0XtyRg%lgUrK6+6_Rb+TB&g$^$+~FIS?Fr7ajE{ z8^qcN8^ne^3tTL}Cu%G2Q4d8AcPY9$)TO*6Ok34RKlX&){U$i{1~^oSX2>;Cu8e1{ zQ2__+8YwhAijv59k*mhRLv*Je+Cu#^o@?p}*H$BQ7q}9*(z|UF$XRrgY zInr+4&AjECB(dV@I8F60iE%nNN{+>o~Xejsme6Gj!Q-w;yYO!|CBFWST zPG?%we|60ZJ@taksTbz!dgIC6kL!V*{o-q25I#cB*|mFibF}USd9d*HYSCqI=L{G_ z%~*Veuj|a?ck~qG!WO^D+aJ(RIQN8qW%u5VY@y4sZcmfhg=O#l#dW?9jZSGbe1|U{ zjI-dVs}eM|-;H$CCsRxO#Qo!IJTr@^CC4+%G{?x5smzRCh2Nge&voX(<-={^bym{C z-?J0w4Pzzn=evrZIWd&<)@&AqnW1^^UGU?HrSwk8qsdt2ST_<%tuSq8eeM%bOPxdGoJ#7vZq*^bEesvj!sal5tCfiRjWm}HHQRWasZ8g9#ZtfkH}sf@ck*xll-5a zRSe&qR1B|h&=Uoc-x`>GRtZ{pK@N#Lt%SJang4*gXibhbG=dCz*HdbMgPr&66LRSB z968jSF9&{l2mHB1uFw@FV9rG~;5oZx@4$(#&Q#hg$)YMdXkzakX_I|QJc1iwZy!j7K)94#g1mpJgQqbS4<>1%Jf2`Xg2B+fVI3z_h zHj+a!V2#+iXjwzhnFCd?oZo5coUzg#FnqJ<61_nX3>(GzUerLpfj=A33>{-urr#jc zMQyM*-(Da!Od6@JI2G+ucrC)UP#@+}nv2fN%JtowzHA6J#24JB*psO7f^&GA`)pTq zIk_=HgFkz&9$?c_cnGIg^IM#!%fTYN5yo<_?7_@sGWX?vyxkUV`Zs!?E$j)6 zhbw+PR;>Jk>x0bsq5!my>%l2KKQ9k1dS)+O`IZ6Nis6H#s%3+v8g|F(HnYPqlXLWF zlu)0-EXbd& z*}{0sMv$9z8-IY+<20rr>}+-98gy#9nUxnEqdyu#{dR)$o4oeo`IDux?kmNbcRrU) ztKhe?nJa!o{br%hdcxOVfkRrx+_*j8SIqkkaHtnJGIWf~i)UyQ)4`lHu;5o}or&~p z`0qP&b&9+4@sW6XM8K`RL5<|hU2em%$Ekm02KNvwD?p>|ik8Uf*Laz0D~$R0^CWA- zXLxR>GXI;){k|JKykEGs!r7BOC%5SoI5dh`nMjS=6^@5Ju~Kq?fa4DB0uyz}(jv(TuWN0)gms-@|3xS_7(yDp(W`(wDqav@4EEu+56 zWuJO=tV^K*EILbVw2apx4ICQ8bGC)&7q9TLzu9HZ8)<9ahCkemSZhlxItS}0tF^~? zYf}j``@g4J8{eK}ZN7-M`o<*D(QA^Vv|lN>&p0aisO*$Q(vMs|B6)N_Ew;|aqp_B_@2FMf^t@|4hz zbCq@v&nrP4;BCsmhxufj?Y^MsN1c;>_vWhpYpJnPPpQH0omGN2pOixu=SgiI=ZK+) z4~fP{?0X%8Z&|;`VOVw09^4_r5&ChuCNL8}>a}EYtR!c3_*pf0_9Z#!pX*ZK>(`~= zzi&!`MVHCEr>7gWS7Us%O$Z*nTrtjDr1g2eLhIqNO7`_!FSLD@qG^-KOc4JwzjyEs zamkkbdY+a1r=6t+#BXsw{wGeq%1dzethR4nhw;W{$+hnmLF~CnYPd%&^e-IsM{CvUmp8~Y z!JFy3*V~%*eXN-OjBZsjAj<8{-J?Q%v4N)uS^w*VtJy5NDcaUsaKTtG{4Hqo~=(GAV z&wZW8F4O=$>9uZCuT}GQ6*%=XeCIFJ{r~X!0Ol^!xwk%pr+&oWf1mov7VBtE0Egfh zYX3);&=zLciFjqn`1!LtSw0KzvlQx@D0-QxGQq>{l|B27D&goIsQF z<_K*`Fg)!UxS513nBbW~ncA zL;{bSsB_RslXoar+@s$?>sYpm_Y=r^yALP)J@cu1)K#9$zyrZ*6L|C!{nuY$(9dY4 z=b!^^&ud902ziM@r83H4dIN9%Hq6T2i?LZcC0MO(66{SMqhFlN>+&l5igCk~+OP1| z^QAv)gFfT+aItC<8q5M_e0FAKJ2}_740qHzqFWk!jj%Q3gF$Q273m{1me-=CN;I8i z&g_M*^v|KpHUoGK4ujhw7{v4a0-mjiytmT*%XVk$8LReOY^tP%-W4=X%3mt z+aVxt09;2<&c-o${|nkeZ{|z|Q8=krn502-%G;K$vYK-;hE*I<({ren}LYBgnhx5G|m*o)UoZP1N zJUJUDwfYO_u+Nbl`@=RVFnGB(aP@pwzacBNzCDvA-}NbC(7kk}?b_qokgu5m&W3~k zl0CUarxfEo9(S>Oy8`V%Te76R&?BuTH?|wO(JtHNfC>9V9~-l>czm%R9?%$Hr+<9+ zgsdOSzHp}vTHkq}sy-DTN9N8>2_=7{<>&VmqgOWCi=HjjPy7;xW z#?I5Urt}f5N{S*}i;Bqqnag=Hd4yv2N4uBa-uh zo4M3L{9TsA;LwM7NXxa72&Q!6>-K^{uhKuHQ8Oj; zIE`y4kJ%jA9Zrt|=KR`1e=tbT_oZ-5aO$tw@nDW$unT_n1hcLa3ATn!3C-3;_zjJZ zwKd(3b~LuZt8P6t(by=Zsw3WXL%_@^>K!tH%ELxVrOw@uG(4$ap0b!QONtGqXr1r^V}?1P)cB z0UOAUobxWH%{qSS{&R%Ffn|YoVfJ5%FR_l_47VAKATk^+Rn?tFIil;U={l?J^9NIj+ z#qKp(5xXvQxGmi)cwa#?6hf|9Mux_xC0lea%NIOephsK_*F1`O)N7YipAU0&J|XCP z!pKj&gLZiy-uFpd%Vk+=@RxAe>(L^f<#+|YM}xu>_M(SG6W(TCuC9&i32ms3zquW_nSxfzj@Q6wbexGNwEo59ketoc>W|@#^1}hu zzvoWX_bas1WTbglvJ(=Jt}$*rE{25V%Ap-{w1LS7RKFL=a_e(n3a$;SHLY)M6k7e9 zEZI^wOLfz?h_#<4OXi3TqNQbvrp|kdqwdoss%7W|U1e|f>jO9+@;FD7D6xKQv|QuQ zxqJn0p--G#aFl5I0e{uy38Hy#f?W9&--{q8kHfjHkCCg%R<7F2JqnF+aXLLlS9-Iz z$&!K#Eq#eT>=$%GXXxpfia%@lWfrj`*_Qh&K11eC)B;+#kt0#E=`DJ-k#Hn~ z*;VQW_t24}HbyWn9Hpt-G1gv}ljx}1j6ZA#=4A%_ai-(fH=bJHE3Py2JEdV@Nj6v_ zj8w}D#*hP&WUHNkPIGgTrmBHC(o=YsFTu>NWK5$=C@SXb*HiyIryhEhgG`d*4|yJ_ zeTvpHFZ&hF$rD`Z#I^E}nrA)N$~?YK0~YDnV+|+U)q{_jxtAA$M>1c3g|Gb%{o({3 zpMz!5eEbn#TLm|61M8hTGHnyc`NCt;jXkqBN81{Xql*Hkt6N3M)$3xJWuhhj8qbs? z&&4q%0fdh0_+KQ_G949O5WZAKje-lQyspE7QW7z-I40tb>VlA6HvSkKI|4f z+jj7JARQ5PzsZ8MifJIrP9 zi;LL}>H<#wNDVs>|Fc%Sj&tC&R@0kRpd0!Te(x?vU$kl8am*VoST=)8;X^f+KiGF& zkDhU1q)_uM_p^HX_>0u7y{KRJGtczoIcdegysU`q1-$8$($V}KpN(p1!lTl9BCe&m zHnzozj>x(%skxDl@DCcI89Z zPbD`#XeR#C8DLW-wNWb9Hy&vEXLvGxkuP~4I4gP&zNqo-aak}-z9bt4o>h!GW@cZK z>GSFdd|JugILFT=i!5N^R@v9INAeoU4y_v*&Zez`Vc}-UFnqlfU?98f4tpM%XYm0! z>0%ssOdE9gkZQPmOlx>;k7)RZOzIOGrI3Eh<&cM)L}Tp{!PE7e#&gms$uB=g4SMf{ zHl&>1?#(Q1P;2IaPG6-!GS3E5OEu@JhS-Zrz#4LD2W0E~?`GyGx)3s*I&gQMHgM5#t)GQ_khjS)%h~C0o3mY`mTeQQ-dm*Fa4_gT9JOY% zRO^+hsrOHD)ZbesnfoQ`D#_}po{VSMR$l+hQ9=WGzh?x$3LHdr)wq83`meQ`y! zytGPOm2J@6 z#E#LJzZxx=ckqAh#HY~#&U7corU%^CQR)nLaA*ZRQZqA`8|>PzU0_6{;PbfAf350d9mOS`Xi2+eD5pNLC4^T-eoVX4y?)n<6dDl z^*K7mfP|L%t_coH|B?3Ej^NQnJX5s*S}R29bwm2-KG!`Zj^9L-Qyc0Ag~+D$gNp|(-Bnm5^<59YPFPk(Ho9=bujx*sk` zM1$&1Jrsxr>`id!6tgr>>Ylf`*StjDQ+s+a|7d&Nhxo%5;}c6(ouxX8%xn5#Eze&L z_q1~AR;LFV+5~2hPJC92-)B~~&B=G+efQ@``02RjmTPd`SEB9BU&q+2yT`OxwTUfO z%{cn8iPom?CpR}O0fz=oYjLE0Xw!6@?@(vrlbVV*bs^fH1-m7EKlC)&c&4_-k1#yj z#m||GIEDIPdzRLJIyJ)U?3qnsw{+GqDe&kqIY@##9+V-4SdYrV^Rv{@lgE_cJp9#$ z;jg$G4U#vrvp#1;p9iNTk3TQi^}k)W2OqmB1dvbWKjO45VES1#v@aO;3p?{8kLd!$ zG$mjKo~{Y_Kt9M2eI92DzMBpSep8s4pV%q}e2KQO;gIM*o9vD4C$+|QnQHLNy-L94 zOl`m@JZAALGJdi~4(R-a6j+%e7$VLKKKrfbSB@$0c()VrfSc?zTeS+GkI!P&ib3cM{~DoItb;F_ zj2F*+W?ug=Bm08uaxpkWu2hi)UB?MaxlaCuEm|rc z2u2-c2J?`&*%PbY5B4&&|8E_ErMy?Pwro?3OJR77%k%sA zIf@ZUSYwi@^%wAasXf;FXJ^>^I6 zE%dPle$FMHyOZdym~9m~J;tKArsnZ>5covD_hJATM9#F+mmgh`T$&C4k`QmJdx6)- zAh0Mosipo5d!NhT`Q;d;@;0waDH;g&=1Sk7&RNOv0z6@dwd5(kJCyfxz!v8Am1ocw zeF-k%8C%v29<79@p3M86|Lyw9rDyrtE7ZX&sE5`tLz@d1JGJcD;j6Z}vc z{LLm%7p0G~*SwivujvtEtBH z$9t81j?462@6vyJhKZ&uGF<(lZ4CoP*y<9=HPc2qOwPR<6<>y@^sW!_2=7C^Mn*w# z4d>f*jw|5Nb$FqT=)q&-HI_QO6?V~&<;Ju$59FvV7oKU@zvqX%f2~zIzt3~-gGc%-~lCY>rtH{;fQL; z!cXXjgR)ODI>yCWqW)~Iq;Hce`PmK#`u9`BfPU^=d$n4<+8B{`0^U{SIK2DAonzxYB_2sdV#j^>1_|=v;P))%JW$=y`^ki zmnQ{Yx*!H$yCelxUs3|%&*}p1WxE*NGL^tz=>-Su5cH$TAZuPF_)Sp3#!&Fmtg(%$)jhUAK)A%@Q@g0j=(7YNs2_&gM}U?S#`Y zqoL{fpAJS_h;JAit5P}{?uU6;#bWN0)2Nl6M{5he28WzH%xb>p4mH%BXrb}~=kZ$V zBEM**WbrWkh5Jg?`TZo*k->6Rn_)`X`|ttpMw0~%zSz+T)zFLmz3v3AP`>~GMg)Z;Z#Z)303iyjOg2y-}|&Xw%hdylo(w&yqwF5yK| z6%ntJ^R6uusAUGDA8__l6=UHb=E_xv7m)Y5MllsER;xJw%Z&7F3Czk8oLI{>&CZH* zPF7^3cIgF2F_5>}!6>;zz2l&_!k@C>D=@{x$GY=*1GwV!VsdJw2T`ZkxOek0dO?>L z`dHWJf5I10TNOS4%h*A4_8Eoc;Kp8N|K0e$J7Cjtp2t>j#Y?D(QrN%$A>LtH%B6`4f;Vo3wkNM_L@nYG6I8Ax4SfM-~Em07Cn4VtlTlP$L z;cdX2tmc=P7Rz^>JywAJ}DzyD-% zOVf=>n#PDJn#RDz8l~?xyXR{M?LNvujn|P4qF2>2$t`lN;<|pD;=PvaoS&Gd)!|Wg zjO^v($7R2#CnUdnS)$*VqoQB*3DNf*cJp@}mW;PE$)et>hWxNq@eSE0dgq{t?7)u5 z&P>UB!Ct3Vyy%@oj?kF>g2&@D!E3`|$!`r_Y%OGkMc`BY=V7#yc#0(9JM_mv(Jv`O z(6euNvrS3-2L%BjnB;#hllG1 z$^Z2XA*khoWSn#gT{swYKU-;AdO~S4@3`9LU%d5qqtTAbR^Zo_;5hi6uIJbm z(PlTC(gyW}KT10y2UhP9g7eo1Aw^$k0xquGRmgW>k)3OH>;h}%0Mt;V*;LkPr zjPu}iFsKEeMjbeG1>PqVZb-}iYBzjsK4Dk-B>loH>YTtxO?96bq55Us-ZC5=WIwrl zPJg+2d_Spb$g&?qmQonlikQYcnqa88GTMrznveaEx$wU zHVTcwH1tAM;D`-uIg2ms0(8@F!E=$VO%^RX_~6lf9`oRX>cAq2zHBCQM5o?jJ$UT& zV72i1@4+szK1%LUUo}z_4Z#<&kk|JCywIm$&mOSn5IFQZyC%n@?PgP~t=>T9bX8# zc*SJeJ*|5*t@TSq*Ybsm+O%BTI%bn%m`9e!9lSM8v-`T_xa{wrC3?S4M$g~mtFAmD z`gk5uJ=hP?A7SSqHdzg9zX^}|{eq8)`PXacvhN*MyaU(Ep6%!AeDC8;-*%fa4w`$*FWOX*{AuyOB{kXm;y(Q$57cF z&hzfz(@2iT%2C zcji&Azsvpf-2&SAbW$!6{{!Cq55)?wL9e$GX_ zuYTtq=snJ1x;aj$UQO1;og|H^4DX@CWPE-NZ~Z=a^cnpLJE&#D!J@$&^e$!V@ENTg zFIPUF;8Nn2=u$)-R1ym}`tg5UkV|0+^;0_z1M~X6)H?Vp7M-THxz14ph8zS#x>FCi zgHs3KsL60Ee1qDg01UbXR%MbMyOCL8BVYd`eb+krw)NCQufbt&aMnoR(>~rl0Dk#` zYZK8LC4oUBx%Qkp!v*Zmhv9SEk=Y+UukbbKLzy3h(L3*t(w2UKmv(#SED{WP4i|*3 zv0?;uQ3tr6%lLiFg6o;VOkoH(l?RT^AB|TcUKxj(m;FKQw4c|L?C8=CXs!pKL2Aw0 zi{beWfOk8nb3Y~fXJeeB`c?X{t8w<4!5jv5Ip)Wb`x9krT*Q3gCbKagdi_#r>{zs1 zUyUR~d$n2^#B;^l7b!X-p|2gvx#~a!CWc zTs=6njN0%W{+4Id+0=VwG1O@fz#*pw3jeI)&^Wo`Tlz9NPE)&^Y_v1XSp3jy*RWGk z87-NaomevCWHX*qmae0P+6eZ{UY%@fn9B@*A@j5RDJ{+2Ke0(SH{0Fr9(A|}A9A?u zS}(ZPf2O#E&e6G@U#j(gfS#ruT=Eah>Nk;<9DP{uE;y|5Je?_cpC=Q{m)eJ%4ZpNx z#c+R}8n9(E9sM@(4M{sBLw~Y{R+Ebwk&X8UI>#OvqP`DYmhKe&Tb|Z% zF;mgc+bIQ9;|Vky-`YWBu=U?9`SQI!pVAjP_f7serTX4FsrtHrW3zY3A#R)1juT<9V9%cY`U~4NP2nky=J8)L zOZga%I8rTo7p`zSGgTXVg}2cGt{|iSPPDFcWt^_mFiNe+Ko9p8wfmd#N@a9Beu!~O z)zo;kDvh^2nAabKZ>|J)j#BUcOJ-De&fW9e`)Bj9m$~0l8&w3uKRD+wCz+iE!VArW z?{z^3@-_KYPa`z7ZwwadlKV+D7vY79`^q(HZ#2PZ7z2iCD@P9}S8b?ToiIqPzKtiW zb2b(O?xcf3+nL|X@C$3u7ij4t^F|9brBiH{*5HvsA2u0ZVjpm*>jeA@@w^%X{yYVr zj)6Nv;H@8ycGOKwv^Ug@anwy_2jCe0gAeC*Cb)SP&YB&v(ut$ALZ@fkc)I`}h|;}E;BWRVqi0E?XaW(m|l>%g5$;E*4j({}ow4e$$2e-m1ycw01b_B`MRUe#Oec9xMo>F#peI{Bn*3KVZ0vu17}<;5OI9!w zn-ps^O^a)>{1o3(`zAZBUFeG~QMQKk2uIyAG!>2TjOAAPgrFs|?@n{|5|(I<2e!!i=euR!q%_$N zk6Pc^$7JuW`y~G`IOi8+*)L2N^b4{j!$&!ip_08XExsF_@)Z3WWLQ5wr48tpt@^)> z*Y?`uvOfBI!Dk_vj!V)N|9CWw$I)imPN+Vk&&ZypQ?ln*=*35+%0BZqYV}oH)Zmv6 zs-f%Ag!e*+*$b}xuM9cx$30Tu;XP7N3>jrl&=Iv9QH|HA*F1L0eq*;v{!8&MI+rZD zJxQ^*MwjjOzkNdM6KRrHLAu&%*-k~{lB_ki-lR2+r!QN+Nvt=f*sT{+9JT?=nVTiz z(=`I!GxO6A>D_(VXYGn6NZ~$O9-}K6N?rwfPZiYcRij5K_++W&<@7I)z@-<=%!GdM5cA<90bKRZMHBt*&O^m&zg z;C*!XfL`h^T5jVhIBtMg>)%^4NA;CVBl|1W7YC`PiD7EZB_0ch$mY2mQ{g*poKq9v zrw^dBoDUW?bG(AqK!_J>#wI#yUmI_$e?+}=7jAmrXbp2Qjd?r#^c{Rji$>Y&FSD2R zEp@~N^tK0_^D}sC8|UoJ{8(hN2waCHW;M@!K z;I#}yM}?-d^ah%uXUq{-Cu*uQ=;Jn0FBO7W?{P0#4;S>`3=|Lk-ptF^$JuIxIBV^< z?9^Y4Y^fVc{`^gLOsy^C6?3GQnF=NdV4 zcs(g4-FQwO@%r6k&u9)ALoz!c;mqIeqVf2i8tnB*rFty9Lp^y^4k~kl*QG54RY7!BT3oa%NTi*lA0{d#D<{_v*7!K-dk@f9C~RpMkG! z*-^#t>2W*&aux6S*|NvUY}tKaj_mf=3E8bDnc5o<%HAG_)IcFu8}u&o{6**n$}>bG z9zup!cF6i!`z8M#=sPp$=SIO}zd_&Gg`Dl{c%!)P6TG+Y61@IeC;Gp$N%X&#Ci%2E zsJKqup|p_sRPxSZn6WHQUFM`Hauc6l&&u^kp98)8nI1 zF_a9$D{(Hx26E_E@&0}8v1AN8&&8^VW29<-dVyp-Y2ILuei^guBV(nCQQS}6=o_YT zPUHJk#B8@CTgN(^k=`q3z_i=$%v7v9>$8*@1xQGjB_xZ8fa91$+D=g zY+2t+uIV&Twv6hlHTe&gsfA=q*TIVU7W-uuyz!$rr=D?6;TKWM9JoG%bG$W~YKNHd z<)JZHG+I;pQWE*CWanHX&$vC;Q7MO&`e-b5PsC_@ zjqZ_oe(`9R=U(iWO?7IFM!UTDosUoF=k4Whf+H$5Qk#r{lgWy=*RBU+H;@-;;WgbH zNzX<O1lX$FO(b z2VK!t`nOzXFUPgLk$Iw#e$2!Sl%0-B1%IIq%Z zMLfw}sD*w)cXpdOT|ISB4>U}FN2pZ_ngM*8s<)tzdjk*TG_c5Tq@y8xLQCVZsZG{@ zXEe1$fs_hQHpa@penGyBBV^d3M?1@XNwKOv$3wI4b)d zOw)M(yWin804!?5<2SoB{`N#ti z&c!8BQG-5F-G?j_y$_2- z-Dd~^M~+EB&*=|Gprh=@F3DvyJi(_`qj*|0EI6kGCZ1CQZscfR39YV9`Zo8vfvc|DuCObZOTCB#$IR%Y<`Y_deEK02+1EIW@>*~EY*TSu2(D#Uy zt9~FaIv(svgIhVtE>;D*Sa6vXa+82sdPyv>UGs5QF&viV>?xyII8t@i7qm>vy~ zY8r-cp9+&KXQ_ie<&n%!%LjN1#nO`%M9TF$hDr7Lkz&0K9&{r$c0PL@&2ZJ9;hS2P zXtTVL$ov@n;c4#g>)@rYG9!B%&gfmTE}r0Nh~H_|26&-cKOy6 z5kBDjPT}zo#}M!ZACTwNLN8vTe$jLPok1P=5_QoO{EAZG{Mo}OYU1mh-i)(&E@t!o zXMBDxcm;PfAKlOh@aZ7O zC)7q`I6AV!IU20;0E;@nwf%#RX9`^P6mY0DJ=$`5=>F6~(P%}x(Q8@2)}HXnv0%?# z4mWzMQu?fO)HfEe1ul;a54r3p`yJ!J-c!H2ikFO$+{ouka|8aYADc%aD74bqv z4tpCO%;=gpeuW!~kF%RgV{Ml6F?c_-i@Jv$RrWN@N2#@C_Jmg9tG9(ZwjElF-|%A5 z4;SkNJUzFDOXiVqlCy>i^-FlI?RWrRCl9Li0HM}4P%^C^BGw!nDp%_9f%uhd-&}TW zUV&GtD7kbx&l?#pMQPkS$u1}!43AC^S~f67FujTg=RtPbx<)!0%BXz?(UX~} zg<40c)t>mGtb>0z5~WlhqmB-b7Hb0%G)>-8#87?fu7b|+`&%Uyzh&|%qcd6wdH>7d{>e!ty&&K`&7;Jps- ztUVgT!c-w>;!Yv>i``-{US@$a$v^ufOA77;$9#?4`N~bQ*O-Yqm;6zRx^k}UWmu>2 z9=gZjJ2p))Sg4y`ArI{#yIwBroW}1I4NLH(M_=tTX0>Q6W5-KkZa?mXWNgk=0y{D* z-@%d3{C!le6zG0JGN4a1u4X^u0(H`R^jb^UyPbAY@w4GQsql4sKlU??Ro^TER9y{6dHy@XS*JVmU#p!}^Hhyob zPe{IVPKmxfPl~?(92LFu@U|YDDtbNHC93a|8$EBMQgac%p_?1!TDKjV=H2Ob$!)tv zs+y_R-;8rD{w6|Oc51j%t7Gr*MU=L@ceJkLc#Lbo25=;U`>CCCa2n^_JM1k^VBWL^ z-lgj}XO^f?vyJ?(4--X`jy!~x3FL`V6V0K{KgdkG1J~f2+-HNpp|;@AQ2Mxm+?Vsw z2>l!_R=(rRuR;qTg-Pb``>UpXeYDk)Lu5;P-p|7e|4u*I6yIO0eshpea~&`0f7yFw zzpQp59{Em>vncLs(?%$kx%};Sz#t#=jI-GVXh92pEmp8(j&_)@jBlw;O0rr0p(gr) z>;@w8X<@FLytpbn?cil7H$AES^R4`&8j zu@^IRr@z@N%xX@tTTU)>*?ML+PXAVS_~mHu!O061f+M}Ci*ED1`{0ARQ3IvKyB2H& zJ95Dq=PYbEkIVU)WB?ZD@OYS?-QdI`zE>KfEgC|P*NOUv-LvP^U@scshv2UZc8qc< zWcRh;?kM!t{9R?#IcLad9uTiFYsc8@#(+cbP`@}e?0?h4oW<)NUxP|#E#$@D+X<|x z0R;zb@ku;e?!38v+$6Qz#e>NN}QSG`D6g&bz9yO-``R6+Ph*L_5Wj^=Mp;s8SoA* z5oq-BpuNFt<~|&R%G>zGnZlUO;TzfT-K1u#Wool^_SBZbrX&+tks0f#CN3tkQBcJDL09iH2EX*~Z* z5%gnIgpl`l3Zc2XMdJ-T6&K@=VLK!Rm$8d=4K2|26v=n>0@*Whoa%DubH%GaIbRuj z9lo>JDM=Vx-d3@eWc!Hw*_EzOhy{r3un z8yS+%3No}8<|sb9k4nBd=q&rQ7n8MBa=)@kwWn-W>Mw&qR`f&bw}=ft?{qY8OSfwh zcWUH@Il6`s@vfz=;Lt&MLwLlh{Udax+oNn%!HP@w&Y+& zj}gq<5(UflF&fJi@_A#%*=xTaZ@09?I|F{Y^bL5!e`B?UnPAZ+db2iY@Zaa0DWo^+ zM=exLE%X+9jXBip&qfHf;e$lWbM`kv+2dF~NHHI!51T$jveXZdEQxSLFAtJTVi=q? zSkwpx&EyzQeea6a>07wZf#Hf}6Flfoa15Oyl$uT9O3hAoINGr*l}X+6%Gj2=f<(LJ zHEN+jN%oqz$J%QyjkQ~DvB&T#A3I2|Hk1F4`Mo)pw>whn{mu3FIXJX}>whVI6&}sS zx!}ZZIAU~$#h2*Mz5|c&y)VZ1wul_W0;k6CZ*XETI8;GRGoFui1b4i7?7%)o0yuIP zPTC0W%%C50_Ez$LeT24{O#8wVzNZ&Bw27ZI9w&2WK@CYqZ`6coa*|V>Rp|;&l|J4=DI?3k_!3`~9HkA$zB~d@R;N{`Oo?7N& zXX0%&y?NUj<1qh1mWC9isda~s>BR2eBj&C?;kuIQ;o34iwa^6WpkdUrPCq@kqq<>u zyR7M}R%Q3rRo!}Dt^U5ZWU3sfm^Ss(R!twQ)D*B=Sc&i5w4q96ID5s;n&=nKsb4t9 zBGEg24L<~DU%G+*)R($di+V+C_F+SdN$E3Re-{|T8wUre_*$IrC2q|U3=PTtn+)^yb2 z?RH$y&&d)D^G*u-KBol#JT%P(TSY_kF3~^!h~)RyQPFn_HIcGMeDGp3Bwvy}v{BF5IBBwrrI>TBQoUhuG~pyI=4fb6E7foG0rg`0Ah0 zDSk&5+R#ISu^V}g?mNT~?>(B3(}$(dna8zl40*bsXY^=K$$q?WUiRyDUiNvyG5)md zn{ig~>v38@CoUT@4@g1p?UV!m*evTmTp{c8w}{?dG6bJ^YAifV0xrO{yRh$%9?H7` z{_1cxyZO97Yq!QQW}n9U&8@QfY@N1oY_e)j+o+m?&<&Ms5$Z}(9rc5E3XNWCTpIm9 za;Z;^(^Y;&pZp8g@T)v_CiCkXcIgj;LHDU=>|7^Z(HfrvZx)Uwh)W+?QQ3{Lcnb4~3#_+DjdO za;RMM1YZ3enQ-)H!#=u23ti zp--!#PCCzV1Md1RHPC)A=&LafOAUD1iW$*JI2C7R`VHn{PLF>$=VCm}oF0k)?OB|o z{?YUNpfxO*OKro>1{xtt7`}A)3(CP~IfqnkeRqVDF4dwenN49$I^#7Pmee^M04%(y=7rrNzziS(R zBQwjgSGnFkM89KB5Gwy-zV1nl^n{uq*!g^eLmGTKn#h=b9h}((&ZL1o_vi(0^8F?B zmF>ZrZ~1s0KKBXWQ311ja_B1$kI__rOb@4pN5VU;XexiZJ9&z)$7?Dp6CBlD;_RkE z_BXz!C!35`Xd=1KPEX8aFvy2KXgWS5hlaZrzZj+~nTHnk9-f(@a6bpZqG0;3O9Q2v z<`3lR9v`TcYkR7d57DmHk;T%vueRzyKV4NanFv2qN1Z~uA)+IDIU1i%u(vaR2U&6@ zzWfh?;P5T}2j;iMOL;y=fVpjB1=BG+yEoFC<gnVE7QIxZG|WJ^ME5ZQd?dO z2K|DUw>Q1n3i3jt#)u~O8Mb=2$($QgY_+zJtkw@cw%YH`t<@gh+~OK`1kEAdVBZ`Q z4Lx#2L(6%=@WFA(_u>xG@7{L7Hwcecm!s^zAJX`QrfEF3ZxP&^S4m#qZH7OgZuk~% z=M1y%dAlUz_*BWb1^=*F+ckk+>}M_AfH&e&!Snr3MXz7KlswjLkvwJ|mVNy5MdM=p zMDCv!0zFQO`eghMXRJ^??yr^I>+yYt8}gf(F8GD)6a1#J*D)kRqd$@E&>u;67`q=7 z4E}gM_6CQ1Q^Yn~cSNuBo}d0LOYd8+5PCuN@l zxr*1X#}v;w^kJ>msX>YBwEja@sDAX1o~sYDJD)B4UuRFQOQsxHNrp(@BeKuyd8&6p zj^fuTL-hX%54El7lKZ9|iZFhS)^eO&j$34u4P&3<%k7$4@<1#e>lJg~`PznyNv^f{ zuhx9YIpY@z_X}tD0XZG<^w|;I$H|~5WY4qg?#2_A4;xOE72?_+#Q=N3HA$ytFsm;tu$s_})M6 zFO$KcR9_vanx=)xmJf$HT0S3Px03_i;2ka2B=wf7Bfz4K{iT{N)JYGS*-t_X_bD@c z1D<9lz@jO`l$uAJ^L@af<;;TfBW24-vPI^^ighbT3bp@?x7Q~!J9~vC zM5lSYNF5Xi28A(JL9f(4&gJ=8 z-li@oF!6Dxm*l_T5B`gV6M6q38D($6FMlwKd{*!ak1l#MZDB`hr|dCm5!xTPB5lzY z?%z(|5T`!c0(Rk@_ToCIQB$v=nv|mK3tdNw^sO&OY-e#p=u#`vkaV?PY=!CQ=IHP zsEfSeh3fd+C;W_AWJNCne|9Hos>lYacp0oiLtXkWbMkNDhPuZKWf$TcY1#gHK{ow5@q4I226n(*v$9n!TR|!(B=;hq)9xbBqSDyBL8t%kT8pMqbZxWI=5P zhi-hJm^2?KRf*uz25RBa^y&ZO-gK#-uCmuKT}9tv+R|qHlwKugsv|qMEu3RIdj6HX zeT2Sj$NzIg#dqPvo}ry>8>2D%v1iu_kE@gHD?Uble+L{o1s~*%Hdmn^GeoPEd0IY3V?JgKcc;{-V#5Z5G2C4Xr3x*D7Asz$xD7UB`^{j+ zHeegufKhizYLWAPv-gkZlQvCGBj;StE!TZr?Z;g}3l={0k|cWV=xBuIie96^LAU*q zpO_~5hm$w$N3Ze0ZXs}Oo)9!(pXS$VuV@^Gw#Qm>!!ik#i7vI(qnv9mq5tyIFtx5XIrI1khaRK8@;Pv5-hUoYC08$H2IR}pa`hjBi+OubS<^cL}SEuX{jqnSgS0e(FOzm`xR zI?DVQeCx`fXu0eLSX4<&^fxu?AY!2~Vxgf?wl;g1*5XXB{%`VphiAb&c#Dan^_2_A z`RCHB`gN4pG>f{B3pjLXfYNC0p|5_2H2@qM3~%gFf2H;lG|2b&G1UIv+fcP`kgir8 zX{eCM$#T#@w=m}@M5@%vnbjrMo(vAX2bQD3`)nlGv;fVh(d>Ei$i?nOQ6J*G`!`&w zb7<$`aaBJX913S1Qjdpn1nM zf9IouSIAz?>yv$&_l5(4&)%c5_c!Ei$H|#C!TrhEs|8*-AmB$P243GT1)N)_dWNo5 zoiD9cox3kk-Q_is&+RP1^W6iI+h1o??-!0L?WV(J_P`@?)jr8f-Xpr*IV2hPpOt;D z98-J-qZOS*4{bo2h-Z-$@Y8lNp!-hI`(U=@*$LgU_ItD-L$=m_c7_x>2A^yJp3&|? zr;sm>8QQ(JUk!R6-PfUqRS!r1{-Z+8<)uQ+`HLfh^F(S~3z8I{58*+5OHckPb!hpt z9FUz4Pk_4n(+nvD4J7|h_e$PZ4k$+be#tm@ujrP#Pt+T8CHu!Is#)ButNVa>$R|TI zo!n(>KD^y(zPn7Sdnw+j^8QHY>i)ygejQ<`&K~7dQ3eKmgzsJ*`mNoG1Nz1q%KnYh z z*LMj{H`d5%)d;0+ayPmD&Awv8XM;skK0W(iMkpr3P_^N^VY=FK_R9NVh8k+@b!lKv zIoR_X@euxH4Rb~*W?h(Kc8-$k7ZCT{BnCP>(Pqk}e%%|dd-TuhTNC8kPQ*SnTz_+@ z$ET6o)PbK@;7y_9Q8bS8yF0x-ugvq4+1gbTKf5S=`Y>leU`BoUn3qm4$d%l z2BtVY3!qkn7ETcwLq)SV#)By5XYdh=7J*$oz^>(B(erQ|yMQg|f&9lON(MjfalNC- zTKO)qPAa9GuO+LrtZu1-u`ekGB9=Xf>3Ns6J zf6)^d&Ad$y;wH51%i72@|3pu*iQX5pWmTKNpm5&zW4vKEQLlc7_jQojlF7tFmx!NS z;EIW?k^Fp<=e->()n*cJeMatWV7|2%`QJ;iTHQ?6baXvtf_k7 zXjZkOKQ)+sLOeLM5*!MlU*E*6*`yJoxo>~je7n1@u0I%524CzHYeZkXj{2*0x6oM# zVYa6`wV~PQ#@-6om%bjZRGs8JdLLbwGF~frX9YcxvRpp@x4|Mb3d-=3t@MXmnHDA0 zln@KO2oA-feXWN>{UKUfDb$CgaJA-T^0EG`NyJ0(VA4_ItjXZdq*E-<*{F1CFD?Q6L4b zJtYQbpAmxOGlKu$ej(5U&1-V1z&f~q zFQ&>qH#Vuh&z35_4QnOuwMnAeOUaT`ZLZ{2gLcPGJnn~}NBc{m;#+b`G5VcVz0>du zlFq6=r}Jf>4Oyb!lvK%g_7=t4X^ZTgoGf`4Cd)=<;X@Z>XdRl;w9YHjr1tOZlLLl3 zbdt{MLwYc?s_ud7ouPzG%7z=XUvjBEDyg1_CHcE!f?9A;GelFX(q#)iC423@f1VM2 z1JR4s<8!tuO%4s*Du-5Pi2;Yn506o!`UkD1Yx_jk#d{U${VaXU|2C`jy@-dTWTpP^ z?V{46H7R>SFW^QAw)(bHfd%q})rH~os z{N6EAsxhM@6W?EKn%P%sOz)}Gd-awZJ{l~U?+nqIEW?}|!y|M}Kh4&x5o-n42M09I z>*V2YZkAkUjfO)84!zx1t}7;&`)8EgQmaG#lm~EF@!k?wBKtoz!W%`KCUJ{cIJN4&;E5m0gJU!iBT+xD9zi`PjW~wi z(P`C}I(&xy;Pd?1QFDrQdU}{+d-M3|)S@0yi~cy4d^px_evp7yD!%rqWAS=L?{+*o zyu-nvH(9CVn%{#t|M{}QDc#QdJjje}Iz7a$yr*1nC>kzK6S2}^;==doO}L>?IS-6l z06vF;b5|U>A2skUaE9i=q3*<-|BN`BsXE$b{xaIya-7*5=2h#j(nEN{Z3v5;x%PYV zu~pRVhVk>q;c$kCiE>6r=9IxwlXG|UzI#iiu|4I+WN_$D=6Oys&oh^}sK-Dwi3cmS zz3^82cQiV?;kxqU#4Q~ol&Uhk)bglR-(j8LJNSxo?HsQ+njV7@jan~aA{qYB3vfe{ zn8$V}fByx({Wf?hTd18EfkDrx3B3ajZDB5HA+e*wPdks#KMIc_+XQRdylIxU%vl!e z-A~%2mu5=#A6ANT+hNIQy@sYCwc|&`LYXs>?U^Jk5A7R)IB#*1MR><52s`bIP_1VQge>{ zr5P>uVEUh*!3TRBAv9eXqqQypi`qw6t+%NO%m}wP?L*_|WA-3tuHy%s7b(=HKL&>` zu^-JCBAfR2l$y`=l+8c&RhzyTBsaY_P;K}#TxxYq5bUErZn3_zs!ceYX>$(Ev%9C` zG1qgG?DS29WOaW}s{6aIRF^qaY=|9Bd`(OgIZSSXA8YDB9OTKKTLKSLPyC~dlAF*0 zF{3F_{|@}3|1l4;k=mCrMl{7H+M91pur@1T(L7=Yd`9b6a?Lqr7$zhrH34vmhk`|D z$X9eGFL@OVn8Yo-@Ujimb06^YQ06{^h(R2)^vhY_l1BxDBMy&yXK?5|m=i!f=MGqe zM_Ez619KcWL`(!%iyoVycmi0}lbGoPe#Xnbg!rjAgXertZ5r+*n#)S%6>=`-H_Fia zKuLTu$r-z_)V>EGY^pZn`_sdB8t91_m7@4*CroI)KiiKbo|j=*_3Zv{@p?SXx(yX%79B+TG}|c%u(Bm%U&R zG0pcze%vR&YIF@ zNtxcNy)nxo=g+e#rWK+bOwKX(Q+i()<&g5zO28-NCoyNlkUxljUMEHU}8It$)^@>}&*^1kdWui})?SfD79w8v$v=Y39V}5&84GP1rEcTktFA=@# z-QZG(bBdoHefwkRq>b7qc$`E-Y12;8xL~OgRKHRRsb7s=;!366ko9W&l60m0%ER>e zFRH%dFROlo(L}-v=*2BbN~clDlK;|7$@BPr(PLVH7!t%B=Xr9DA$Z z4r|NhjbgpaY_;mb7^li&a+>#0HPg7Uwx-*}LoQ&@&N!|95_(s*Ne z4xmBg(8xybu(XZOVlbb{QQ|u^fJ!HU3v0n1J?mHElf}d&jy{>A*WLoA7{Qk%;LTZd z&MLv7J>*a4xzABkng9l&y%ij;x#l42E~`Us_y`99F4D_aAXtS!Ob zXhD;xVtShv@ll&pI;TbGxYFuUO5JwdB`LW6vJ~?2Ic7Wy<$$$?V&E#)E^sKnQ1Z#Z zOS)5m6x88_6l?^Ga?dEijhFDIzpRGD(lDyvIB| zg68j$f_~1C0#dfizWU9I*K^BM4{@ICWnU-yKY^Ql@T4604_=EISJdE}S9Ad-S9E^0 zS9N}0U)1|OLZ?h9gbR?5&*FaKBJk*WJnF+%;8(Op@m;z~_UX1%@rzid1|Cm>pO~lk z48NdyT>4z`dWeQnpA5C*u64SOsT<{>X68}qwu^p?cZ;5#vSjD`+hniQ6e%E_`TDOm zO1@84N`caHxYQfOfGe4j&+r`4^F1D0y-)LeZLj8bB3o8FZ&lj5tW)b7=##mp^L_8M zwT@@LK4pVunlnLPmN3Gp(mu>k)q_6${cuBBA@k!7&hKgV=45av2n=eMsMLNpL9YAh zW2sU8STK!C5F35*8m|ebKQ`LlS`=ot1dg`1jg7Xob&RvN7P3E{#ox|~elMd=)lIGL&<8vmOpS>8rYF7o&>=c}l-2svaEMI!1}0K}&=LRa9I2~I zCEqLxGgPl0p|4%ao_quxddT@W$5B5SXEVRb`T7vv@hxtjfJZCXLwhi%zLxyNiF5K7 z>YdlXi8sj0PQXD#C#Ym5YY;KXX_g%wqlbsc{1?lQg?~*^U$}*S(N0h8)Uva{ zA9O^Dt(@Zp@OwX)jfkVvGlWWc)uKI|0 z=zlyn4?I1Op6e&nxh8`zx9Q7bo5V~3)U%_hP45B2MB=i?;M8v7p{MlT z95b*VP!C@O7Cna+-8d)>s7}BeuJ)c5g4GVKSb*1Nt)C12(nwMFYLRTGKE{UjY|` z`l3=(hVIM)YR;SB!Lr9ybmcSH$%30!j(>G+XZD#i_QKjRXl$byI*S+I?Yit7&A9E781(aHF{JLC6!cu76kt3l27G%$^#7?q^gDGz^c!_T3g}lL1(Y5Y zgZ`mzeek&Aqn=d!FI>?1-@YLmbw^d#jBLqcP`2pNoGbX0(bu}TOAOx6yhm)V&~9nA z6mkGx)um~2K*V;*`#_THb!o9|{BXJGJ7tIFKj*X<*!7we+;U9`?u%CIvriTO9@pfc zBsCpl_6wEBWopk$k<#=T@xKd0Ubc@5D8-F=nCaZCR`fJhxE|y_2p6 zE!wU38k4W{Y@eh0eVL}VFW8`V_-?)2J~ByacMZ+i#2u3Nyj02iC>YkX7On!g)3#Cc zeRmmpCQI%9i&CuK-I)Jv%n{s|Wk@c2vPIW#_6V;1vSm4VtJ<=5gRb5SkDw7~p8Z5T zbT3;o$E{QnUIyTaqz9j&iOi zdJkQzdqc^qdh6=H=%+Uo4^x}k4L3B##H)4BZxN-_y9Br12Q)W%r`6354cpCoWY@rb zs>|cul56-{O*bxFX&v58t?JenowDH){2;0R3bBtv$NwxC^zKNfN)cX~8LcEgu;@+V zpqZoe^z;oiUlA8AAFY@&c)SboIhxdszmK&xwKC7Ki(1O91iR@C>O^DfIF5sJ{RXjAEsu40wFZK5)aHuG@ro9L zS>N)rmHT7ylg`77aUnmGKRNW(O6tKchgZcX;C;I&v1U`WP@5mE)r?Nm@E*}>|D;ED z0ce%B8sZ|GqLHM7RzLm%#M>B#|&%mT74qr&}uO0No-Wn&=envbsgV?7U9CGN) zkAu(nkaZD0QFrpPIjquHt)>`0R`&_5t$oM|Gw|rPjTGuj!sMF$^vjMA_aqVTEP>WB>4>3){v9+9VcOwP0WrSB5_r=TfxBu5PP z%9et*r6|F_rb>Yeb3`8(Fd$;3>>apLasM_;^p-9Qev593K~so{?q5&?`dpFy!>)=! zD=)|aZg8Ys@qWc0)_-xH=oiMk$@pB&e|?H#oCfzNd!ypsXNBr5tybDSw^iyqC|&9} zX1m%>%2eAmuwGAB+ilyXwA-{*Zs(OOhs;h@f)Ayue$H?R-dZIG99<~~t=%977i*F>GP^oU zsd8N|*IeEpm=`UzS zOd0fMrc#s8IKQ_tx4WHHKz(2dzM@}`wKuHh+KEOxH4z=>o78k~IDDCj!(AO-j$C)l zeU|@0Jj5JpQ8GAU1`D2`Nl=++fOlvpB5y6a!fIjGl^Dh0=Tq#MIhzZXP(LmD7yceg{JtF=(NR9g5xYm}ESWENCG1itAV&$15q`I5%(f-E_*>t$E z$>^PxlBbn`0}cN6wb43%<}z9yxoQ?*lfwbs;AshtI9^)zwN59ll; zv(CdqDyP2q8*{s_MCi-zqH)V?YefYy&{J@gI#&5I>XBCbpEktW+b*CpR6{J}@UHz0 zF459RT_xUiHK)i4?bL_%u0EVuA^g%D%b&`0c@DAfmo zL#31L4ZWvpO-?hk=Iyf$t?f5!9!co1?z)7hDLL8GW1`;!W;=Y3i{66^nCmZ98kYj_J3im6!_`} zshuua4DFaK1{7s##^(;$-Sg2`8*{+!<-OPLQhrcSx*pbq=Z=WY6LO@+Zb|xD{T5x_ zuI-ZPSgvL+=3UN-Eqo`3#yFMyKpfN`uF9!+r_xR13}tC(5Z<0A z);(MT*La;|@>(WZm|?elA0afq2=@3eV=;AvT633qiyD4k&g}H9F?P%SN!GT>)9o$0 z;ziS+)RKyail!^Q@Ixf-b{?ddzZj}FZyTpr%hEMz#c9zw<)GyDOP1jJc9z}w^lriB z+g#cCj~tzT$#xg@-b#bgak$P*4YOwTAgwW!nWZsk^L;_BJc|E^XIaAzw25wp>1%$X z7GwZ_KIfeKll<%-asne5^bc5d1sr-CP8)M(rkSk&5eqq^`;kd*_K-gP32IvCsn)(m z{;`DIWG&dh+-ecn{H%oQ*c-g?;dgSc;tlM-54b%J4!sMeMDz1Q_&#Wul|@iXYJ&63 z9Bk1>9{(CKPaAvodT;=5t>OyuF`oNu6&UiAzdNw%5PC(_pohnWqjZ&_fzwT#78m-&CIFn_dify{w!8k z!Td5)bW#W7LDkB@oA< zO;r9Beuh$4C*%E*R%+}`-jA(AK|~SI!0-F7)M+*Pi=gEf>ZtD;d)bkH(l+4Ub-5wx2`$? z{5dsHse6w7u4b50J94yA(?~2di5b^-nYj;!oAw@`f#bDZ1B2SAQ3t{)nG$1bGcnWJ zKwgH|Yvr&gLuqQbzWj5v^6oOz+AUIF?FNSt?oqYK%;PIyyes>N8=p@dpVd5iGf%k1 zJD|#Yg4Q@>YFqP7v|_tYx0?sf658&3B3feC2%g^_mwb<25CaySmi+b{5xqAZ)Vw}C zB6vSy_G{yDDWL3>?Dy6Qosa#P?0a#a;QMT^;9CH;yqqT*ZSZ-nY!O zZ?n0-w$*NYX&W9O+eDAc8KOtte$o9zo@_k6UTv41B>Gou6TQ;Fpa&ad-)@^^Z(FY9 z`QZ`8I3LZ`Ur(q$Xmj}7I;8V1Jft%&KdO5DijRHuLD7Bw0m0)2-mG(psoEDv{;lYf zjLp&lw`PceaT#LJrCrR%IyjN&h*a;CJFMF-ci56ChbE-T?d!M5?H+HXR<%_Q*}O>! zc3ZFbzX}c=Und54eU8G^6Wj*aEFS6zzoRBXzZl$jcHVol37TCq4>2imwy( z<@!iys9!9YhAh{bycTMf_3={MCF;tHs1a`+DmBa;COdrQYknW8R4-x% z@ybxOW&$&fqv?-FqiZ)VUTAHDAMHWDZ-cLRa-d>*j~>^85xS;tr^#Z(PEEhyoahmI zK=inhA-Sqqn#-Hir*Gs*PSbZt&QsD9=S_1B_Bq3J4S)8f7dy;u$`7|Uu4IOC2!0Ng z%un~A#_$K6U2kHa`wlHM)&P1kUD^A#GGoRJT4N)Bn*k0@VHUj`^JKf3L2<+T^y37< zJdk;;>&#{1d($)%PQohs<)g_{y3vPOP8<`%wf>0xF^AO|jFCA{?-HxDa{EEF)3ayP zaeTAEN`|$YTweC3bv2F>v@Z^F2~3wht8$WumA6EeYDgvVO9G~V8LIH9e@6xZ6eHKw)s^#rTgJB1ZQvU(|y?OAEegR{5GS6`>0zEq7ztOz5i+l!MSU*y? z+UoFY6o>(?=Y+uALeY2f zLBV)rzh*RY4fZ%LIkZ>(y1+L+RH*p$KQ4QH&FonX-06NtWZxf{MZdaV2)Lgl1q@3P z+#Y1v+>WPPd@gRV`M$MI^YXys%XORP+;_L#@L-4Fb$o#?@a_4EuW7C1WoPDN?poQ` zb%ku)#mrjFUcn8&X=C0Q#rp;3y7CXJ##u*nUYW;LuhIg=bL}z7Jq2CJ#sgp&+8au~ zI?H{-@uYquQ|_FQCO`j0n$)RoyA=B7b}_UJj!}HF6f$R% z9DIG98gPAuE+Ar=5^!?06mV^m6tIQ4-XAl>;3#z9-09&DyDa(*yD0kZMSHgJfT-)7 zCt0$0icRw~Wz)1|U46%GYTf7AqS3pkq?SH|)W%TstG5vg$?Q`_=;y6R z%QJGQTD@(UuKEbQk{Q&6(48_Z!Z*X(UtjmXp1K-G-v8bp&AfD|*gP*>vp<X~kLKeR)0>$gX6uF8<)Et@1QW3{B+Oq4CB(W%WIqBVqs*-cCFYtZn~ zdkk;8-Dq3WVt52mQBtErS9BpYiO*R6ocGh|8>2^5zYq*^0h_YX)J7kyek8gc0-E@G zaHt1$u2M8ZW$Hr#u|m@w_S7}hD0_e@pAvVtqQ`s*Ua`h`8N(i_5UVT(f3|@S%-jE` zDYS$8s;Esn@aP}nNw0Y4XW8*CPo8Hk^#OJ2A)Mp=Ikz1>w5DV98_t{tbNV zPW*hHW48wzBEc2M{F*mCvZt*3#7yPHQ$G@4p}|=67FhH=d7jGN9l&d%W?uFIe1dM| z8sEa#S<8FIAFOdYInge37{|izTS31;N6zKJT*eh@&)Df;@E;F`}b3U4zEZGj!H|NVJ(6!JcukmZqe5OVi$Q zt!>L=(Ro5&Vcr~{93#|S1&98Kv9}C}6dHWU%MzKX9*{oX#i>-YO?#?2{q?~1jW`>M5qVaX;gc~i3|)d({fo&;upSJmTBY~hxlHf>*K#!=c(okx#s)c{XsaA3rbzzBGpSb} z5d9L+BpF06@Xis%BV(5=9?BM5-_MdvVv17#eUh$rSBg^CakplEH&<)RS|yvGa9)-S zaVniP!l~j7=Im?GF1`(KVlFuJ-8e(Ztm#VKq*+qqpjleu5^yMdqEfpDJ%pZc2lMG| zY(zi$H!#PYb#0iwqHTn}A`jhjW=w0lu(qP-`3W=h(adA)=%cR-=&7&yxev2LgJjdL zVN%n$NWrH2P_{nJwmQu^Zu1zi*Xq?i-Rj<)qdD)`rx{LVYc{`)Qd5_ON=y0}$ubt6 z^@Ndj)6UU$b1odEi=(w>{H@F@BkbmtX#VYu(oFNw9bOk9nBPZB>qX{GBzSU@(3Nl} zHo8e|^aJ+CUgTqsCfFL^X3uX*kQx-cJ96otEuucu94|FE!5!`nR(!zq{*)SY2)Wrf zVh`qYOV<;pIQm+9SQV_}oU{FiHJCTWF9Tm_dSve8P#MHGRq%WAc>ZCRcFOvGGGIA}1{F#jS&H)PsfR zS&>6qAMcJ*=F`g8!kG>Umm2>Ddtavx(?TBfH{Ox{%0hov9doU4=WDacYx)ywlyIGU5({0VNAHqoZGJA%YVJl| z=t->2RDlL;O+32ak(SoG5qPbl>G7EF;8o(E|G^(R2QE(I_3Z|SdPGSLotYO5jFIIC zcrY*L+r2)_(mYmflnnZ*;80&fGM5`UwLL15`0yuN|(4_1RiWz@+J;@SFFq`k3*-q}^WhN&f0pMbMC0j%ytT_~0H z?O9x2Q{C(!-C(20?~iwG0Cr<8Q3WNzJi&#f06YTE8w{7 zJL7`l8~mB0rJ#G;X96TmX4awT5Zy&W**S>6n z+Wv=)YWt)uYRHfHMHgqw!R?ckc8k_4K?^o9OGe!~VUrTjvPub@y;=#X-lznJ!7*C0 zUGmv05BRC zNV&P&aKThbEVL7y;{j+`7t$Lpp;m1gsWm*r7idqkz4_Z1Tg##ddvi1W8TMqnY2?O> z)P{DUUwxhatb&)`Ux~Knk@!^IhOk&|)6tjwJziJoOHV6;Iy1WT z|KgDW-$z&0pA|`*qGxp?HgN|_&_FBR3kG!nLwZu1mgv*><$Nw??E^zZVxei^6Ivi8 z)SOHAvNGwxzsmEz2Cr^`L-fv`d`|xMJ%39j9y&+tg2!}8a-4IKEBYY#-WN4+jPHnn zz{R2|92*VnXIJS!fMZ4}$Y|HyV#wJQE#UqYqi1 z5MOlxLxaGv5aKLnj`Jh;jUo0bKu2jZeLXaY%El2pO{OnDo*HipTq?9aDg}6p_4F1e zk8iWQJfW>+1@-*_v365DUb27S;b@JtHaCtI%=h6`vJyN^?2m-n2iGMT$?qGuK`P^xvz z<#$D^BMClrAR4!+_>S~L2mL&ALJp6kN#xPz;Wg#+UG^s?tKoHYjj}ghgGcSdXHd#q z+b;655b%gTXpJv8vj}9XyMM8Vr_CI zCTfls+WL=^Y{M5?rN^ml&NuU|UT2SM-j~rqi^;e8c0FkK-F8gyTXjnEk2<0F_#KkH zYth~bAs>4SzF#SI;zNfd&tn&4(=|8UMw@fXD_j_Rkc}uj5k| znkt&Ta%|SeS$2Ee9J#R>t%Ha3LKlN4e-gVsgTrP>aDH}vob%I*AL%Oc=Hqd;La7g4 zrq=!O5t=Y#;k@IcNPnWXiaqxl7(;Ai+CnZc6Ktxc4~x!t-Fj+6w}^#yayxP-<$1yZGsFxy2YM+TWh0TFl$6PC5BjkAC}YUN7g7lkJccF;#9$OP1?z zuMz9prb#Uvr!{c6Xv(A?hnF;(HFDh%RwA|PuGEP<9WhZPelU@?)_IZk*6-n0+Q|b3 zGw*Q^y@|ANwQ^siQgxM?xP&-ct8=`qc_rFd!>K29bo9vR*SF*6FZns2nSi0}iRc^_ zqucr{keFr(F#|L0=rE$`3I@3nFLp^ZlpF#}9Qv^AsrX4d6*+j&!-$_8yrNieXc$=L z5>IT!^F9MJZgcHBW<=0RD}DiN`h#nrna2&}_a_`Tn#YxZIj_?5|C)G+{(tfF;Lc-S zhYI#sz@gjx?x+uq=QuwTWBtq=LNDesw-WEYNbV8EUJ!<_+y-jae{#J>gE7PTzoq0K zKSw)1bLd2(?O$BZF?MjwcC2@J4=aiFE>oNOBt}=X3hyuSu=1cWa(Oqdb389AjE-41 z^k2V+mxDiZ+wBDS=+ua&fI}g%*5=X}ThnjhS|fVXji=!TDd7KP_**0J!21q7Qi+Wk z=x@$J`{121n)Ut)F~A7_&*`cZ@WrQ+FY}%rqi3tm&jvcTl)mL$bq)UF75es`1L1m5 zgM2cA`Vjn~ub40UlR9-Fb)Q|lwrA9aD&fk)$E>CMGh`D*MD1F(Q zQHF{k)Rr7Lv>VP48h;fpQ@j3@@7Nh^E?}=g)1W9EPWxT3=_RmeIXGndK&b0G)n=YP z!QRwF&R!BLw0$_+qIzz$7$)RdJkK4rc?|%M68Bk+n-AH&I~Pd4cn$i$a!U1Ec3kyS z(7gyi_hLkb;D2G8=zVLy;M)5VdDdr=cls^SZ{inPfaR*_ee8_lHmXo||Le49Ogo}^ z&e^ZIn)Ya3SDA|)l4mm>$hLX~WeDz}nS$pZJ4DybIihp;4#BN-w+43EUA*$`u7!9* z4?HP*6&{qmo*k1tT`mj8@(Y^ZM)dkSWC?zSS=7(*u)nm$$?NNl29Kvnl6%ljyU$!= zw3b5EZ~1O1;K%)%Z!h?n4M|Gyxs|%mwvB3sg>aZU?HB#OKO`9I3k2`gM@7Fo)Y~Jr z2?6KUia~K}rC`sklK&oj0G92Nyf^O?J%_*7Wn>}|8NY}!Xl#irBIYK=Nbsr!u>eol2&Hg=e-uSIj_zqoc7+|&2a1$mpAOF#NiUyy?}llKgzC!0l2Bn@A@RiosZrCbj` zqaB?+P-&>{E14~Q#pcBW#nu^vWOLqdsqI0$UAK3!r1Z_Px`rOHd0agrctsx(-O<<9 z^-GqU_ivGFMlF}>A}7c#Ka7-GnI&sTf{z`{nhHOzH#q1$T5B3deF%@>rmyg&xfo_| z5#d$lMc7-0Mrvj=vHT5cw*vE!OVJLUgwN@7%wHLa-Os=+*&nWyHE|w?64!5F_5^NB z^&M~sEGfr>qc{efal{LD@Zn#s=Na7h5G+^%X4H){6r-*9>=8IJn)|o0S~!>Sj4u<= z@-Ts4U3sjdx3!Ar?^(<8KGS3_S1u{gwZN2Q}Vj;3G4vkMY*M5~iy@iig{1;?iSWb2;?Kd$Yg5 z@gpB}=txpehr=BWR{qVp!!b5+3<7aBWrH$0d(N3#4MozYr z>wFgd#Jll!vnA2e+C9_5KiT+@3(m*9ny?JC()p% zhd=wI8ql#o4ah~y`73ne@mn^!qzE3<^XwiS&I?{;V9%w`1aH$#&F70xMW2yp70)|p zvHG5qJSUtNy~bS-y&_J?Ue0K$cA#$hF*vuBJa9#p=6Nwwbi14-x+P}_9_9?qn7}bL z^o{;ucH`Jl+4IOD#UpsP3y9g~88J}d?)%!ZXD$?cz8q6v4pJU_O{zP>4PKpHb0tFtBJ!Gn@Z@^L}Ea?WN?KW=UNg_?Bd z9a4)YIwkk8p_>2&{Y~{Diy^`l&ZIu zDs|E#$?P*zHs4E78&5E!Xr>m_9e-B~b0C$R^M7;Bzs7g_fO+apV9;#p6OP%9A^5@c zrG7nks8Vn2r`F!+i8f+ysi|?0*!nK|X8lLWt@%@B-4FBxYWLXOyx9W`M@3KTVcE53 zmQLxiMro~Brc}4iQmcL9RP&<|iup0etK>EK5`TR_4R!+l#HZ0;A0A;hjloOViFuMt zu&6J2Szov#G304$@t(eb-eYs5*pN&8XeRzzck%lhOC4bm{rgT}&wS2hdPn79U{MtD zeLgkn>ztcwxJDg$*L`B01>`kV{N9o46;1r2*_^N4!JtEM5x*q2LJOn_eot{4`{|e9 z+70j_3;bbb46gKl{OV%I^QkM9!f7lSLws_I+i@&MO?n=;^zR+D>|zVgdx4cm{x$^M z`j}fc9y67{M}m3L)RR>HKY3QEf%p3d>Vs;GOtO`L}}Z0St?S65!|CGc+r$KDH0<`Eald?q5Z^)r}x zS&Tk`d7QPmLt5 zjFg(3B1Ox0F=FeDMA1^bS#=wFSqg6bR0{b4eXr(o4xXOi)+tga=DzD(_mKL~ir#9q zou2H55mNm!&NWYPXfildh^EyW%-rBbS%yzp`CzneFQJ9&=#?4bY;8kht(H0PXiqZ7 zn@7*^1kXhazcz)>=*uueMI?I1#FM2Rd9PW#_inu35qt;l60^?W=gr`d7dhG;-oKGN z{i|5KvtpHoV~LtACE8~H-`pnm)>OM^*dd!o@BLP{3;XS^KOGS~kI=6-FjpUp9?|ww za?ls>7tuiTeIZ-$9Fr=z|C??1tY*J0VNSi*SUh zNi^QQB6!cbC>kTrh+bU|3!a0(xyR&Q&+_b^r*~+c)(p|@g%su{whF!4Uy?DluE1(|FBZ7JVv- z&4!*){m$=GJIC*opC6yC1cp$@E?usMj##dA*u7cmc$z+c@k!19rIYr6y~i}aZ3i{q zsaZnc_HANNUW(}VBJohpdh)XsO7J~LKW~rVw(N-LwC$ixx|naXDLMAG&goKf-%X0y zx>ag&!@IRKTeQqrscZT?L9IPDR9CZ*dE+to0j(LUtA2U9QoCioT=maNrKX;EXwJvF zy7?3IjyiACWokjohZ-tV=`{mL!t!hrzc~XP|d!>71J!@p#m_7S*5x=VBKuu|2Ibq4en9)`js)Z#^y1$|FGvg zanNAul+1rO2Qhy>7XEZxq+r_4b+Ve;COXc=c!uDSBQ)%%ZoQSgl$nUij_`yVsZI7F zF7Lpec$a+YTdsW**ka_|{DSk8d5w}sT)*Av)!#z@%%S(%gB;3J5Y zYQp%I6)W%<|KS*Scn&jaMa-BL7jX={UyIXOq5M9W;58ejZ z;0v$0$c@*!p4VZbZtl=zLuaITDSx}~z#tYi_Y(B3i#(Wz#ZROxmDLYSev=;BR(fbb zWWPa5Cac411yc zo4jl%eKPc_TC_N&^{*LrscDPi{``3*IQ6<5Jm``Vn4T}XbWai7hQ})MbMHCVdGs(; z4d|oPyfs8<$fMRc9$v`8Fn#&K2&WPOeS!2SeaS;`7;oDOU*0iV3Rmb4U#g8_W{{?UH82SkZ!J)%^ziE7Lc*~TpBMu(_Uo4FO zBFDx1fP9phJND@z@o;q$#ioN3nkA>1ZB9-pn%Aylg7E=ZbTZ%WvE;Dk)qGs^Jq(9Y zW|kwhPz_q0{dQmT0lW8Ea<8z%l4sI!$z#C<(Y@`w z=zfz}De$uBU2{P+_PeMV@1N0(>yK#e+Cj}D`lx1nX`kk`E=O`5lqz|AkS-ZhlSS_@ zl0{#S-J0Lc^Yr8{%Er$RD;}3}C6AZzExNHo@bpg+T<`3(yYM=leGBEFgD05-ImxX4 zS=ImXDZNiRezCnX)ffEIl}^1g)R2KmYDeP=wd3ZcN{8P!NudW02)-Go!8_LYQ-aSg zhc#c~q2O7Y<)Dxx*%-D(@}0Rx4w^9s4S{8%$MST+?fM?kdE9;XNzuhb}-^kS3o@^H^HOrJ{HCkWuXrNQ|ZTJ+$arC?<=xSO%CdXQ?R<&QHui8G( zQ2ED4y4p`-6tiuJYI+Wjg-s(2<#WijCo(?~$?fxKbej2j7=EuOz@`s~pXcGd88#A( zKnYCR~ zTvqC>3qEjVCel!zO{}$w+4(QwD($59{1!3)QT|60I8=eI=z~$3=}N?Z{o`hRthMD3 zJ>$Fx{PyqtYW_w>0z7Dm0N`_WdyaDp~R^x_%345I~@rZ<0o)& zZM<9#AF+HHvC&sNe>6B2z%es8jt{XOF&R0izQ`AR3gPV&T-GuT)~XymUFtmU-yU}ezq^vV_NIRI4I0$=N17&sMb5F-HWM>u zBjF#wi>|06H+w)`$d~$A0q50sU{Da>6FrAghc?}d;7~a{h&5x?%80RQ#Z2~*#qh8t zZo9=em7L<^$3)+IM_-@Vf+D^^wheLL^w@%wV{y?pcr z$JAigLS4WMr&a$y_A5a@XQ_d|?2(PNr&N#rXwdp@kvn|GY+0|ZV&EIpsiF%-pBQ*j zOABO=;rK%qW@G8tG=0sDIK}(|T4O##4dq#A3r_@l8i;}B!%dyRIk$#h#80E0${vD69^?b> zff0Gc=Gn|Q4n$XaeP3Nea38(t)=3!>#AvPc`ZP(Jo2hx;!*8V%9xM~`H0N#G zMd9Rn+14<}+2k4HRMQo|=&nO_RVUGU9ff{J20Ef}bL%ID;a^Qnd@x=dTbS1_A{UFI zPMRKPZH}e(GnIMzpCg6(%fvg8?2(VqDS1GBdIq}9ub~0)1u^_Z>K$(E@0DnxO{X8^ zA4%*1#yq3O{4Uq0!-EVz4Tr{PDLP}Nj=uYM-~%-x_|y85u{_3u`vx)hQNiECcpS6v zCBs;^`CBv?k`3nk&f}}NRwBWi?}>5VK{JRs{C{^4lTimPJH_??Hjk^}+Rr35xd}#e z2H((q{MX9;Rbb(6aELs?F<*v8ny%y~^{2JWbbi7y!-)q1@X*~ve_#!@x)-Sp4}mWn zho)~T9KDZY^yR-K$Q8|rQso1pR*RfF%G*9ULIGh+9Cy?4J1 z(R08jI`<#@JGV4P%kNgMP$WrK9jjDdKu=-|`;8m#?GWGHJocS;d9S~K zL&wL{dxN{O7mO<9Z(oDkZNy8>F@~~&1hwk<@k(v*G{N%ud`-B!$L<+^PV=*z7X0p> z00RmH-_^uJZyuL?^e1GWo9LysoKXW(kH~>ua29{uWAlyKZ}Z$uJ;#Z@aCx@X<59NF zI6hVITACuc&7cPsve)KrKB;*PIwyGVyPz3Om+Zz>S2UlgS2bVTWzDza1;Hos6k0@h zR)_8rjUQ1DT3jHxl^hg2KRbx7?0F$%#x*Hu?G-s-<#EMtE%Wtm%x7-M6I|yW7TnX$ zYF_u?4?I3B`|Uff2FIOIL;9UkeCqLV&D*PbPdci29l0!f;6>*3X_^x3y+Li)K1mMv zjoGyP!&1n1hoqoXuUa+p=8t&mre~ZLRDwXO2PFHQHJHJIo;U9;X*gi`BN! zR6$G5wQ3EAt@f{u*(|;HXl)6(TB~=q*19Q4Y#umYHbq5JM;xZsXGZGqLey6;m}01! zwoIx2b)DSs-D;)&&L?Wkj41{*iIt{#@R45}uCF)%H+}&eu^;#cA%zp#l>lWtWI*m{o zD)E9C5pFmAImX&_9vtey$sRZKio%-k>DK8Am5!HrH_|Bl$?HE>Ae zb~_DIXD@;$j{Vo6k1z4KcpmpM z=lyVgKg{pmV9^EEWA3M3UF;4kcND{ zF&6!cO>to7i1o@HJ%c(vQ|5+C zy?DQc%+`zmgO0Q66ZK^Sxpnl=J|s8Wo2XXlCrEV{ris?TIhrn>IOEsTqIc>!!EgRK z(f?~|)OWz3hj6Ga{V)=4MV2oxVvIToZEa?)UfFjQbATeJ&MfUX`Z=&u!Grc3u$zPJF8QzJ6Blu^$zE zR__;lRvnbQT~ElyUMD5bqemsL0r{Qylm9Ba>Whll^cFDdm`()ouM-(rAJj(iI>VkdN=-WM5 zq6V&Bt%R)HqI7;?v+{zxMhRIsU+-s~ulL!yO!A(%g?uzaaIM-bORE;?l;39R-QW3G zuROm|w9MM7wT{|hwe{U)vv0|@TkFA~o%`%9iMuS8f^>_m_adnY|J<7U!_->*=qmpj z=T!FgC)A$T;mN#Ksec7Lvd_|0O-|5P{Su`#RiJrzaiog>uD*O_xKkN(?dA8``@)G? z!l?m13e#17$Nb?UW~}E@7w8IqWDo1L!IG)GkKFhJd0A**=lUIF{4yXY~R4_$z9Ivj{L7?;; z6-%Y|U_tv~U$jsc9d9%Bgj<)v41F>xVr{-mjpP7k+Ee0zN zz>O{iD}p&Dkw5a79&xO`& z2QcJE=657>`3TQ{#%tkQ=Lhosme@nt;FBj`^$E|t&TBo~B;v&M2Z9$s?#833M(n1D zI8NbDd`30zzr$zlA?7rT+*dg7_2C>UHc?Ar9)H45SO-VyefESj>c`Qt&F9<&x6$Gc z<})VnJ*IQN1mDZXu`lC0)}BXa4gaP%kMlKg?%(;W&Yaio>B#|Cc?|twn+Cb+z2cgh zqvIM~p|K6F&*K`K2V;XKM>N+K#b~v=*?an+qqh!C%*>t z(2KRNBToG?K6nkB*g=s>m2sf1^jS1I?tw!G@XgTnr|w3Nwu(|5A4|V*@TW&?vy1*v zbw2cdnugzYk?ZOCO-)?SzTAI6e0~t4j%zRm+jj|^hM##2wS*pyRRKC^h4T}2g*Az2 zpnz2g;Pe>mp}fIL)!7kZ^R;O%--RZ#*HU+D6 zZGKs!+8&?-^2J6esKZt%uuq0;-LzF_yPsxg_0xR)v(8ES&|%Yc!PXRg%WvlBTZyxD z!T+1CH;Yqs{+k!d{$FH>=0fV&x?~H+3i^hW&o%hDmgwCBHi?b@lCNK|)z$ntoW;aU zt?`o`?xu*Xu7BcS6JZxh1-)glYPU zBO`TX-RNImO>e@k)Q4Tf?hhsxQ$@a^9hfi!9GK7C8_fLwl{g?g)zb0da%Fq!>NT_; z($PSl+gq%e+*7EV(@m-=>uz+!4v-rjBuPT}O1IZweCnWF!Lo3d=r?n@DnFX8b7oA? zm)%J+l)u(rb#9?<hhWlEYBV0QH(r9v_F9xgeWOshAD(Xz z{eA+eZ+eIZ&TD9b4U8444iMXzfws_Oa?Xk1(HM^1$o+HxgB~Oal^wB-9#ZR=4i3E@ zX~<{)%*Wr!KMLjqV*_2K_NXWETLbI&8?cBz(Rsr?*u&iK#Qhh+C>cBA9(=_781H}J z`aFgk^t31O;Ag-ca`c?bkpDawbD6!BzKi*A*U1m-&>Pax-&LQVgS|svl!9+OdpECt z0N2q04k-E}oYz6Ax z3jJ7Pm#9toh4109rFIh&isid&*gHLlfnNZd-eH{Qd!gH3u$G?w5K7bJOd_MCSo zd9qJg+rNPAU*mrnhzC7Go<29GxvqU=Q>}YoQ{AY6T1_c2yEO6`4g4?l#L5%M)vMHy zwWiMg2e?g6;zPHH4~>XXt8Mtwjk5%wd1&tJJS7D_I;{l%c3Ky__n6M~`)-}N0gbM4 zy?kn0zN;^{eW9-wNSGZHIG9?R-fld&fh@*Vs4DF@in(A@-dS(NS~-w3(_p8B15aOW-XcOkJj_@V{m$@2Hd>k7(7 z=<+>Wx^anKPrk!WDnW#*kf)kB)(#Y+S~;v{~72Li7 zpPkTIJ_0)?ppid#ugmiB9=CbL0hiTr$ZZYY=L+Zq#(dA%ec0vq?760ZtH)esc;%Mq zyEM!FZ7%DF87}j>Ot;TByWQT==xD@JH}cAHF{GaUAmMPepQTsx`h9YsK<~vr(8}+4 zL=K#lErx`kd2c_a27Po<2{xQpf+hNn{goqF7aS4;Zk!Y>4=)Io7dHvPmNYRmXsOa_ z;Y`)GX^rT2HA65hUZ&W3El~nHuTfgc8S*pf+mz>|tuwt85|6q$&-!E0G+p|`v-#%Mtd?iA4E=T|61BA4Ipk^A zF!wxt$-g8vZ~^V%_Pynrmc7(!zuszXR)1aHy)mMzI888y>=#Tf>TU*Xm%R&S>Rq1> zH&E+sD84?(P~NMr&M^$U`2ZaN1ui1|6K8Mu!a?Xvd+hvUG)yfy9&C3P3OU&_o)-h;dN*! z{+kO9y@u~a{+%4SJ|9kYegZfI78FDhAI;%?)0ykUU;g9Jb>rNtzzL6D$Of>$!+%^6 zh`P68w5W@B zVe3pH-s3q>AI^OZd*CrP&KW-Ia=bC0Sa<%A1T-G0oO^xBVyl=RVk|^y6`Cfy0u)kfv_m7Iu6``M4a-2L^AQ}U|4OB|6kng0Xwc^bv z&G}7KbJJMH=Lw?o4%g;auJb@_9qMV&%GBr2#U9EV7TTK&8@xAx7PM(ela(c6UD~*d8O*#ZG_Q(;bcS5pR3e1fmupu?hd8pisee{cV$v&8BzqjZ(G`{*m_1zwal(NN;ORA2Q| zno@0ABiHnqCOa3wnTYDCD=z?t9ukXrhCPq{u6z=4p_AktVwi8suzPkBkMzP`DIm`iY%-8!88&p?H@bA_w8a{losc5}hFvwHl|m-4$DC8hhe0;?h*gj&d|A{^9=67DgKd4sBt%>iv3a4^b>iHpHUPu`9`r-h5arby1Sg6}e&I60V8I0oA&5nE|EuRn?J27VO{1B)xk z)t^bw9KR+s*EhvBHAN*f)lVMms{I)(`UVZyLy?V*kHMi`;ew}^chywv*l(R^ zqQ8$ZIbHI(Hd8ep8t$Vk8|LG#ny#~q-zc{nuu~4Xlcu!lFi#g6H%GQ5FAzeG%@;!r zGZm|DlFl@Inra=oKn~ov6g|lGLa3V_vS;9NZ={#&`CQptOs&eg98I_NfZILlY;$AZ z!_7?{cDS26Z5A87)+jZ9uaRoUuMui5q$rge6AUGfBD{+3CVBn4XpvfRZM{;RzD=q- zyhW;QHA8V+rXG2654HRYbXU6&1C0fLQW>}K{a?fO4kT_ohj{ew95p^1LgX=Lv&3ymTK~H{NeU{1poAHvj6pE2E&-q-t`OO z3?+$hX6M3_%maVsqwBg9Ec=}r$^FzC<(O>IiRCC?u(eBz%^1L=;*1h4!Y=o=#HOHOg zdF^P!PG${$MxLub=T5_(pdU?M8}J8vs6g=8B@7StRe=eWjGfdc-G^TYNAT$pG!xBW z5t>^0=Xl*2*8LT7UL(LGPd*JBiny~r{|Y$O3%d#4(Gzl2Pha4%D6rrejyD0Wms(JkO1z=|*Se1gmb`f7{5jeC1ZI4Iza~_?e<>(xBhif^2 z?|Kvrx`RChKcHYBw?|@6p*{Uy+P%wR)4-v&k&O*uk+GmbdF}61BWKDUu|Z8x zgRd7%onUu(tH%b4j=kudx8uJ{fe-&;gl2ykeyXR&e@l$3u6;~n;}~qW?(9WJ$c4u7 z``|q0mtYV5M%-^3|D#8LdKvLS^AOFEo#gS`j0M@)LN9|w_rak}T$_F1{-apmhkwQS zKIk~u=h5oH-cN)?L*cq1a>eqIt~%TB20@)5cxR#!kaR@wzeP{!b{Em8{!|G(a9Rzq zJT+$7LD1x5+l{peyk35iKzO2>PkuP;9QtI{%={`tX3;a^rwz`HsAQ z)&a>B0f+h_wHU(>il!%UBdZR&%vW>WwtGi};JQ=vlP311GmQ(nXdWm%@Mpd?G$`!=gHb^RA zNBpaF`i4b>3k@ye1aZMUS#7i1Z5(_`3;2D%;J+vObFQ z9({lAV`F`eUG*<@Nu?Y;<1DxbPp+a8EZT@J=m*#@W!No+aG1xzL%fY069N`}ir;mX zn9yPRBtA<&`_^#UZW2Gr>sj*-=I>9e+&ZtIw$A~ z&wx2UqFLf#W`Rlc7AmUac0P8{Iv$tUP<`^LhLZ6ZTOS$BDddJ~)(0Y;8WVwoiynpNeR79T?cu@GtoO zDR{IAyCXBIscuLF`e)=M9bt0CGGf);v4=X6*TNpEX~+N77dz=Zesu^qM2&rQZj@Zz z5-ih=5?tL=HUGhbyt(;Sf4e?=c(^n)7fZeJ~?6XK0k**b$>SvZ9-74paYsJIPf&2Tj}0vFqCK-|fK; zIspbv=h`Rm`y~9bj>EO8A!7x58}g!!)OdUg7R9k&P>Y=3ifj5!wD)7lC89ZWB+;ZkMP1Xn}r(aqZ4A)4~nGR-iSTCi*2(0do?1q2_{aa3tJKTBvMZPS9M zY!PhRcDk+o_qi<83|Jl=7A#*J60G!jwV86=R`AGb&2gC*6D#eV3x{HSK_0XN_&@u!f}Qs{Q-@;aT7gT6ee1T75&A(Z4-5XowhF52)wg1NGDt3ONR3-OR zoF;6Y$7sK3*g^CMu9UzbPft9F_)!nA=tpp&1-?=PF-hW*4tQ*iG;|RCsk44!J)nIifrW8T}jPuwrldzLY!3@uw{tHa`0BpL(OStXfFuuy;;9kK&eh+K4@O$uVFR}a}@?`B|8r`7-8=JO=H`lGg<`@VT z&5CTUofy?zdyJa)uyCOYzH`;v*g|>a#$^2TXF1-$mOY0p^c6LRKV#3L4eZf?b>KTX zj!bEm?(A@zUc+Z_ot0bG9@B-)Kdf25Oq2Y5M(DhX2T9sn@2mEZkM$KPeU<9XePySO zzKesXVZMh?-I4xiYtdd94+gyjH*5g*kqJ(Q3>MvqaXUs57qsJFf5tvFml)zzV$|!w zp&EWK{Oh6~;8XEvp<>?>q2~GVa-~Sl?DuHzCt-{qz@kj@{Mj5^VTXRo-f<*RS9~Z@ zEsIN*DoaNSHHOhz^YFng>BqS)-`aGy_t1lq#S8|ipUELNj>{px(>v_P1DbUo9OuF< zT1eGS&4P_%>V{@)$3vp!3Hg~$Xvi+&H|HJH%t`y*W{v*SPw~InQJ?RZDVSZUg8$*o zZvXvR?toM18Xe_*S567RZ=4odbv!AxoJp*x3$dc-=*c)|pBQv$w`S|SPYAqyh8hcE zL(hC627h`+vb{;aM!y|m;HUUu>r(~!{zlD;PZBg>niP0@v}&F?T(w>wBL}I|q~Jg2 z%Yi-MB(`29n}QcgK}oYD>-I@O4y}-J2o#H_lMJUs|FX_GAh^sRzWM zWz?pX9hF0O9+HA~WD9 zy~KtL!ME)x&D0&A`f{3P_-Cr2XJo$~ao z$G);LUgopT@fo&(auqts_5s9EkAO7~m{*Ccp`Pq}GpV^Kr}s@JeAtELORgjfRqODz zj-cytA3jMk+8sEkGpL%rG={1wUYuloY78I-1cBF^) z>#+!RmhI`gC^L81yG>)~dkxTmwe{ClEHizPk zPrVX6`qMfzu8B8eA9?sG)Q=U;1&6-iwI{Hr=CD?Z!36TmMV=n9bsS#?XU@_`h2K{g z2G)Ge{om0eMBlB{&UJq=N>{OjJlXyzWBw}Q9Noa6Zuol@#Ga^I%lnz{_!8f#3&%EG zqdDNzk39D)>nss`A~&6f*2z;(Uf)y87RcEDUp{q}Y+8F6}96}FU9w1c(q4neiR?NeP`j=QV^=0-Ki0!<}?Ipw#e;Fv%zsZ=HsMIe) z!>7Y;x9Kn9skhImA-Tu&!Tr$ko0}$B?#~u|woDYH4n38c?>p%$K1T=bTo1LfEwRX- z(cWAXsbPa@HMii6b;2g<1Yb-er#FWEC4zn>IdB>M`477i!;WS@IufO)?pR;wiSdPk zM~}hSV(h*2B=#)q{tYAaB@5v=bigkgNGy$b+LI0pauj)UxjwU~@A-!R^e6IVrOEW# z8YbFXjTCC@;?a{F=h9zUsp;s)dJ53JVYB?m>CLb4U`a^|j3*5Gpd2Un69L?vMDS}V?F_O9WbTPQ+94(~N3^C{eF`&VV zg~0kHTF`ehL~Gq>)xYm()u(K-;+qHt6-<=^wog<1@2{4mO{@>doZHW^R^h>r+)n@djPhqjjpIBvq+yl`2=y-yl1+)vDvw zWjcHFa-HL=b*gjRR;9WoORhh)Qgzh~)HUz=Kwo9~SYPHxye6a{`E>lU1OLUMWk(U5 z>9>g)DcD4BFecEWH-S2}fPsp=SA^(9gUm6J{V$5~UWBeHILfPP?F65upiI$c-f_{S z5F0Wqk)_fR#@h5GV`Jn1wXP+(*?O7z|{u!W|=uRZ{; z@eCOBDmG9W81xCbrgQW{-GU!BhhzUJrF<##IFp$79DKM7?6-Bi?-Dg2F0^j1;lI(J zuOgh9CVGdK+{M!IvCzRy`Od=rd12hq=7f+96eo4KJ07oi195SOMd-bZ0_VKIpgoc9 z+Og;?bQs{Si6A!Qj|S{x`q$HMv$92pDhYE%aR!W}^mMd-ymC9$1 z5bdKzy0tMA1@{+A1gU7N;Pd#XV(~qvSR;_YQmfyj)=2B=Bfs2O zd3Lc+S>AH5iv62Z`=xC7f>~-^`#HY#@x2WVFTSs@sOzFHeVv?qS1>4)^*@g}^&vig z7F?cg)D5o1KKc_b(ExO%E=1_cH{+MxMQ<+_8^9qV61ttNUz=?4)+&% zy#pI`hJPmN>*qxo%UgsQ9d~*gs=h^Qe-&7C3cOhi_Dm-(-HzK~982KDrFf3a>u9uH zi_yDkpX9EI8A6|WG?fRS@7^a-DS4B8^(O2Tj~}CF?h%hI{)W8#SaQ%Ouy00SzoVg3 z8H_$wdwN?G5R0t^hjPK6z4Quv9t{n2waPrXw~hE^x8Pwfh>|PgsE0M8zuqNILwi6Y zmZdpMv8#5Xnc7IsjvCgdm zr}ReZIY8&6o}y}0n6YpKHJqM4jTxM0Fc|X+Smdd3&EvL*ACSTN^VmHm@U8_m+#B?S zJ%KIu01T(Lyf~gU+J?B%7sR}8(7!$gW4egLx8NI0w z`nzjRp$q(Ggu8AKy5c*iAN)L0sP$-6za6C%qRS1JQ!R3PbaBDf!Qf15uICkg=LN23 zK1T!B7@IMF81XbKuUSN#dNR1Poe|I2$NxE!|0W|It!iq|!jk0D+QCZ6^$~JaSA4Uy zDWYr1B0;ci(Y%M^H`SaVzOq{gY~CgW-Go<^ddzKmh%SBmL&S3EZC{HIWjZa{PEdn( zaIf3?#Syoy-w7e;HR|6h=mNjrA6RpqBl>)z-r37u6qFVB2OO{z%#DH^0rGS3K_68i5Odr9QJikkpcWu^m zm)D76>rA03DpRQ4pC&tA+^kghNSABxZl~@(Q>tIQO|E-=vr<#BL2nUh?lsn1TN6y2?I7z3MKcD!!J}QeY(iLA#Zzu|u5E z-o2-HQDI-N@}9l*l>^Z7*h=25g86%rT-z#QOPf7$DMoiNsDfDKN6cqx#vD0Anrr3^ zZK|st;&N)qn*DyVSkWp`E}g}EA%aEzO3{K^-5b<K5{nM-Lxx|?Z2D1i=IeIwHtog!KtZj{T|8jyMPfy>W)@WFN#T@U%-kXh` zlnWko2CqDMe(Kcn_F|uS>g?CDK2~u2h`9J?;MFneq~7*uc=e~}7FxcZI`T?t{j%^` z>#1wEQg_pa{&TlES0n3ZE$@v3cdp_uo(JQ=ntZfS3&^i|>dL5}(tCOx72M`?(c&%~ z1r8+=)Bcd}zmJhdJ#Y~H>zBfLh>USNH=w(?pL|(pRAXZ)ICP(US;0VeZCse>_>Fz4 z5zU2Hsj5L}J8Tfy16IZE*Q{DEI zE->w=YMHS^G_huU%a@6Ur9*Vhe-G322^i`1FXL;XV0}tND%8&J+fKrGJA%kHGLqerGmVw2JFUT@2hXeZjBXuK|0~J^v5$ zWf^+||3&_591UEX#o#vax`I*hx|06*Wn%}cMN7RUuKCWrhv~F831n!0h^muP`&^rgk(EbOd&<5lvr@ExVuAw|M4=-n?P2Y#Ej=nZ8>k`j@X1)zpQeE^NMHda_8h-B~FI)ou}k zE>4pyzfRLE^HQ|Hkt@Z($~E+QTqy<@q9ybRxbwnP*>-2DV)9Lq{cg@xe8SfAZm0?a}TQiFKyNbY}_OoKgbeXF{@N}?tEGM zFkNWM+bY#wOI7V1Ht8IG>1vIFTSLMfFAR4;*?#9Qq4vpWW-LW_F?Spd%&UJ!Jw7+fc4-|FZA|PqJ)~3@q)9*(B_&w!j zhBenXi2wYND3^Dq2iM2stck4@tpZb)YV`%V{Z>LstAspN**iI2VZUeXa zfkWi$3Qw_acQCi<_fwQfE!T6{Wjnybl6a-$ecodvF1?1h&;jhCE zhSQMB?VIQi!fz@*N;q^{s)9^XT}B%JR&lIzu; z;m7R()Jy1Czr^ax9>!|+qi{d%XpF^E%P~E=vFRYaqx(d;YQHC6<WuRqskBRaea zb+4ZMWioau{ewJpFa;m;U$*0Q{lKAD$fB@2N4{F9haq)@-deXx0rG?!d)aa1)7r70_d7;2|kE_>dZMYpWRg&l<^m zYn$dj{(u%V5u7>=_DnmjnkS#uS=*dd0&kJy`X)y-eVC>B&D-hrjoIW5_}>Q2fB#mu ze<1y+!_U)q?2_EN;-cCr^t7=>zq4x4fm}IoN`_=jTOkIT=LtSZlV#)IBaFT~ChIM8 zS4b_7qpO%QPYU{Do?!VhMF_gJRA_N1O$eI3UI>}EKx{dkoY{>rs>wb<^_#LtF|J>w zc;8>E1ck0wY`?8nyhp8*y;iOif**ochiB3scCHk(ewl1e+o<}+Zr1yJfR0^#N|S!z zG`V@|bhWwPdZFpu7OCz`s?PDhG`*ucI26BKsm>G)ty;-YBS z72L)D=|)_)1A9?d;<;a+s2D zqJwjHh}$_lNvwF7ppD}e&EqH@5 zfZXji@+lX1oy6Lt&sX8QU`lC>!rrJudr?=gmG||;p6bI|P35)a96!WHd7s~1Ox&3I zk77^X8ZBBa90nG>#QLV+ba5B#q%-sl6L>xlj7}zIdXM)OV1o>0UVlnop^jip5_@kx zxuVjsbed-$@ht8c!5K&5_3ZPwIG(9eJ#_`ZMxXEzrdms6+h) zj%hyFVu6h7~($1xJDZtc>S>PSBO~puh3HczyAXA#(ZM5t4J|II-c{B06w&m|a5z4L^(J})+Y;%TYxs<8}i*v*l%6Qc_e7M1@%MgQ-I#1T6W(i*7 z@GI9Gc0Xe|&=~Uhez(b=7}P5I9i2;2Y+jQU^KWByKFO1f`nc%^wf#nyyn082Xq+b3 zEtsz~yqvBz&q@~?8d7ymHC69ilcCml?NDmo&Qxn6(sj<{je3W1qgs7tt5m0D2@TWs ziZzaHV$H*eieqedeYO1~ePuO$fPM~BODALhhcVZeGRJNcpLq$MVi-6?@As0^|Mf^# z%Ht!ox*n14#S#zJ*&;sW1I_CFnIBd1dy?OXT?~wa?Yp_^34;yG% zBK8ZuSSh^NPZ=$+d(yE}cCfA%72=Jk<>5u#GXDIT}HY!*$l|EHG*=^Y;+1`xhL#!)rgnzHxK@A)Mzld+$&* z=AC?&h1Vu~&d)f+^Fi25A(3ja3*X=tSmUYF&jNRTC;s56t4!e<&H;=1avObp4|k~G zyhn?J^H}(tJg^FXui!_%=P>s5M(XQ3z~dS~Tsj*2v;=O~AFO>RS_hliCvJgJ9pK2* zkEQHoVqGVRkIf@qo6qNyZ!2iYdE>#(ANbB6urA5R75{(^{@p=JxrysX-mv5^{4po} zFEs4NBjC+>YW4g%mJpwA29G`>XEqaF^qMGl?f8Ms4F%z@`ib-i(y?FMfIsv%H5l9A zKer`jbb;JPIrh*8=rO(-t<>Cs3lKZENqBx^lg}`6W~Ike)1EV`)de5#DlwK`xh~tk z2i<|Evs~6`o89K(@rrlF2gdrL;1Iok9PNk`g^@Fz7KR208ZYzk%YI@!M||i9^cU%^ zQX*gnof)Q7Y@a996|NQ<9!~wQCtZ0UeW%ZI4bVI&=*oXV|0lRq`obdmX{RLU3mu7u z{EO@l@WJxw<4Y|Jb;rzsVc6Pk_PI{r(J}n9<|L);_u-O#;&`p$z2%y?FGDjVZgrbl z=4h6m=#4QH{0QAHns%%d%~vu6ub{(%;gbVy-?QlWtlJ~lMr{)UZL2lE#p~QYqte~x z^bF1HL;1j~=f-RhT2Gpwwzx7}^^G2?8v72>d4D}wwhUY@Sxm%p=92q* zI7H`ld#viWhMq(V*J=JSi`{`|CrKgpk&5l?2%UG^A$qC(B3Zn2N;E$IlRNmyzx8iS zd|Lnd7rzSu`8TNFKd1!shif!>hHOcjs056itkcKLk=^Gux*CesXf;O`=$m@1QJUKB z(A?i-i1qKJ>6|)ns3-&7jZB@hU8df-bgRyJ8aruJx>EZvOKhy%r@7wB5}W5NHQ3Si zD(>9PXn&=Lt}3;+S~>=Oy*#uQ$Dx%*ZoVoV96A)CRt~2J-F4==!v2ZAY-QhQVuz7J zgG8==CUJ_919XmEVQS5+NL_V#oR7nrVsQ6Dr&707^m}WnLAM~r$1yC*tN1fGLvW`n zReZCX@Pm%xkAZjg4Pekj;zV2MN4|*IbtgD-AJLDhE?#uRCJ9wJ#DiXk3+tPN4Mco8 zn>mu`$&In@6mTe?b#-M;Vi}p0z zzCpXN^CHnNpr^rA`?0a|%0Qjta;#iiiNALV{)m;{2=-Wa!&v&)1ralw1%ATKUD2`3=di*o-wg7she-Gb^YgquVqF@8pm0k#W=NQMqo>!@FY0U^?9(>7l zpT$0b?o>f9aOe^?_xtQ~ldz+s&@uX7yuM`nP`R@0SfQbLkz0IawV?k#Rr5_p&#&;X zX!`iLWNw!$`JdY$`kz}b_-gP~e%q-9tfv>m``Aj8w`$h*>)d|63*5%+1%fGmkz~nP zD|$cJ;P#Ema0gsP*X)NZ(f9FY!6$Ts=-YLv=;x;I>yphb-;w(?+q4`dHb%O$M$7ap{2K?!m3{iSxr6Pu{)pYGPi0a%-x8~_z z_RJ==I*$0z0&u7b96Fb&cl?^Jw+|pTREvF7uura24+zcE_Xw_pCFp4NGnU=yVsw1p zOL4^al`B4`p6o^PGG+hakWzJ#d7r>u{0qLpUF?Ewtcz0SZ*i1l9}yweOe1%DlJ$`v zuCwc+)S6*~bkzaFylTE%s1pY4ki0i7Q+(cy_tw6R@vb}SY+T7lmO-=-j!IF6Z``(KFZMDq9pUPEp>|1`Mr0GsJeUc-5x(wq2c969lFp8JbY z&TCim8v28Iw1v=dMMI7F=p@ERe20(WvcMH8m;?^)1cT=Cd6`^KY?Weap$eBrdFspb z`5xVNBQ=_ZU|CmwpCekX*a|OVKkGRZPK|^X_}MVI+>1Uz{?wy8&`s^mx!wa~l6d`# z=q}wMuSf0+oqKvB;xqn>@A3|KRNXn!9b}T`Mei?D%6<|Q$fm-!hdbFH}PmM--brLy;68P}gw^bY1 zzy1V=oZwJ-l+?H<%vkyQ=;r3-F7D)^jUG(_wHZ^Ih-K-y#fERk2E4WiPd6{U@?xQa`OOAwp{yfIWVK-2Fdj-u)IW zSN<5MR1BKno&O}pd?%*A@7>P_SZ=2dF+Hjx=GBWB(Jta(X#N%oT=#rN5q(USGB-N& zUv}i0|Bt<@EBnP1cUaRNH-q=-Qbg$|c~0 zlBM~N&5?q-?2}sL?os{e1?qQh6?$*Wq|hVlB+I((vd_`oqUm{J)Z0?sUZ>`3x{@hU zK-px~Hg16ukg-zoejVKL-YELLvr+OHNS^)rGSS$uP%yd|Ylh%uZg1BrvE^&)mFKdv zW&hS2CBIpVsR3JH2(&IyeUHzReea~ue_)nmd4HnpUzw!%EKF4VPbKI~uE7TH4-@s? z@jU)utl}$xM}3nNZ?xZhHjS2jhmV(hpPwL_-kl-^-kh#8-&v;fzsGld4;-nr+=F$+}~o6cn7TStGJE{U=$P zdrXE@7mGb~ZL{7nd>c5lTdf|nQ*|!hqH|2jP#v9iD~`&2O7)UGYR!qQ`kLt}x+=e3 z;71R&Iu%~Ct-oA3pPn0^qW{_v?bpH7ur`ASe>2xNMEpNL<6qXxCe}@7_&mMw$-L1Z zrgyL7OoYB_Rg})zI0*Y|sNPf8rVQUM_?(`m7<{7i&Anp`j*@7d-51Wk$0zGZ?`L|z zRy~3baGc|A;zT`(5BF#9F;_6V>ij9TB8gM`ZAgS4s|YB^Su8=FI1`YPOnT*e8wIyWO- zHJ8F&PTwf4>QJO+KSF$ZD76#~)R z!RElj>lJ?~Qq^kT^C|1t$Ge=K(zQE#=xW+`Rvb4v)7PvQx}4!cE%hEXBV$G9Huzs< z^ulZ$;``*@5aZuo>s(deOf?k53=Mefxt{C5^mELEq5Ll|gGK3ZUK6p2=$FDCV#r7L zAg=@3M~mvDx6!dJhH>cZ2g#+(syne0On;Y~8a^YS}bj3fi<>^|_mhX3t8=JR98Fut+m*nkxDikJXuT zXXpcK7OU35YbC$g8|ldb9<^O2dOut$8l{Dr@!?{(_g4!vV;%gSZS$p;^A}3C`7>lw z>?gi?>*DqX6GW&d}y`ky`TQ>m-!z`auwS*C*`0wPst&_ zo|0{^A6LwwM-|)L9Jo-sCF_$DvaR(wu|>lfspa&ua+~!hq_$riky_u*6@3O}(c7NB z_B}T1s$S2~IjXj+HMe%lb?@wy>w9jM>o#svs@rBORTFcRsu%ak&dF)AbKFQ{>6spC z)iC<6mcZo+hd=$8_`^|bAvhuBCD_+32C9`3`)(s#-emle4_KSN_&>)Y<;n?>V(q)( za-BRtSKT{YZ->9*q(0OB@>pHnvzrCIYn$L5GD(*AMd@nai!s=*MW_x#Z`qNHcE~31 zW*&H>;g5AAM|Nm{Qf(L@*Bar)y-B^m7W#m$iV>aGWOvPjB)9V~bc5=_pq~dz<>QjX z@=nQO`ACocDfJ!9!;Pw=UVO~g%}V2dYEE4rKekK@pb=XIgHrhxn1If6Z&+-|;!bIyn}6ng4c_hS#W zBlksp2YsbIu?K28cx(!|1f~^u^!~PT?!&~J_`Cvd@Msu3?2p-dJ$n4hiE-ZtN86IC zdkZ_IobLs86^ufgZ47((d9eR*qy&#Zs{DvLlet{;dh$d85n{zM@_zTwzL*Ig{2zY% zB=ol7d>2#SQgo4CWN(6jzMKo2tgr+>F_4^K>o~C@7oK8TtWY^I#$Bb+i>x1gfO>*G zVc<*ww$NADL^08tgT8O}KprCwXurYjmh@m+iVrl4UMRDO(d-O&)wCJlb~X&qs&C3~Q(Lslkj(VX^1p#LWj{EKuV4%1%-6g=S?KouHAT=bn5g*v zHd!|RFGaAnNYO0cPDNvXs^T3xTQ;0uB=}ukESO^9CAL~B1wEZF1&$f71gsyfc-4ISE`0%RR`){-$ zO=!Wso2vv>=F;0WR|**al~@k4Kl2IsH8U(Vv6R zri$|PsZpF!XkDd6D%Go^rMl1J#pc9OYD3~;K{W3M_cw`Z%}71{6pWQqqYYI?;z++x zdo&39y%pS`WyCn?zv{TmK8h`GAB0~vh+Nqh;gWq1T*6)+P5}Mvb|h;~?(G$zseyQ( z@g~@In0cFl4+R&ea2)rafIla&k;1?kPrS&R_oZ^~v0%`P*hQ7ZwKwox=rx31y{E2A zSI`SiIJuesKHp<|`SX37z>f^RYXHAtKDN%=XzxtpH$5RH`(cz^z6YOc2|W+7Ny`SK z@%Az}RE#~udy6J>j%e_0KR6T%{#+s7Lr=vb>di}2sll2PCsZ9opXnHOkOjVUK@?KL z(PCu|V<{YmD8_bb@$RGR9ZavKH==~f3ivrP9MD;CJR8YZ-Ha5g{J@~tFwMC#Omocc zBUNsq&i-5KJ7jQZEPGiD^Jg>j=uU*<2#HpmI|ixMw}yMkBX_vH7oKzZPo&o8D)pM; zc_pNT{CxtNhG*#s+Hx#?a-KQ+EwTQY*qW$!Vdp_y&H@y(Npm$<_caP%#e(= z(`5fWQ)Tn7vqf|MY{4`HZnOVn$$#z)#ZWa*^trxBuza;Zwm!353OuzKjYew3Mokj~ zUYRcX4Wox{(|Rd*?-H@))mdVweuieNoGF;4$wENEB$w%z=`P!=i-e$;(T3c6N%KF9 zW`8m@{kb`EEBdoO-?UX~{cDv7ri!Y)}{v_)6FYa4Z0JBj)3lxpAKEjGLe4kc`t8pdx|YBp`xmm9Zwmt9Zw zsarbH&{WY|?|2p*Dx`PjRdO%h@Pi7-$sA!ndx$Nh1Bc)&mYt8%7o(qAKn|IHHoD@S zaZ2R?>Zt#XP->nDSF2g~|K-U}j?guHzgU(&*(#_HvfRe|%LS!APH%rM#;g25q@nT& z+8`r|Nk7ZTC(rK$i}q1Z|2grc!;IhY17D|a>^tF7MH6eQE?IE&9O|y|p_U^hQL5ZX z&VCW|!;~ad3gFU9)NlNYO|%f-CX$>_Df0y_p3?nbQWEuNEr~O(C66+S^*9?_s5^6V zANUZ2eKIo+9cFOiI(AMqkKJefogzOgvtQokaSsQmBYvJ2=h1lnDKYO?IOed<>#>jU z;|hE6x_`Jml_Rxbg;#k#nLYO!d3tilMZrAx70-%}aP-u4*-2sPwLx1q^2wkC>bw&@~(gFBd-;w*8&M0R6EyvgT z8JnsX_(s3S|8j4uz_-?nMSO?7d`*O-KKMm`5#L^Bv*C_ zd&xUmF0o@{B{SCJ*OA9Bn$NKajzS&d1AIIM>|L-q^oQC}vgHQy#0n{zgb`n@MLugEikuj90ADfmLOOp>Y`Mw>#o|r?IqSO zAYa!TTX6x`#0Ogt4c&sbBE0fET*bxA)zRQ_E_S9Lxs@mE7ig_`v^NVhaEe~r`5sM1 z>hAJ9b*|qrs0H@$*o(@UHzn}(51=Fb8Cq4j*g`w_U)4l?!JZ`a=0?i)m1Bjv>(k%FhM5(1;ii>*r2{3b1x^u1;)`fDkY&(y_& zpWjl=|MexJ^=gJ_`65@bes@XZTd<%Q>|>XU9Nru4rBOssqrxO&~~_tPpGe-u~k@DB-(v|;B{bV^fv;;0nG5t77+0*C|U;a7V=|XU52m3Sq4U6e{UOqfZcAN^A>hFcq zcQrz(y%DQA?+jO}KU%H`2eS0qFNamR?IB6mdA?ij6{*?_B8?Rp{SB2SZ1=ChpTpGS z$C9T%PA$hQYRW!f{@x>|Fo}3|1N$nmjj~IFm9pj7J#B}(ox_qf$F!lEqvJ5mevX=s zJZi^Wtf?ewTKAJTE5r^WZ(n*H+hqvzF%wLRWB-p%kjg(L&bXVomvJw~ z<9f8m*bAS`#(wEaov6&-`3APpPjE_Bv#u|LUq)i#Xw4U$z`pU=IGJEbKjta5uEkaO zgKvQ&--0s_!Jx;SdmrN|kG+PSRRSjV<$gTx8Ohpf) z``zO4u3Vo~#%R{-W8zw!h@tc*w)J?BQnV1?ZWQ&U$>9Q8u%f+VxZwN|uCqrAWm%x7Zmjd`01%+_oo$M7E)VHAN@}JN4EVj^# z{6}x`x;OYw-Uf&4?0Ng*mGaxfiKzc5+yO5>d9beN!f>goc&t=cF-?^FlNYNxsab}f z)2x1Js+Tg}B;-u=_3t-NZg~jJu;R5sU~#(OKN{X~;d06AqlKbnE;U>y=4!zU7mJo5 z>m~o$dt}SYIZ6vNI?<`~WoxTxN?>nr?8-vfTs~JaPfF2QUYn@5jvQ|Axssqa>Jkk; z-MD>bEHUV@I`3)Ybbevvye7?2ZR_UA!AE9EK}8c)YuE(UER(-4nj;3^TOzhlmx%wz z(pkr6RlooL^c~B)mXw5m*nx_ogfuJ}8yllxqZu%|V{9}dM~)8Z22rsSR1}+!0_*O) zPX7>WIbqY1EjZ-9RyRo9hIYzQ~n`8+5bb%ItUXXXm z2cl2^uLSS&a0t4uQ(JAGZVbLK#$@pyXZBq&)nMs2Ted#8NcQ<8L-L72E1}g6RSDXs zxccu9jD5l6yxo#|a=TWq?A9t;?Nut@+9}tJ0(;2S*Y{(L-;OR`rdH8mgI0fZt|n)P znVNRJYp6UypXsB0)Y1iT7Y||ocSVPDR+v^i8_kcmm`C^N0rfk)p%vV7o+Cfn5G_|o z#HN3R3w<5Ey;St}M#QPr>B&;_wat=Y+DR{Y?fYiccvMkM3teIhbk4oP4dn+0Yn6Xs z^PneEl?et-L#HU4cy$i1|01^84jf9xmj5hDu6V>fsP$;EtlJo&{P{7SiqoS#mFq{l ztKS?URQ-`4RS00v#8{~!D-KQ@-_2&7eTVIEEJiDigX`UbSP8lyMFwKlTNu&UN4Mek zeF8SXRV+wjZCxiuxdv>B(tRj!XfLtp&+*B=W*+LC|MA#N=ULy^n7dBqY6EefeqhS4 ztnKxTOvV~83)?0yn4#NK^Lg)I_GLA=m;tubaNnigAU_|B^XEJCaL)f83?hbI_$fX7 zR`UK+Y!h76d%+g$MpyX?+wcPK1HqtiV9+_vIqR_WGB)2aa3>Ef_Z4WnehChZ#-BT$ zAQv^ns|5|b&ZVZUk#UcF;fvT_H^J2%#8@w|FV8YAaqRG=h_UN@149lNlXIPXUk(~Y zx{Vma^DyGI8^P&4d_I-&0ysrXOFu^9xu!=k)YbK2zxI)%3Z#!>i!iBTA2~EH`h62W zuiVAGWEPrsm*`(ekNe885pwN)?B*Zw%f`p~RC=YTP4+#4oOi?HJMo6-Fkh1b7l1>b z-;e@ET$KV2V;eN@lkKl$iM|zCg5lI|)_R&G9vrMS?dWEx4CpRZz1dr=`MS4M^(A_I zFU5#eeX%9sDdvZQN9`C7IPV?6q71O;0OJp?!4&JvVUMI+RxK9 zN0$`U`UkZWPu9!!%bTR&6X|dS=PK6T(^SXfIdX900yW^9RMqzLEY-GWyq6^dou*0A z2GxJ4!7ys1(VU%ZG`*K>Fa(U(ysnHf_-&i6TH4PQ16t0K0;;E}b`>1ToF&^1PLr$y z$cgpGH?ypgLeHh>9BRovbb@HJOq3n3&s18btrA*U=)w5#nrIz!TeMlvD0b?yTGdW7 zhW?Wb22Rqfu}P}myVDe(v(pu?xVegH?NY^ja*m>XKS>r|oGr@@3uNVLy6PMShkDc= zrMmN8xyG?tY6_@xdykK}j&UJN7bHJ>NsCNbjqb521Io zFLfNgU{DVBPZw$hCNu6vsikd)s>MsuKz=GlElwmJ_bf` zhzY6x;geeNHFnWo;M^hN%nz}J9$^E0Jwm8Rr;ej9T6!%Kgv#;gn0D`7?Ng|8a$=Ju&8?*_yM6wL%?O{u&-z@||(m zTW7%`YEFuC*0 ze-j%*e~q}iT388&-Gnpa$Nx3JIiLm(UV)aM%`svdqNfelftc<lG$cPePNi6Y;*s#QSovbQw~BJh%c5VQX~Xd5seE)-u&!w;w)+tNdbyY+6SDotW`nwsXlw%YqrI{kerouz8^p zGJK(8b0=w*#t8=EJ4)c;1#*BpRdLLjLEXn_vt?VH(X=U2RZm2znqQn|yfnsWxG>gW zcx#NoL_M6>pQ8+3mnSLy?_&dXo~qcei7XvvD%Jz&cHB=DZBtiDj4MSm^)^Fb>>?$&+d|2)ez{;RUn2ND zw@5N?oF#ijO;Nlqqm5=Bu1O!pD8iF5s_@Gkxj8mXYG{kT>$H7x?UB85?GNP6BC&x+ z6BkO_rqzURQLDS7Mbti3tuG&Ba9ZBfYF_WImW}F1{tP|*VzghIB8-I&Y8C}z)fPBI zXVGDWe^IoZ^{v5Sxd;x)^yhd4HzW()iQ&|7v>Jx?;y9)8=u%OAoaM2tJ}Edpha3A- zrqg#zF?d^VbKmihl3YoP?}(>3m&2ER|{LJS`*hRnbJO~_mh`pwBzz;Awv;Fnh zGWpnb{ds&9j4Az(7og|#&~Rg4(3Q-dA4`lnHQWQ=*;CUOO^#s^uKGFHMK6WPRjI`OE>rWGjeg5cav7FLt!7=MiT>hh z!#fLASJ`RVr^|<8fb#=65IwPg8P`->%{9&6bH?oLeOUE7mM!}@b_-q~r%7H?hHPFE zWAw~?)vL?}?|&09qINx%io5hN|CzdnDj99f~qF6I{wjhtqbopOB*=&2~{bt9b18&RCwL;4i>%}&sXByjWT%oo= zpV=`pUG{rzrR-I{O*HjbEBO{qHQ7EHV-6@xGFV;MJ}YL+K`p3h|C*i{xk;KeX0&F% zIz_erGEcNWHCwUwnWovl8e#NLi&MSY50%WVhDqj$ak9B{ylVJxgvKx!dkj+zA17$0 zypfvsu5qf*d2E+V;z=*gP%O2ng6-mB(Rw{i3J~Z8?K4RU`gfdUe{F(f=`u;Ojh-h4 zu1k}x{kBSumb=M;?N=Qa_9}kowkkeHGGy<5TSc>Zli<5H8D=8>p7nldn&wUVbaFYFD_SWN?&L(g}ZNI^10H z!xaiO7FFbp-+>#l7arodFk{(k!?ddP(`4t6EpF3!v@f@v^Msr{;R*6iclrMiD>`jG z)iM)Y#T#H-Gd1Fm!5%mH`q#;=1d%sW@X6jskN+QH3yEkKrN>EC5^?DiIMkCyxvMUW zbXC6u7Tq4PF$LPl1 z0)s5zQaOH2CC`6iZM%rQpp9PeBK}VgxHMyk;l^sf{tshVmxkQ#JYUH? z{Q!F?28?-KzlSo1b-j-eKHtjo6^t*zr{@_%S-a$jb2gF>{sg=-@t$5IVq`sx0gtFF z(R(`QR;2)miw|ei4FxEj7c=&+*?*eYdvA@K$ir{|hIlR0nY-<494I{R330zqU z&a~zEY+|hOV9(27558U=J+aaAH{|J>)o-&tH*rjGzw@5Q57TppkFf`cP33ju_^pg! z_79%%7f*bg28#(qV;OB>|-@fnyo)4ExbI_=6i!NDwn5*Ux zG3(>>ckLPFs;>xh*FHy$$8fG^N9>_D(dj%Mq13(@snib%H#8iI@~S^H-qN&Ti)?)9 zhGa{=sW|elsX@1{tAV~ZRqJr?#3tyaoVv&3Q?u3MGiSSG?l?=6bGrLh4(M(w zJKmR`4#bG=3=nE2M~F3hW95oF_}^o(9nqskuL+$MV%EP$d;RMIhw!lrpGlC4ua6Z= zV>xFXIN$KAbLgq7=ge|Oa(opGy#|f`;Q1ZE?c2<)Qu6jff>JV+e0^)MDOTrmj8ICK zkCG}spCDHIr3k{j47cx}hnp?WoNBU~q^zY!L$Em!r)AChg5~+s!-OepRgB zBk+SBuT=cQrkE_!-Qp}d2lGkN;h`kaNp98Uq>0X?sDG7=pF;O$zov3=vNK$>>Bj zlT|Yf~yrba~bzdynI#Bm8`hetjI8zB! zSF4VawThYdl!?0qX~Skwy}w%W-MUt^&fP5dE?Ft~s8c+?Z;Y1Jz|oS_Gs!LIPjkCI zTqe}Kw?(K;*df++*hjC7JyQJ#nM$o4d+3e*T2cwS}q0O5vQ00kFAnA>l)(A z?FSo6o5Re-@x!=3O;H+KZ*?2%-*?+@U-E=JkJiB}+yiDsDvgVKsb$;f0oxBTKg|XsTMp3=EN}0VFOX; zNsTJn)5Hi;$)mf#p*U=-d-zY(I~4TLZ5Kut<|Mr~s8vTBpEa8bK2#82I8NO9eK6$= zSfp!=(Srl-wccCNm`kmEPAk?b^%%KdGRVKv8`hBfEZ@^}#3K9D#Gt+_A8l4*-o_{8 z^yxs$C$!`X;%`1F=B0j_)jPLlo z9G_wk`&GdEI%iVv9deK3*~%Ium!Dq>j_CDkXjT^Jb#(N4r=O$V-$Toz&gIEm`W9VA zn~=IT_DMe;v}8HfLGS|_ICkn;{;L`NB0?^?6DCykK?~>`u;)*(=wvu`u@OSGi8}l- z*hos4RGvY9&4c*aL-EN6gsauxV-vN6Z{IE2tGaKLSM|v`Cg-6Ont9VT&Hmf_##V2> zZwTB64h7#-Y;fotKb%l3QxAxKpJsXd7Emvdyjk(~3Ntkqy>2es&_}Hi`+I6r2MYA@ z6>5LRJ}%=rzDo|}Q~WYtY#KC&I4|Z$zP zh~b*al%RMmiC4T2C8(zM;L^!4s<|KE`+BBoeS4h2FMovUdw8ton>EJhyJoTyYFs3? z@ZKbZ4%i^J{&cP!Qa3{N8yYToy%{AN2TqgyCax20QkH1{bh{K3u~xQzxlS^ExJOd% zZku2BXOqR_Pd_JBec>gh0GHgi_<+qbP(%D&VXVpeeL&zSXu}zlP zaCpB|>)xZ(ewV4%JV9^q>OrvRkXrq}JzDMFCC17{v0CZ4{#woPcZ}s7dT3>f2B;M# za;<0|mfxh7Fb&q<{UJ^z_lWU%Qy!)!4j=(Ne{=NU>%a`L4%93}x;}wZaK^ zF@1!o?t@i=Hvf>@GU%))uW9p!w$vAV1ZQy{c8(FgPy)XHpWwuw@j`hDoS|Oiu$s9yo%+9c zaB&p5=N9M+y$rWTCEt7lTjD13jvRg7XV@?JQH4SJeV=u-hW95jPH{Zrm~+=yvy<_8 z`f(q{4k_fmSumOPI-K9GW*(F0)wQzn-~$vC=sZB|roV_eh4SCzUh?%m(An4#2UzoZ zO&W2e{5ZBxZa2_pALch=f9V=$^kdBb5xekx#x342VEqYf?+M%ej9C3o=n<|&%l04M{~8|7znq)J z*odzU2j}r!4a5mMV;2vEI}|s>Ra-s8RhJ#%u2~EYEyOljGeoQy94?lBML&&WVPg4g zu4x>$&^T%do2VhIh)^pxMXS}s?yDz9n#)%u`PHu4W6(BUFxl4ZH+K%)V{X@c*318e zTe4#+{h7C&P;AEzDb^u-CBKuGMBD4fBuhz>YJ9eGJ>%Jsabx4@T?m z0rTJ@@wAVLBP<8Ihj9JSq1V=q>^<7xyKA`Y zHy*r*9-(+onIieS772kR3&o)Ovt;}0(-m9FM1w7UvS!_ZRkt}QV;3@hQmcGr1w?! zV4*H6%-vWS?QUo}T&&dKZe-(+ljA6^M34G7J@7sPhX&%eC*iaACg!Z-o2|hf?+lOn zFZRz4m&Qs=H;m`tQPEAXr4##3e5W9kd6~)ls{#W|JnzjqCNBivM=O}fpf^u}na_)u zlTY#f5O6}T|BB@Ki<|>p52Pjb7j+ax#4roH6FVWEo%1I7CG3%0G(Yk>@V%!QFZ0|3 z26d&!m|o+K&Z}O_nw!UGm$@gmAcr-E?KAiP>w89={K-wQU^%Zp<-0$yAH-7g$ou5s z_vU}eZ=WW2?q*-w@O%zy3XRje;jAU%IQcIz{(*lT#Cuu%_d*^c`2E-XUlrfE3T}}% zqSizy(tXlLV9ZaftJUcDljqOT^&p=x9x<#{6Up=Y;OmxR3*Upo_#Tr-(m}Gpu#?u!?VpTT-Viroj2u>Ec8z=9#VqF z>_EHfplGQ$E!wDG@G2asDs8$N$|t?8m3#mW?TGd?q@&|I44Zf__Rvf4t*+>?YOe7K zeBMxSzBR}H9Wk^aoL_p`qGO;H=ziE-&d)CFo-c^+FX8wX;g_z%m(Il2Eyi9X_M7KP zRP$da_TQ25?g(lMz@eD2Le*yzg}R3`+@8hY(EoNddSBn$=r>_slkK0Q%>hd;x`JA0GcbLt%dtMoZT)MX$Fg>l=zo8Km#xAebiy zwp<_w|Bt?fSJ6XkJ4Xp9B_H2mnquuYQ?d=5BiYR8MEi|3dhJP2%`?WUzFU)I|5`Ap zU#e(tIadknzeou>yI2l>d7&J#B}ENdKf_=jvPcfSyGm*~G(!%J+#(0HUn2+2n;|*e ziIU%`F|zOO8Io=6Tq)qIrE3h)xg}PYVeK>*(W_+R`1M~ zmH#D6#&?s^;2$d~yW$nmd#EhS#E2dwYp$$iTI0;^V$;=wa^0Q%O0{#BTK(!ywK{|6 zA=z?ckKIyJ?tJ>Q#%QJV+^qSvuU56Yk5)b(?qHV);>7r41?aj?!5&JAP)n33`aux$ zTf=?fGi-NX_%bu#*xm$(`VW@s{y-DvC3qp7hDkNmBVA3K7Q4L%9drh?JL?X7>982E zeV*!1ok*R#7x8Xt$+m+#Nnp?lG_6`tpM4Xb%$NAkC-l8?pz%>VM5t54J&iXag~kDB zLbrj(`W1G^EpTRWoLuoO8bohk7hQ(W;XfQs0UG$fV=EDxF6qd3#-n-G6}-C3cb(qR%#ZQ~Xni@f^#BFSh@42^s2R@M}%fHL}I>*Grx}_&a9`Tyo5#YcQw4r}u zE?dAV=5uZeb2gbr9S=U`-ueQ#wH%!JnAdli+v|9w)}QrHe1}ID+g^cBNN>^n=a}od zZI;f^W8eqL+X?J9z7GAP_4u#x$wF$wU&O9LyI#-H<;~`Qx3Vws?B5Q4Gl9op?E46| zsn=-C;J+8b<8k3%8o?PfOa9|vKf`Zze8Tq2`-uPbVI=DqM2uu7Tw>~33Jc*^J;OTu zF`x?-Iu#f_}r> zw<%iV;BA_=_oC)M>8PpAz@6qUv$mK!esIJXwC%bU7k$G4S{(ghja_~kaC;|PL z!#h1b`{oPgPkT!ZA>_?61_?DMB8iJrXI4SY$$a9~=ZWhFbBy$+Eck?DKLtO`2ELfU znNf^J{96Na8m|3+eD`*66yvZ_(F4i*i9SN;Lgj>U{y!y7WC5R&Mks};H8_5O z8vGouPb3*E-%L{dznUgH)-MzT4=)vM3yCKs<8wVfQT1IoRrWndok!9_$&$ZV4)g(o z@|Vb=XqvV1U!b<=JXZ_4pDqOcvqEh7)e^bol}&P>Ynv3*d8HWoM~XY(Vv5UtnOcz} z^NGi$%8pB5pWh56@X-X-K6Sq2*qbTY#sVoRTRv-NGK)eo(1CM4s$l#!vg@ns*LMjc>qbJU3gdcrQk& zB=6qPxt~%KOb`2;#GJjso>t_lejluro}|WXV7OYm6<(NywLmYroY~CFDEt@vp0fGW z*awCSjbec&!1GnuLKbphE183z#f#;0h+)6SdoN+5kAQ>x1M!fn%(-{K zo6BG+HRi=|`ih=sJx^tQ?`9o#;TShDKR3edZ-L!Fe5vRzbNOHLU0pf$4cJ0x4e9;N za*xudK8fFw|0;OGXViA&?8TN5!IKmGkFK>fkTq=N^O^d218ej+a2!~UMo7Ll`|u8S zRVmwC$a{;49qTzNYA^JdI6aQR9{%?)#yGI&eXyn)ZWJ}b1?L${xh|X8j>hl5;y>=7 zA8-*rVgTPKR+sk@I699M1rX=_}L(5}O)FJ@og~qnzh{^e@*gn)CEF*HQOnTXAjZyP1>BIT{Df>oqm_Tm}9d zcSkU{m~-Bh*T3+1kRBeJId}Eg%+KP#{Z39Y60Unav4DQq;cpgri{yHe!9n^Uxas#S{pb^6o$ZxO6drMYeCE8PC?rwTqXb3}8)e90?v zj${o>l^lb~)xWYvw*S3P4D5774K3TR2H!#Rx_zeLyM|cxq1_(uSNFKQu557nberq) zo;y#F;}?o9^l6>1Y?m6zch*)OQmQ9qDK*!2D|K=ErG}UGi;YV*%C$evmZ}$kL-c5_ z^`b}FJN=aE-^i<4;Pud(qiiL-#fgj!5n9o2%=bxLJ9ulk(OlD>T;I{y=kG>}bz8zc zO)*2=&Gi$+#^_0Mpk1%BIDFzzwig@>8{IlmjO+gjjHS(NTq5zk`$!+vxGM>?^O#IJIR!{?pnyH(&% zAC8lrX88+PL!oSUBkxg5lJ^^*-{Nr-_xfJk<9}woO@Oly!8w>YOew(E%zp)J_2Bct zd&+N*t(b~mQ;bcqlE+WM@Hg0Q2LCmK$K9NRxAD(Z>hT-GosGYfC)-{YZXdNw`w`RHRxR9$~7q$C_&81Rv+{MVj%*8QFiw zZ7JZ@BZfBaRX&~G$?y*Rd!tw2sLg8IoC7A`+})~U%Ni~8$5o1D&0)9C@7Zqed#R#n z&Opf((nqMX3=t}A5klEL?B7!GJ%DR_8I9ooku&>*^YA|BgBaL<^Vo;;GoQKq>oBeO z4tT8R$DZSSM(`L84q@*W1cN;}`2FkgoqJ-BlSeJSL|#M#kB(DkJ!+Ix)^e;^*>jS+ z=A)U7(zZ00Vb=kt`Mu-KzNZd0S^wD3WI49RZ7ts_SnNAozBhI`z2c9%{U#ss*k>M* z9BD_Sp!xd+$G68s+Xl2_N1=6iaH;G#lBD@RN-|mc&(Q)#EKx$cPFI7QMj8CzHEC~C zH;+x^_do2D7gkCEe)HvE|ItS4<5+_+iG108^cT~osD6v)Dfai~s)5>EHDu>JHT2AE zL-3gC=71jaBuCR!)n=cr_&>c|_IkWUGQE>7`wW23-Fksw4W33VO^RgRuuL>L7l{_* zJkc?N+Wot$Wrws!3LbgN5ZdFo7QAr39FUGqk_vD7=K~(UwFg|5P8(hRtLC}QX>$c7 zeW4&eS}iuevqNfl;gDS0^`KIFWVc%P4K?>`_e+hVvOUh@nV$M*XG>N7!<4dr;klma zD_6bNPpBpIcH{R~x)`e-F#x~>NpbQsLLL9Oc) z?Daw5&oy|rXj7MkgsCO=L28K?m~#Rxn>VQq7|2+`e?_4MvK!v@8FK!oz%BIIN(Q49 zc`i~bTZcyEdTLLu;ZLq-|8!0CL>dY{Wd5CH5O2x<1p6cgEcpN*?g4Z4DsyfG z_r^uk7o8-AkOd}{vCdC&jLZJRTea{ZYqB$g{?kQw;UeuIZ}eM?Tu=ta^kPk(VeVqX z>pglNA7`Y&K}MUcC>ah@HvbXNnk6Qhw}kiUiS@*WPoU>$G~UzW%z0qgOmO0T&aFZn z7qwe?=cBb;JwJYe^?wn6vX1+@DO$_h$^Rwt{36&&A5yr#CSC6+uRX`M1)S0KA$MZq z@mkk`&tYxlOyKhk{1-hx^z)FjlXLM2-@nf>Hi3Q3{P!f*=@)$V7g!v?_kZX2|Kr>Y z=Kt1$Jtf#lm-sB6L)}w8)Jqx zHyw>}Hnl(_=E$3}f+TOn2+#zj^p-tghZ~I527gRO~0VczhIk1HZaNk$)d5Ilt;H z)D#X9E96L_yqWrr0x+WsKC=Z3k{CU}UzuZ6!J6e9zs`L+$y|5_oK@jP==H>FiA{gQ zwIODhH=g-^8y%~Rc)5gn#?qz48kc}a=(x<`oQRgTQb(RvCHQwM)Uf+zCmtpHz&3JsW>eG;-SbwMf z>|UzoST)+@9~osZ9*NM@q-f2UINs0r^{=$oWdPup+2U|2F<(00xgrGHn+&E}m_!$WZB z)ppL&v@24!9H?trd>|!zK@w>qgUavC9xc#YuFBJ|ZU@I81at zoa-^&N24x(o8a^1BvshZ+f?rCWhnX>&7t?v_4piKn;&ta(_o7Pr`kHeP_dqxvqIvU zx6u6jo7|a3-)0L&f3+F*0_Nnon*Y9UB#0e5If}zEmCS6b*@-4Ay)w{!TA2 zD3brmWu5C>ptpIX?wfu+hTI$2G53k(e1bh>$Ea5sPu`$iq$zh4{n>-Sl+&!aJ?u|QwlRnQI?q}fM||TRIko4( zKePgJ$xGyn2PfMxyjgcY@}E;U<{4$jy%MGVj(luiAKhWR#B$!hbM!#uv8)LDF$|GELZ$!Hq%+b!qhf&UkvwZHM?^rML z5!_!Y_jB**0`K*UFhk|qXhW48VXjLap-FF%-_bIujYMbbg%1?VuP2O_x@}U&o`-~% zzb`kmdNf(KBmCqr$recp=8?aQavX4Yhiyq1^V@HfecTs{jJ&T&w8tja(sBe8lZ-{>M!@7Wu zDK!f^FSTG3@y$uZtR1XxbkWOl2Pjp2iT5lC(<;9wZ}ti`)kj8nnxCEF_MVsS@m)#n z-|cBeIlI5H8tv;6g;>xra=2sYYgLYp?3cu=sk5(r0>4doyWhmOYb3t4laYvhwGn-2 z2Y8~eKF4xze@(XyShLaK05zuYowWjV9E-+-JG-%Yy7Fko-r&0Ce#}}O4-Q?zep&}F zB?;T6M~s#eh93u?DyMU-nwN+FRfDa;n$HR5y|>_Bo7g`5BX~y!-51EwZ6NBB^gbDA zOBiy{2zl}wpH;(!d;v_W#xHsm+vfr6`xtBEG;6^^jLMESg$eA*<~?fTimIqp=|%3~ zW|*NcFx-?cMH%y-u%>2!sl7QiJ^thXQyzjdc|7XcL&U3dlH;@->N|;NnVtk;_i=o= zFSC!jzxEgV^%*uHyvN*||JiF=ZUl8|V0S?)_TwV&6HCg|_c0ipv$K!CkURSh&9u+a zhozpZ@(cPYZX;IR1G+$UV5}4SkiK3g{V4%z4A0E&nWzC%8v68-HvV9OZd zK0P@v&wxSjrr~X9`TBk0I5Q9X~*iQ{N8rQ|D z=66RLEEgvmt>4YkYz?UfdjdK9H0-0T#FWmbiT;^uC7)nys``lrzh5S3-oH`r;XpH_ zYMvPIa4I$W;Lj^5qN5$Txs71qV07HlR!V_+yF^F8Da96iSh086CE2E;S9Erc(p|15GaL7TNx)nGSLGFI$9<}PJLsDJS9v7#aHie^&_TQou|qb|O@2whmTVQZ;>tqw*D{$s{W zY_>pbrA}bfI>u;lXdO5-6C8Sx{BQ1leHr3^6^e8v4B4?QUE z-v!jJ!A~^g>NO?g!TyWm>;l^k@w_wd|H83c#};VCF%uKZO8|d$Efn-X^4GC1hrrAB zuC`syaNL+nAb!*tbP`S7a2W;|l_k3MDa{e^xyA7z_D`fV}6IIg-a}lwJUgTIu)7R=>aA+6Df0yH( z1g4Vf%qv2FSJ$aq#`!>x1)J26zln2J!TlkC&yVxnS>(`e;g1=p9coYA{ZH^nYDRl% z>qa#@t423BgGUW>Cd%~*bDBiQ_Ga_$qfXz4N1PTZyV;h7e^!3b>3e3A+ftpbdgm>Z z4I|dN)By)w-gl0}7bHg1bW{v+9+vETGX+QLW+C*A4597zG@*6S8Yy`2DtMgfVz6PY z(0)R?+^#=4`0ezWZ#_eE99m=w96QU)9K2f8W-k|m?yXjWo}R7xyppV#pB|?A#0)q3 zWR5X=$y1Dern!pa#sVdH_*S&f)(W=oR)~Jbh(n)%TjULYd-^)TPuu3PN^9Mr%Thh9 zdQA6(xXI^Vz(0I>hHR~#A={@fmIHkEOWyB&Ao)#$=iY9MX!~rjVtzSUlinO@5I2of zrQaq=hW;}pC1bwij9)7@+}|Fs;Tgm zYetCGq2!ke!nMK?gN+4K2bl_E2OEpWq4_Z*(opCH=IA_%uZEfOK11&S9Lg_1JL(uX zgzZ%JExG=Bcxgkhh5DhjXC7v(GL1FX_gO9&+U#@t z_*C1$ovFkNext7AWuEs3Ti#{7f?s$f+EBI@y~e$89Bcb4HIIo)?+0tvf^OCGG2T*p2;gHJXNpUgAJSdV~y)^nHsb-YFS^S!2`V~*%sC(+Qj+foANq?N1uT`PxDUK5|` z+`vfY{u0)~LmoZYU&q;(JKzy@x`k=jLw$(1Ux-pm0_f$s2poD9e(6f?6=-UdL~yOn zV57ow$`4`f==G`z?2|9?=cmDrp=@7|9qHO=*Z~Dk^FR1oV4ERd#=g;O{}&Q#`Z~&# zR{(GNtwht4UncrKUcN@FJVoAV4w|#@aSG3(pPzvinhou=K4{4rhkDAYz@xFzLIv8e zHBHz)Tgh2H4ENLw40qT45$>*k90s?a{AV|=HNAeSgQzRq6>g~T8t7Hr9AzqcH`-iU z5pSw$J=e>4YLhXzVUwZL`AyzWe|o|k`05SK_SQv%#d2P?Y`!YnpFXGA4;@#n|6Gx* zKVBE@3r@=RqCIZ^-sytZzvG?G*TP-(e}F@w)R}!9Csjt%*HepC%AUk3<#(`&^g3TX z2K^o9DF@%O5jzhKQBDZ=kMqon`CRMS*vv~gR=vlQH~fXea6G=i*4zwU&%(a?8ytE_ zf9W3*J@v68o1GtxYHl7n*421nlu}cYs>e(%_cc*=l?}MG9c5R;^eU&BnmC|=8ZlmZu zZn3EJOVA8|MQh$$qBK+SD3kfX49zlfmKw1Akl^UQ(-V-qNw8+G7X9{P>->+{^_dLO znwjNsY}nxreXzparbmi9^ulb>F>VfcGfT3LhF26gTekOGt@!+uCHp+0S9alg$!?sd znt~>1(zoMPQ5!3%*^?w~0Syei zntxcQ)Lxq|S3WxeJ>k(pUC(&A;^!!(lzxdNAJC`uFnZMga^L+h%20F%|709K*(&r; z(Sy$?x06>24s|3xbU9Kf|B2qM$x&*x5-r!l{VhK{MyeV;*W-M3wOjj$ek#vzm9&Hu z)zdCosTHZ`|6zbuRzgh2Mck)1eXa6|Mbqo5qMUJx@eR4UAGu$C7fz0sxRaA!W&(bn zZs+`gzx9M#rJ3}C$fa&c*K3?80yOYzZ);W;g1oh=84`oP0(B=1I_Hta09@E+SU^Z8{S>Df_KfQ|Dl$G40Bi(tS< zB3EX}?+8DkD;keM^u=jfuU03dNi~1POO*;WHm%8xZ3KTphq^1DiFa3y8s@4@8s@3+ zqTlNRY7KqpFTD`k=d&XIYJc*$k=}v^YsD$WG3K0PUvgHl`(Bl8 zCvHi$k?2u;bXl}_r{`JfE~n3P(_PYMVXnr{Bi)VN=%sgn+QcC0Am`~lhSAN>L$i7% zc3l$JPUD=<;aWCuEx87HA2JU-%xk^3Ew#)#7oy-h_|uaU;dpUv3h>Y9*HJhM|7kxpmPICtahQJ#jG%bL|-_(Q|9n=L7aT-Lvjd2B0)L#ro5 z>$mV2JFji@e|uiN^}%MR|4Ro1|L4yu_NOn&p`izapaE%eNT;Pr$dVOGNS{@5u$5k` zYI7_!|y zOS1%d`f|lEY##bR!&UEJ;tVE#`YFCWU9$%e7x(NHg7P+s0j~9;@A*}drQ2dPC~~bF zSWQptjr%-7f3Fcj(q;>e7Ad0j$62!f$`r}6dxmIxezIbFh<)@9c>+(G8q{NjVqY^u z@;)_6QWlMoW#a@%J~>TRE$Du9SR%SAGCfU84+~A(k4d#>!J?PXmwmTRV>$vBX{ zuOSI+4_Hg5xn|#! za|mG^8EPzC8Ksq89I8~bp{}q?oK$;anB4Fv!BFw-0#Ea}t)gh$poj&l70<}!vggmK za#LljRNa+Wc-=s)JQiQ-U3@Y6Ow>@vQI&-sg$_|g5At;@zzlkVl!t_?Rexe9Ik9ir z;=}y~{!9g@`Y?J>Cy>aXH%R$G_)ZU^=!03Wn?ehx$b-WrL{uTL&KJ zV;^1wlg7ebkR!Chcz%}z&QLp--!evJ`zji+=qG}&7gQ=SLn3+kGTw`hl*_h9dul__&-i4LyZYV)cg19Msz0O$!X5f^gu!8qLqoPtoV%)O zSaaReG0pYK^jVw&r!jz7(33EC-EHik*4QK)!#uSPFzNYlp>{kv3dLO4u3Xn4T$6V6 zb7@1Ix*%GuoDJ7!+Dy4&V3wqwzb#poUeN*{A2tUc-r&`)bf+^Tfl={w6Y#en3&}B^O^55InhJ&!6vFct%UmSQ!PoGrNF_fq!y_w z?xFPyYz9~6| z-VlSmE(*4t7e()|Xv==L1TE@#)o0oWqwl9PR7YjD=y;vJ)^DwmTNJF2>{ByD ze{;HMZv*Dh@7nR+0m1gt8Z;fzp6x$X@_TE#?0XsBb|yX?&uv*rivN|#ie>W*#l9~^ zw)RRE&1d5!@vC^*RW?>}XHS#V*0V(8q=k~3czBcV5urKrm{dnRsrpUg)6;k3qh+B1 zLLP0@MyX-fYN>ABY){?0iE_n(I75+whHuqiQ_)|H%i+eN&e#rVT%UEsF$QsOuK|mu za-GcFPs(Bp#mmW+4<(*Fc#@~8E?KNGC%~7TEY%OsaEgW5g0yIp>i%NBsn&0jRv$4! ztsWRBqm!jn>>~FSNbY?fS`g>pGQNifem;1jYcCq$QHtc?E>WxIK+CvQgjTs4O!ktDX=e&u5hIVN!SrUj3Vo`2+C@|t8Fc1^N<_JJ7C_NExP z^|}}^2A}o%Ezy1z+tH9E+J=H5ua7kNE>BVIN6EFv(r2S|i)?qSkZt4AMW4;-f?x77 zDd75M$u@ht*h<=ne!y;=Gmg<%w);8X|nA7 z??hS6ikF(-ikGX$C3)(*Px2T(O;SvznX2>a^>R~ow$MD|s8siMwpx|APpvM1%QzE# z{BB#6`rk8@`gIvX{n^FNrsJc8swVuDj__>W9%Lv&i@LZwvCk*;Nkpe2-=7$Uu2D#D zo18veKQzJe=@(u+K2j^!{qDLcXizN@>hfp6k52J4r0!^z!Y;bi@mY$y?<`Z@uZhO$ zw}=}(6`_<5C6?0~?$5u(wjY8)lf#7is}W+|5Nb(YCNA^@?vI@j8L3p%4O6OC#i>D~yx8u+rMKdxAo&N^D{}^qqGxU^j zMkys9GyLE-UZuCrR$|r@BjNHz!exUWSPwVhTXJgD-WM9dfR^A4v6MXWOL;Q(O$clG zE7tNtFl8p6WiftbJ?nX+XIcAac>Iyau3!ptJGV9VPbZ#d@*DWl`uhb5V33VD{|8tz zfo=cBNMpOj*iIXGkC=49K(Gkzc77iFplc)zWetev2B7<1tu1rTAHJr5AZ-xZ?%yvQ{+y^KbS69*o|j8ZMIY$zR-4s4FShMb7OgV;wkj znf@lT(7u02P4U}skvC)aWYRDC8hpf4Xiv956aNLcp+Dk#9ioQxGc=q&ixtbx;iH`b zkJ`j~s-j~&RR`fN%3;oiD{zUj!=1JB!d-Qhk)E24VPbVP_l1LC&@*9Ll@A)i!y}}I z_3#+SL>VgRzf@(NqSY=-Q^mX!ih2H3)#ul1s&DgogWt%lh5&V$xy`Wk#t8sMcwP`mLacGBDE#VsCYEXZK4(^mvLSt_&A7rX&} z*%ZcVu1^V=e;3V|4d_1(i4>X^_LCbkdn>i)`-=4mqa^X}2AB8jBToP36E52)C*Af9 zXNABOmxPeEm&M?Rdt8B+$H~^dVS;>fgs2rR6ark!8fP^@|qa%%PrCI)fL5Z z@Um)OeO(TE<~CZ>w`99OOy9Uc^q-19H7HeeT-hlH+&LuM-aIHfoPF@u|@DdzfJHCpq^s_c;i1( zm4_yXuG+=!hBvmmo1R|fGW0* zn&pq^$MSomsc>YtQPp?a>+ykw_S>3q4_tHpkg)@Fg~dYovg zaz+@+Hb$yt8T5h;2-hln@RN?xuReo0)1TfzvE<1z!jm_39+4&X}lF zKa7%VCJpdPl%(pXWrhUR%T*{m|hbGRC5mH$h zeJ9?)rr3$CQp=hy#75V()I0N?*TEe)QiY#lPkhAx?j`=)2Ag9zYyKEoKb`PtZhN}G_!~GaKlAzkexa^0w2S|N3s7PuZWE6^HIlefcfPk7OhLP?#JaOjT+u{s3oq~Ait=|O7E zS#XG6X0=ICY7M>SsvN`2mHnnk4Zp5X#V-#k-j)-Zcl&di@0YBxg(sBYE^D-Q-7*aA z#WjWw|7J-o|3JI>=Ur0kkN3%eOUW^vJ)_t@J0SVq-sMp<-h4zcat#Owm)om=9N~;2Ub2(T2m0G#0?ODumOTmx!H= z=3mKR^z^O~L$h#yxIzD>Bg~&4Id9SQYa2gMYzpcvH~jjZQd{0rskt&xX--TM%zisu zerboA{hvMMv~@V+3H;=O5cu?UPhi)5a)5b)8Zd5%q&CG$%8?Y=qAidE4YL%-Q`1!I z+J)$IQ(Jm~+Oqo#Rmc6ElI7-Rp+(a&sm0z@*?u!kvF2mrl%A9QOR87uDdmZz_QUFUtWR&GzxRTu4{>|0Z1+s!1^=Rmw_x-m{MzcWXm$E#>d-tDn>Jm9gK zG6Y*l3N>4!RgE4Mo~3hz#;)|UAGpaa&z|E^FU}U4ml7X(>ZsfK>@l&?KU=Ndgyv8@ zeH6dRQtFLcmHI0gO2gL~V*Rj1LSx?~XLAQ^@;StUwnUopE@1~bxo*CU9In$sMl$!2 zAHbv4jGHm0{K!aS(If2Y476bH!i{YhD%aP>D)p`5Fa5C5t^IVw<<)VgV7jzKb0>~B zSIs0Z@&$42N;uD75(|189Lk`+^)}-=x%r>5ci{Py>2cU(Vj#C;<;utebkJhunh0|H z`_Wr_i#+UQ>Rah^Q~44+-d|Zeb66vF#80QP|2foHkEE{SFg}JrpRfFAsVs|o`5EFY z?~vB!;?AS2zj*JjbMphXA#Wjx;}xft(8Yi zsE$Fce6APYK}Rg_7Tc-^^V+fRL!;H=@AUq%;KjGtN7h)WxP;o1Z@?jHbc@Gf&piTn z-ovJ}!bdO?qh{Yqx<<;SaAztypm*{nc$NvqWU=OYk$>N%>*W(m&%+kddxW6LUCG&Z{5x<3EvNRSZ26z0h{q!uf5<*oNLzGX7T$*Wd?u zLuuGU`$o7c-%WH?bpwx%3=^tXldsxK{{B?7r}7=Nde>uLJ-aZ3cOs8b?wwWOyZ4gL zPgjtxZhP1vPakGn?j56@Z$bC)A9`JVb7+@Q*`z~69_g?vk9IA~W8CgQyJUtBeg%AH zSBG<~e+;31mtl48FS9u(RoR@ss?c2L=95mpH5f@tw#y7??Gx{>Os2igTr(W{D*pp5~jalD`Os0Kw2KAXSdMdA;v%jWdgk7bvEK4x z%zkkiEpM|3A=Os3YBwP^W-*#~tk~`3FErnQzy54++q;%Qc#rW68GBrNo8w>qN7 zl|xL2d4esjz#w2v%SYgBM&LaD2hLry@jJq7tLB9`myf_~iCZ|guUbO2eTTdb-dwHo z7Vs^~i#co-?bAb0F(=*8;%fh{$t z-;c*$Utq@{U<`D+H5y`)w~$-CfPEhTp4$=k-N${O0cXJdKRF9rcnRlccp5gA06)+_ z-#{RDBkl1NVx(^1(3JqH`6uL(S@g;|UJn7K$G4(bacI=tc08x#nyj{vRP8NFuD zp|^ECzUN`ko-f7se8Y?Efd67^26(?}Xw3W(L%-$0wSDEyc3$@uI=%~&yJNDg+J*y~ z?gPX&{l^*I`{!80b<8qLyG{>DxJ0@oo+aGB-e(&TwT~L+TFMPA$r8s5*~X6QtE8OL z&Jg;8w=6pSL92AU!m4vh)8x;B3F~##_-cH}&SRlkC(Z?pFCr$iK*L4hyZ;fGv>!P1 z5_~>JqBa(d-!=4IHGiWfd@Yo1+8@ERLbjQ6}OlQoSTw65e-Q1hbwN819?fT7Mv%0S8=OT;D`m!Syr}r+~99}wT z)BU!UG#-oKbYF!s+)LrC@J}3LVpB+m7;wJI8phy|OdCf-Gx$L&1U7>?CzVp(`9x!Z;7?W~BK=l$f@cG9KAOyH2mB9WNiE zoa3rVx4r}9ka}>_&gfHJdWd$Jc7ky&J<7Ov9w1$F_t{*>muXHvmDxaPi@>5K0ZapG zWOZl3yV$jY;Dg~+h8a58>vyqE*z+pnVDAHmcmspS5f?!}P-{l4WY_oF`6lQH8h^pskK=m>7&IUK?qRt60pH2@SR;Wu%W$mM ze5e*rAF6pj;-*R9x84TM{DSv{*5ZG9B@wygQ@FkaJb4~!L=>bsEDC_%#~=Q-&fXA9*Dt}g&Raob`)%M* zYM`y-l^|Q!^O*fw=S8&n;{P-h=ZXP5f0rlOHWuIEOq_eN!BH9)8fRQp$mwVs7ug_}#aJqvH_$(S)QaTeF!F^g^6G=poMI)iT?@-E$f$Jb_a zi8t$R?X@^Wp0gRS*L5##q70ucV}$tSjG&993`Y`Z=lQ9m<3-GZ-m-xR>Ld-<(1@KhB|tDY=-Xk;fWd2EY6? z@WZ^5alf~la!TJrITut@?({y=eb+wCeQ71>EWbyN7S0kQ-4BuOnR_VrpDIWv4SAk; zNHaxXIhGP`5A*2}eyFFdfCn16mC}#hOzK`qAsju|5)N!Sp^M6+4BPTibIT)L%$c;# z9K{Im$)h5}Ici=!&8|qL_;<2N-g65H-T%ag_NtTiN@@7UxoHVg*Jm5;&BOceM3Vs+kJBc^&xDg;)o_ZRBC_*21~gjF@H_u%s8UN(1&g`X*|R0t<5S_;|!7 z=fL^T0S={kigj;-b2yF1e~ahM#r?prnlR+R_8R;*IIh#k2`+=jvd{5G9sr+Ps%?ZWRHI9Qu2s-fkhv29tfiF+S9)qu24f;}_{Ei&N3%nkDgz9(V{ldXR zy@KNjKpgc0u(<~qq2mAP2#jg=)w(^rwXPm~SNDKPi!np`2gIBLYU_BPRtI1c2^@MQ zz}j^z1U;>YPk#-@+-Y220RQknc=m=P7RrWC-Em-1CLaF+Vxm8QpN*(<_F<;NtDbb{ zm+0MT^kVvx;y82Jda5sa3p04Hij}^+$eD64Gp7Al7{{<1v|G|m+LgaSx^~?lU8bHB z^dt7M?%(B$V_PPodk3BsZ?herzbUkTGMjIoHj))uEuoCYT>HNKB~8=6qbDprOHQcYO${4U zz>K5|Xr~Fqw29tFIevDSa&g&942{}JjoMy7j~os>qH-y>%Q=jr$9mTBcm%7y9L|Wf zv9w`+66xleNxG*dvksrE;KUaK80xaWBSEyfJ}hv(_^fE{*9~o6B_GFQL0X zKE!mCVTQENF0Ox8K2L-u3rxvIk(!z!Q1KZowQD0|{UBQC{vc9rn;$N>{2H#b?+H`e zEx}6bi@{<`Z>ZQZ7Hec6-*gb?+a6#nh`!ZQ-)?`YFO<<7$IwAa4 zJHWfPe}vypE_}=ivEQa(&-@1bX~TSu`F>=peTLUH=$|*@bG(av1uSZeL++M}bp?C2 z5%s@vdmlCK*8_`wL{9f6_Qy8tCkiKPKLG*2f=h`M{x-rMG!wN;3BO8wxe?B;T348rzU<`WE?LDQ9 z2J|<~w=yosJnLvQM*&~^`^X7>YlBIt-}>>kt$@fhG+-F)!W zH-SSZaa@>9+Nk1~G|c94@S@u10#jQM@4bR!MLyS*4b5~@01bZ;1~neG4s)yR`lLF$ zm+MlT=QrcEuj2Y?d{6Db(p{cZ$EWa7obIo6odvGFjpLdQoS2Ik=z*u!m5BQMZ@{5^ zV2}%Pu_%0J*Zs+k+kRx{9^jPG&)VbSZ|fc!K(;gZe>emY?S7b@;te11xtMF&g4@pk zoBj#bx*zyx-NAT027W|0@IBA=Ap0B;M^_`?ZU|y+Bl1`}W;u&|AS`?et)=ep|TMIwMll(xw?|E9JHDSybVtr2dfn#{163Y2bCAEq7M;+?yJYn95$gu{JqZ>? zNzwFlCh0U5Iw6lF%Kfgrrwg_9_;^--cO&g^t$=cV1D>oyfI}CHY4?L$S(n?G*|R!D zbE_)S3{!R!`rGh_UI~x-+5(Hi&^5ICeE3nvucejhjkF1Qj>}!-cHd@Grmwcq4*zWj zS5!(Hl1mtQ)eX_v?>Ij)x|kmJDg4pWz(swV$vE}FUoPwb<<@qDcFQ?HIsaCQxzU@c zp$!@I(A(h6r@{{wGeDFAZ&G+Kf>ggxvAHyECLOD@C__ggEiPV4bN#_Iy(564cKMQ| z#h)TdmvQ}5li2R2VzTGd5wiXLOH{|n16=#(sZ!5ZiL!N4yh7aBAX9g91-5tx%e-E| zQokkfRwaQS>`4$TPpy-+S#f&Hp(uUtt1G1Lrf8|BDN5-M3|CvX1xWR??OF`zRA2XD zIvwDx=NdruR>cz5aoHBOyV$J6Ra*3MC4@LXg|+;%QtAnWe|`$;&YQpuy}FodKMbzm zIDU&mkpsPjy&t@Q>S_jOy&k=g*}$h9%tiYZzx_AtziGrl(cmyrz}Zd#U-B|yq-mI2 zff`j4c-N*`*o&7Ddo-dqWw!LPuMXNn&QSf#0g`-yBqMx@% zuoLx|Wr$HWBYv{?l6`^KD+TuizajM2qn`t@7*?}CE~g^i;@du z*Zy?F0QA0hptXAy@!37#4rVdh`=jcbz|o(^%$xCuecl81=6Yy7E1`A%12}{JhqfSa z`DtFjdc=uQ@RnYUdL;I6=a;CT?MCkV2J*I8f2w1suhw(Y*Vc2!-`evUIHL3T-#7t> z#6Y6`pAe$$EM{De4<_4>1{0mvpsN`OLT^W)t+xSlJHnwUy0S$~yjz_$KNmV`{xincjb&c0T!@;QaDQ!BM|W z8GSih_jJfsVf3>Z^0>SLY54qNNp-uVxz-;eT&5JWE}s`M`W4$WVQm;;EAv3T7#>@1 z1!E3PDAiF04QDUno)M^pt@0HxBY?*&GR)FN|JYjK(<^~u-GN}SE^?{RY>Sb)k~d1` z_BC>kOT66WyjHU8PZOx62`s&21xZ)?61HF9MQWMLc4W@tJ7!JeIwEE=UBAsDx_f48 zy~;blw%7Y{sRkh;e$ekam-csbQ&k%+UBvtov=mAm3DI2Cp*0xHO*D-(O8AH&SiJ zs3*BcgwRZGAm}?)=FgnzwMXwKdtooZV>_u9Xfd z3-(#nlWjuZJv@iH z=O8Y*f!rJVkG7wn6&s6KW(D^1N?<_`_V)qA8t|dVjB>vIQ^aO7Fxv~h^(|A-PhIW@ zFKyJ``=LEXUwXaWH>?$p!A$1*U(ruw|N8;yf9{lFl*))BMB>YoFyECTjiLL3BasfA}v z{S4gqBJgiJ@}|FlLx^c=@B6T|mw-dzz#ZgwjZ=Xm`@uQg0A5bUih$q8b>I($<2()Q zy9e&)b$q@HUUc&r{GEd9r+|gcxc?d+{{{NZ0>Fc#M%`%FD6NG?pc~k;2RL*Cmv(>1 zQrzZ<{OH#pt?LQuS;byjj|g2)ENb%S(3@&Te)^Rc+1lrcx)Lzz5@teg1;>-=t##f8 zPQ3~LMKj+20<=RD{D^Mai|E{jILaZ=+WQw?(;FH1I5zN!G|8!ZhobK~#@R0M)GvV%i;0j~@%~h$?7LoKT;C z4)eVE0HNVWaG$%uf!acZrbS_5(}Oj9cmF17;ADnu$xW38zXTq2Cd;-(St9jJD#xbA zarDCg##;0)+kNjHwqx;Jw)5+0T$?tN?fmXdva@>*;;=bPcjQ8H;NwLW%Uz#-@>o_pYOyQkK$+V+bLzD>P~62136-oz$6ANp;zJLatdw z$ooPmZf6uB99c!O+gH-I5963Y-xPY#tB@S%IH(PF9wjaF_A$1Y?V@#kp=hy{OV+(5 z5`C$Z=WS)IaI2W(hUExUcZx*ZNs{S@>534arAmIO3fGaSYGsLv7P?-hK3}g8wpd-i zGh(9$OZncFA#~r)09#+iGP7-2_8#UBHUh5NqrJ2B5E^ zP6j5xYa2bLTq;7}R3muq+~yyj~w zkmo2^XYiWe0ArkiXW!#}-tZD?=HWff&)&uVSyygf6AcgKelnj>Mwg{RI5(7oJq}Szyo*PrC7>H{EF05!?W_ zO~Px3<26@;tD6pt{1ER6kEq&IU+MAJ;Db&Bi|jpYK{&oVa5hGNqB97(hkeMA-uAZj z=zX*fdyo8GaLPTvqIZGCt573*7dR7yoNKqAtvd`@)P~yPA!q{H!5vKk-ps@03e@g@ z_P6$if|uTn`n@Og)Kdb8?ua0wJ3T<_-4|%tI*(K>yf8vS(&ILyq3Y3qMv<(n}X8eeF%sX*u$+0ASITo3!(XS1`}?0_VV7;$582 z3MTH9D7|--aTr;`kGznhj{jz>IQHKnMcRJD{Oo6~ZL>zS_Y93buF;eC*c87#gW`{6 z7U7L-t56eeBcL@Hz`UrIYv3qvp&mTeU#v~QxvU2@ms4oJy)`IW{cXsbV(bMDN$3iMK(E0;KSBR?2EwyacnW$Hx1`39W?(Hv$^)v zx0&w!ZxNj>?@+yW(4(!7p!zbr2W-#mp{$4Nb|D@$LJ!;<+(E2 z8S|vwY^zv<`*K#98^s!zt!7~vKi~bDb?!mCc5=I#7!|479=X987qVz)l^Gp=)cr;vc9TOs{)4WCDR4yg| zx{Z`PcR?Syi!jZr(2UW$NQW``j8nvBPF=8vFqXeJ@Iv|x%hbcCSm)R(%3vuXot}bM z|M8Wi;YO5ADTudXWRxs)E9Rl-`j3Psz0B@!{DQY79uq>wrL75d+Di4EH&vM=qBSRzNFQ&JSoF8@lb%CE&q589UC@DtPW}?DuH!&XM4ip~bC%FE(;6rr|u^FAK5I96aVQ zFz!RdN8qg*J_d)Jj+o^UYVuOEfH{S8(upLxR!$N;3P0G4WDTycqg3e zA8y2)E*(6CV&F+q3mkd}I9>^UZ2{hIrytcxpcl*n?eqp#^V=)HB|f=oX-9Gyse29CQLWx^uvy3Ljh7YJaU`B6QUMp_bp}Pjr19Kz4oZ zkNL`ocWws|U9+GwLY=>7(IQ*l7w`nS?xUGM@z?rl5(l(f`5O6V7A-_=<<;9|jOqF@ z)?w&%+Mx?xjK6&Zoy!N5YuE+Wq5K%~gHxRIJBK;Lq$)-gF)yg*FzeR5Ll}3dLUi@6 zkPIK4rbkqL+B-S!Gqd}#!&W|Zk3~Oak45_TkX4=zpR~w0Lii&{v!U;%J;Yb6KjI_R z{NpFr9taYeT!W?NkAlUC>IDN|&& z#Ei+7SW}KDRBe#Oxhq5%7;)CR7o7E#VyFoWceO`PlTWr8v+f)huEaMDHeV2ocO z7(L=R)2dkB)Us4`_z~CZlW1c%Jgh%S;GJE=1U>D~%5(_B{j!4M1N~|KptmT#vV@o1 zFk|s7w9ungaPp{Sf_|g7U<~sV#a*5P@14lX&WW`C`DA!3<`7P4yEH@SF4A~Ak8{># zat^<2rF9(#Y|dwnl0#A}dBgM7oEme0boK^UbS;)P2{D>7Ce6lo!GG?y%j6&h#O+BWu4n+na+nFD5h&R(EqXM+3(b2qWq zp&>-y72EJ3u%;f^u@E&62Kpw{o$3p{`1%3FHPA0Nz6)&uG=+6}h!IrmPiVrQbl|bT zpa%HD);VF{+PPu7KZf1M;|gAT3~(q9x#IwM>h0jmX=t*qFJYPjJ-M0_z^+GlU;B)C zBd`bl8nr=Kc2BTe=p6s@V(LFYtm6gGAPs$MUn58R5}1U3(}vHWi6j1pLFltUzBt;8 zYbu6jJpp{r3O~B-qz~O5f}E&h3E6RL5!taC{LL!#YRz1v^?r;#(Z`qxmH=-t0r86! zx!DuMK~2z4?7{2f_)&|)oO&#~ugDqrEOub#JQ*N7dJ#B#3OH1Qm@EMpgt^qM5;WKF zw{JZL&dmhA=o|1uD^VA7#G(*`b)mnk1i16Br`8hy9E|s}^>`we%Yv538{g|4aBK&V zt38LD?Q38egB%XN`JF!@2Sbk5`7-e68N^1vga* z(FbVLh)Tk6@C2y_qvt-MiXEO;#k+>bRN3ufl`}v^^hh_ z-%HB(GBokkcpKFgLU+o3LQ^8JXiTusNQcU;ov}*KgIKN?btd!lWC?kfOx(_v2qIr3 z7Uf9PgItNO$dZ_InKJWNmduRFklCgTS?EfV#c687zuzSEQ|7NH`a7bC{_W^Hi}E+y)V00rvWx*?-gc|%1h1do1EkZ(;AAc(vTh55 zSovHa%gg>8mm9?MrsaZ;jOC2SR|*E-Wuh(&{qiC-j0e`yPG-z8dn%eU8sN#{ypGf@ zT0^L>h6}o{S19V!5rTq#BYuuA&20?jgfAj^wIPt#ZSm!$3Lj1$vz}F-TgRA4c)gBK zrJT0q(z<T1nB(|n1T8LK zOUS<2nwpfSsiX2Vaakt8J0y?->i1$c=9zt!LJH@0kYZ^eEu75b_^NE4JGh0X4ixf4 zO@%~WI;c{;$Mw{`;|4mmTFQ6k1;-ljf}oQI#z-A;~H;D_O_=l%uEz^JU|T zJlR-=bs}Fj-QFUpk9Y8L?oL)!vRUz?1cv@`waqpwb%2^wKA$Em z48?2yhd9X2J0AxQWnpc?>;d$lG_OT|225&kMQtt+?>86NHwj$LT~D&T6mtlBJ;-)t zk=7}}SL_?qoR%Y3n~XaCMI1YPhU)Wh9G6iOJAis!4(fH|fkTfl|B!;NvJRhdEb_)< zz`M!F0SC|YI_aeGRAO@TZU*3s}$&Q&GL`S%{ zt!pf_i8jP+&@(mP02a~Uldb}X79*}I_9DA>p`Y|V;-Fo?pf+&XjliK1fmCM)Vj?r} zbjU($@1PgiyWB?`NDj6Tv6}`MYq?e4TtT|b$`r?BZ`3_MG)4FPwS0Nxq!QlgcxyQ!G5ZMRlP#LGV1ved1;50AAg&X?ndWCBxHfZ)+`BMYv@J+x zw3m|^^6w0R8J8{5sfd4;Rs7LvSa@ge3) zmxQtUB;;yh5T|-^7|UzAf$21oB52c zbStBN5AU`!xvb+KsjTb72ws28mll?VFyebFY5j;bm^Ztg(0vk5Nv)ZNW?&O4RsQobnX4#P z=lf9aLUQ(}?ebnT|I?nAV_ZvOPb9X+8`Opi21h1;U@> zMXVg~(=Q-L3ql{H5Ay7G-1Y%*C>S_&9XJ#Vj}GXA8?vzXYZ2r0W8XnfR$qiY4((vw z)4-r6;K@0xzYyDSh+Cj%uGK>?xCXh?QQUqOI8+KuLSI!KG?{e_bZck8HE)Cu{Zhmb zm?s184!&+8a=tk9BoU}7LD^{8XtME^?$;n<09r#?m$em7@FKI zz`aIjgI~deF{GRIQl;9eP@%< z_i+v!-#%zToiTUr0`O=E`aUQ5kzEtL$nINSTHht)WKH1u>%HMw=R@^*N7+a8Rod2uTOwaG5^uHG&CMdE<83lt}cd?B=v)#r$+-XxL?$^|V zTS@-YO(gBP0sb@b?BIuCQm+up_s6Ukh>A>}4M=0@)oC0W{RPH7Q)C}xip-~3B2$3% zVy47y$r8nwEJ=QnsR$M6s&F$+Cz_HKu0K_z{qejP)(B+mGKn<$Fj}b>VSV6j8O#i@ z*s}ZvEptK!HHR=W^(fG)aX|xQbYwSqX>|`#nPL$p7ma?HdG$LBC7?NBfyVV1k} zUJxTUhSAEaF{GkjK?&WFG@H4SX77R z(jMl)wxQ>t#jXKb4UWYglfX|8c?P&?Z=u#7T0m(3YT>7d8Z1|XTKJPj?7z{#9Owya zJ_QyvVy?s&@M+BiW}L-+7Z4wn!e{RZYFc*h4Ik{)r=e@Z?9~PdeAMsIEyV+eeqBs; ztitRfk40c8~h>RZ7-*W>S@xQqo4-H&x)G257c{@Odx4qU{lL62B3_V}y7D$GKu5A1X9 z4#$NVWi^f9o@8L%2jI+~NA1wgeZ!-uz7;+mS&>Zbv@pJA0%D>{9N$fRN0;y!S0OH2 z4IFGkkIK*R6uRVR>pclBCl371HN-%T$kE232L}Bs?O%FR9Znu}+kJ3X$1$Ve5aOcU z_zt{*HGcrZToL!Yi0|dWV!E{wxLJgC0FN099?cVc+Dp*EkAkN;j%N-oHiuwrd+c3Q7*?TH7Cn%o5@n5>2Kz0 zYSHFFu`}PyO)s{vNflO+++)>EuOyVag(UxXE=B*BO$(7pJU1AnXb0A^v^j|vzD?(a z+Ekvc&gALD41t=NB`^)Zq1o92=aD6{D>6mik||2>XUp=OY(*HGr3(8~bmF~@I{K4j zS@TX8h%4y=vu`!_PYB0=bJMcDE!O-%vn?~oY_$bhwWeSzxq6v}p_cd2pRed5HpFz< zwr?0<$80Bb^Q$$}zzM>s4Ze$?fyY@9DatN!w4oXESU+7)xxb0t*Hx=I)AyL~dL6OQ zTd|BWVl{2*!Tk-Hq|24fq)S_#=JeHWLWr!CsLo1`J${>Z3@jvu^@IziBfz#FF>iWY zASaCt=B0z-f*6`fyR2Tv8fT|dx{vcTV{ba){M8!9DI2wauTWOKyoNMfNhTfd0te4P z-+erl(yd)j>mSE4y1$}n`Q<2DSsqF2)&@~>pC2Jk4J5=M)XC1rP|S$+Br|_K#mw2r zFxzukcI_UP=T5N7sw1rO-62jKe^}y6kE(pn2?GYvn3#%F4)lW44)ilejO^fEmAh9Y zb6Hzu`sEyj3C&db#c4WeXqsN!3@+&L7Df0wUy)wfDydVeL^HHW}jz1 z9(mQPz$DD2ZdwNY5qu)r7TEU@_Sk-$>wV#?d;$BhA2{KTdet@5$DM(DhrxY2gA0aF z#KYH6pUQ!MbQ&I$2!3TU;))nx%2HrJJ#eTAz9NT_lO-S@bAnIMH^|5CAhwCcjixwVw^?bgEEn8?Zsy`fg^t% z{wR$eRQqZ0=om1~K2HjMb1lcJYh5W2GK^bpKRGw-`P@Wo}Bt4as$&Z+k$GYb4U>v_Fq#eK7MLI6nPdY5$tGPrK z(yrY_l=0P4n|!{^DmCX>%k0>Em|Y7+rxNv#bVNixk(n2pT!y+XdCSIC#^sG zP+DaGK?SVrV`j#7(SNRPCnrJAxH^|mT}ufEsmkWkxs@1lb}j9481plZW@)B|BAcl` zix}dbz&K{22mcn`eE%JWr@hA9_Lcdd$$rUZ_2T(lMRD9N$-h+? z*q`@0i1o#S?tH3E8o$~?PhZZm-m5tN>-0fse#L-F?Xf7Qvk5fOPy-<`=-*fhjtITX zbCwA82Qd?526{@jAm(X;PnjclJz!DS^YFso1I>;HmIw{uS?H>v|84U_Kf$Ze2%%Qi zP=fRPXe@Z>VU8bPHwrl#>SlG-z!V46zE%Q@EZD!75c_n3`+`Up70A{Wb9OtqZ^M>PXjqDMdN)4-)S)gbI4y+@cIW4=h9fedvML2$fvh?Bj5I>dPe)veS{C$cQ}wV7q2A5OFJxvZqzdR zuGkEpT_uf|ZqUZGQ=IO?Zq`U_VMou;;zw`WD31Dco$7Qi&S3aro#=LP3+cYPkTR4O z6An%L2`A6}n)9Cpw5tIgf$O%C^pi>(`!H`%VzSJ#!%nlZywIZ0+@tA#tpslZen{GD z)50#>x{mB8yFyMdgHPp31L4U+XHybCxNsdWZBOInM>&GrkSz)cSrYpI_*0)J@s7EY z(2}W0(=!!iKRAleJVhDIQKgbhh5I&LA^%8M24726%|00-H*u}N7exxZzc)p=L1+Co zaA;frVW|qFY%X4`R_Z~KS&;*rZ(=t$aZQ)lo-n8@*hm`Y=TJ^P@L1fwnHc(aI_bW- zh%gM>Mv!$G=A3XJ&@tpByI^#%VmXC2e z>7H1?>R(D>+{h$)B<3u-9f;uN-SEr*ZY3*y=*y@FJ!##^P)_&R8Cp#`OsF61BK31I z^L0c5-=9hZ1RrEO_W|P9Y3uGVFcp8k>rFU><7- z{pM&w->{UBo0pQ}yl|R-xRhd6#*^&+1d4un1DMGO<%*6BK4oA}0E>>EFjDsp>#4~73NxihqQ$Kexp0d_cjYSVFTnk;(si;2Vxl|g zdZ9hdz^_4G+?TGXy_t$JF;h0qN@JCq8wu{vS_?I9tws!sr$}zYAdhpBG^^AieOqAT zUQQu2>uRdMJp$)U@XSl#H+B%bY#Th!!oUN?AolqHv&(XTLEg|1^?@(yMUPh?I3ow> zjDA5p6o@{tVB}^b_!($|>c${PIgEYzGH}EfIK+bknT^XA5$pIM9`Z-ra|hTk2;8yH zb03N8m{tG8-sk=gVx#ZyH}bUFkHPQeVZVdJesUZBV0Mo_)Yt1i!e=}UtrPh2){g&i z=uA87LAR^~2R#Y*2cnnkXW$V0{Or8b|7HQvh{;|-9ZTm$H&%lSx`RAy5xAa5z?vh7 zuNn{oCF1spxa82oo&&wkIcSDn0}q}Joci69ulo#q_yVk7JO#`L5uZe04t*K=L@q+3 z4PX1(VB}#o-c%i$%xh3F=<^Y4Rn)4CA z{RQs45P8}Gc(7~%2DJd2hQQlS50COaUTn)4;Lie_H;#HU9q@(f)cMd|X~9hADPO85 zEm$+74?vl>$70Y|Tl62FBXr{~Q~HFfl<|k_v_Ah5tBwNy+=QO+PqUfP5$jZ4bhPte zWrAX|=4+!D7Lulih=;=W5>7uJARKpYqa2M9yhukg)a0ESGda^N#O4mjgN1`Sk0P7y zpAyozwTMz*+(Po*yKVh${}O%tRjOm#UCLTeMfLr7h3cx_N101fIALOjpnE<~R3_($ zQUcDw1=))D7x;*)z#TGIRaRuHx6b8;F^3X_G0bXR8AMuN3S-C%fedjhP@^s^8x(sJ29#N=`_<>eZAxgQpq^eM zIJ})txgOX;xF=wq&zU@0x2u#eI8@slemI~Rn$l@w;tJ8QJ3@3YMhOOCDX;4Z<&4uJ z1fv&fXlH>%Q?h9H_$uTj9-kG@9W~E~D9JaXBuX70&Kpl$)iT zd~`P_W>kvoCr1_S`Qv(R;V~mU@0f`>a>7KF95c|~`&H(@Qh|BDNMM$2mzW2e6=rRY z%Ds`P6NaYigmdY7eo?BPpNzctbc(8YB`b1cio~CTuJosLlHqa&nIl^V*v9lhwlUt! z9?vlIr}kOI4ZAh*+dTMruP1GXmrI$mi!H_P7aoco7crA;!4haufjQH`QNusI z?Pv50Tu{dc)?iMc z_{iRG8Uy|&0(%#|p0x__0`t6T>|W4rxNS2Qyk*dLjybCEfjI0hJ<@~cZG$K1$G|Gg zy{KzPJ*y60W0#<7S_A&70Q+$SaHIvXQz~LB_~+M*1@<%o5BK7IccG7c4!GlDV38wm zKo6ce8JO}Ja7O@UBx7Gk0=6tqaKPmr5Q9z^%OMP%1P_$H>ov+N_}X^(+Bb>O2% z0=wY7(sCa$-c{tB!#s$t&wa?QGVnxepmB&qoM?ro2e79NUPkSU;ML;p1?>fLvSu&T zul?w5(#tkb5ivk|ZMEp%tR6JXJZ#nR@J{YI!>Yt3%FuL!GW~axHpX9}jISLcjAM6@ z!ww}0PLZn&!mkyfOCxt7e81RFCYV}Kpp z(Jy8en$>eVZMuwNS~V0=cs#Z=dhoc{^HZb;9tjy{^!0U2g*;<{TtFn z?(J;J?ztT8{Xfn=!0{0y25-_H$GocKFE>fj%-=1%anzNbcNoT zC0l<=QwYB#MI_@DoyT%T433nz+*J(Su!sC!r+hIjZZTvrwK8Fqv$I*WC!s| zdbj{j6G7e*z$+i&Z~t&!SDe5(?!!FAH<$DBc7H~UU(V`2Ur!ouCDMkwVVrU!0vG_D z_UsTw)q*+w{WG-di$`ep-;Pl3FCQe`OTo20vz2lCGnI0^yNPgm5C@OOrGk70oca4J z88vz>Y1Ai^rn#F)hop6sGI2f4eil!0m*Po@SZ~w6zfLngw~A8!SjKSqQ8X75LvwAh zB=bxnLHEL|pWMmH-uG7K)q4 zHHrykX8yuXvusI2Y@BQ6;tyG*3CC>eYvr2coKBG6#gN@^z(+M7vBp+#nYW;6{2O?) z89g3Tzyl3OFU8;Bi=F}gbRcJ2fqI#r9fxq9g%+>L5ByLS_F4&aLiYI~e!wCLTESc_ z8h8X>AIv1T`&x*P8o(Xv@pn5OWA{$9TvDCAo`$)r@NE^JtVXc3v(Cf@VJmU)t`V^}X9*c{Rt9^mz z97CT*6Kd?QBL|(1V+}`~w+$GxAD02Zmz`LjgJ8(fCtv{{cLeWGAr|@sb-X5U(jWbQT$7A=OamWw4w!_RdKU?QjEx>d&kW4l zSqskSW$^7|fkB19nft(@4-nJ2fG_(2p7$F(iJofa$*^&pHNCTAKFPdzMaoFr|hIgsQU@`Jx58m-~)u~j0(avs7iBrp~U9=-8#ZF zDU25DS!V7taFYkO4=5M55$aQ0NO=eRxLUHU<~3)CeyLjQ{PsB8zqpths48Hr z)%#dmLlJNNAzc!0T?TZ)5QYC_-U@HCIN%q06&_Sqll>A zi^&<17?&npv4U|MK5P~T@1^HCz1SyBtlAw zrHn6bq#U1(Ck=0hQo?o2QO#H_7_Vi}j>C&dQ*#k%dLAA`AH*}d%3?~+DIs)|&{Mw# z{brb>q7MScGX=5AIp`dx`Ef!T`pFgtvg#bnz2A%2>ENUswFNtKOW09;H5y`3l;{BC)O5L1@y4)r!|7*TY+?H!oBNH`UP!uJ)M^gf~ zn&J|mmFG9o)V^$n%ih7t*UDMlqjFBSp+uCsN)+xviH^BnZeXkT8Q9Mcs_giK3Y%Lc zvwu_xjAgGtZU7El+p2JJd9rXPN8%?yv-L*0A{|Rp#iCSIoRF%>&!i~g`ecQ9E{$W$ zlL>lHhLtdtS~z35MY_LrK=LW>lYT!ssEj>hHJ;gLQ~t>yh|ZO$hl5j%hSsncbHpCQ zhv-Z6c%Wv~HXZR$IC|}a@%vp19J-6!Gk`-CsKH36nL#hy%wS(?*lX}SwDTzSUBIGu z|Gzd5y&`5%@iox%Jqbe&^CMOVbcC-VjyME-C(Fhgks;uHe8-)O`_L(nVs8*;PPfJ2zG zZ0~PJFA38$9Q%C*G=J}UP%ZCy(oLOME0Kr!Vqd4j^KcqslSbfBvJcygecN1wJ=_mG zN&;qV00s?1PwPFzEobpsPKaq}EO+R~9|D7>BfdI>Sf>JYvt+Dn^qRzZGxdG=yhCxE z3-Q^{ z4bV=nKy9oQD*`C-~06ba=FCGb>8J^#5@K)*E z2d(@p{69mGTc21!_U59GqdjEMvLj-^x@==V`Cj3md>%8qZtvF|eah$&W6RiKSE@Oe zZ?3}s{yO7Sbc8mfZ>LOh1!3~qs~Mgx)||Lv%#eUjj@L}(0?+FVY!jV!Y` zd%>gBVKrgck!aO@lrgBR+T1T6$~DV9;KGL{5z5SLl6o!GX1Q5RP%q}O+K!D3aUqT! z+`UZF;t~{MSc;6_t}LfRBX?+vthHGaH2iAiu7%cEFR2} zxpB}EjZK%7+3AuLl`0GQ8zf=UMp10u$SaNUqV#u^!uQ1sbVM3M{hmOvuf{PfW<)bH zwrXO+I>PZn3hDA_lg;>CtW|t1jxtP3qgGJj_%H@-ltUhitEq?(I z=tsQKD6!o@*88vO)3vT^9Aj z44du=`pbS^PP6wT8ERQ9O(!LjT>EBHXx&9CZ|p`awwu!%N<^iqSmB=(>)18B^t?l< zPWX4P%CFq7vOn)v*dO*wOy+KhKD|p}HW#S;LtuX~c)B6bZ*5AK<%emCycSsGhFGW} z6}S&A^|*A7y_;^Mo3qXIz7mV@>VAu^e$SxJYu|u6aLQuHKV>ua9ncgZhp=U?p<4@< zVFr8v*K`{5h~C63kcrR|ja$gKdLj)CKMOoetvCAR5hvOGUq^uF4Z>r7N8FYQy|)>&jojc3@H*n519+WL ze%uqx0jsG59vwklYdc=Q0`bR};B+g{pEZbH^&h~)oQ39Rrw83!2d|C~eW=bY$cfG) zKN<(DDFYY&GhV+O{(kVP$9z<}H3*#YO!z5(0=@Gf9`gg@qglWe8$Rn*;O`HJ)8Nb0 z;0z2p3JrG`{63ySTo#4nYRCPfajY@GAaM!P@Gm&>ZeWlbVjyQ=(6de>WM6bhqoh;zaPJgnq6g_5c@Tt6R*EYh3(TwlmDd1)*ZeIf&dJW%$ zikOM_(E8eO+v~tlS7^e~i`zLH=aW0&^!~zpj_k!+-)K+kz~WHz;E=dJ%Z#jk>T2Pj z_)3{gUVMNsjVWTr#^y`U%-b$L_4t@D{K+}qjXp~LA4%UG7sc6qKl98pb^Fqr-I(59 zePf!b8l?*sKoLQui_&}Vh>C#Nu`Ae<*pirHVvNQ_lbC2?@*SIF)9Jh+-Tn-_5|G5`o=<0qdlX}fk0Eq-ED^qnX8Gbo zh8&s4t4pJGLw4*-<^YFMO7+Ie6?%gi{g_y4Fg^<$>Z#H@BN}SHu5|@>=#C;rcI{q#oZd(?L)|1K9;sl@E zMa`-{q)|0pO=GQ>6R6Gsdtq-^GRAShh?(;R{joW`-Wxj{A7jr99FVd;oL3fxP+dO0 z7Z@d&W02u`I)$@($MS|AXplz4@Xk#;sn;{R1<$q?(Q9n0I3jnyF@dgD`8IfnMF9r!TG3(;JNjQc-81KUNzBrokxsc>sMFN~hvIULQXRCU z9k~YO*BqTRvOo~07c;{1HFn{D>+DL!R=efe4x7W!t|5ol57-^YwK5jhX848IGHic7 z+jlXI?YXju^v*-(*<{RBeu3ZF$EbfwQE!aFoc<$VQ2{V17TW6Bz@wwM{T3d~$FT=7 zoN1U0UO5=o+6|me)HImcV|FjyIt?Asd0f89eWk$YETC9`Yo7 z0-%Gw2i^89_$%HlnoDox0|V-C?n7&M7r}Z^jYSK4VdP+yM@qLO+8!TlY*H zV=&*29r^nWz!>btwzKHT9tXCaM<3;lYkdG3!$fEedr_yr*Q0%mKk72@Mk;DSWSwEJ zRk&LLjiU?doCe_2Ry^-;HrcR8NHXfoR{We&Jm*w!IYZE4-@)xdXrEo6%U*z47mK{- zzkSrc{{n}mPbVD&Sh)b$CjckQ!9SxH>+V6_;|%U+2QV`o{1LpfJI=xf3cAGJf1n9^ z5gc`5Fx&eMyi%HxYfy~dY&ZDn0PN(v49qS7@An11_cZc@m_XaWpJBFv3DNz7<8ua? zv=Tf2Rjnp|zmYLLUCWOb@(ttO$TN(5zCs%N_gc~E}Y(qgLPhv?DyTs zUB1xBx_yf*!Ox1QX(}@Pn)8VDnHtupU!|Ge&SR`=VtCzxNM0^Uw;4`m*hSCuL3!R% zyUZkO+@5faEstQx9k38Tv@&@6?;jIjZHy^=Ac%Y<*nGR(_v)^IKt zt2y`ZYT}t$Og)QZWyjLRqB*LBFTW%Jzj-k-+%&ugOFRx&%ok`T`OYES9 z1XE>x02T3XA$&87=#Im0Ru?H4e_O&E2a>4c8%4zRD1440F?-L$W9}yM&c%Cpx5KD| z*0u;EpV}vme09G(f;k|0*$#?cn-7SdhR;c>z>+FWxz@d$$n)&DDtohw2s{3j=QT7$H(jH{X`XOKBiDs&w zuz~8m(4UQgw!qLJD<>OtqR$!wt*kSWyhan5vCc%#tu@g6dIOic+CZPGHSq2g20@44 z{VZ^I>kAa+L7rYp$u;r`&{~J&>cxk73Um~_a=(C+8p=7bs$SD?-=>@jcYY=?FTSwcNJxNLd_8>aJ6y3}hW_tvK5%F;`m)b)-+)qy~JyDe=bMZL*(RpT|E1b*&^9=Ztq*g|L; zJMnsXVAIZE(sLR)gb`t+r*SsvK8v~jVt9%fz#ruRk3`_lR6n6D8};iE><6ww4_kwp zX$tu6>+l=;CD1nLioMqNB5Z@9O9$0E*)}d5pP@nUt*CEeM?EYsj;Jlrd%O-0icjhZ zG6QAfmYvj`xraK}Y$R?gwzB3IcWUPG&@N6xUd@X+MEMi{zbmsCYupOPR9deYKVHEa z>z7l*2O&H?wt&#*;%(CXlmR7ssZETDw~OX^46Tb~xn0rBz@(MhK>ccU=s*%<8@HGa zIHrhNT)r%RS+3}#%JhbR$_&PZr3UleN`uLzN@t!_shBTS=uBT@PId}2G@o39keg`| zFJ+o^+p_igdAYJ_Muwml(}}V@hc!N)OwHTlcwNqXDxL~rXP?aR?h;C1_i-p+3__ToWxQVQ?g_WG!iCWIRpoV&d=kXqIV)oUY%{ZJX(@cNX zsgB1=Sf`JWpW#`{TEiPOr{`0NYfUI`mV>BN8^rOy%;5z1it9g^Pp$vP@s=5hyu~kv za}6yfZkKY1t78i1bRvUu`YV|_e6o-C=x*gl{m{aX`fZ;$g4r*5&P7dh`hei|#zDa| z=dj?>eu($DvzK!@u$6UY*Rd{a4d+yvOHCt|QT?T8UcY`JuUi+-nfP4RJfey*=vHg` zVqj8MzNXur!pha@ta!YTm5~>#U%H7ke%->Ef84{FrOlj4ScliWPSA%mNy0z%dUCkV z$mG8I)+x(+qJ`M25oNmqQm(kP`rHywULbPO-C()J-7CynAs_q>9@mA2^l}|z~_cNT#>1W ztXI@>$W6R65xXDIA+_a#%Nfquc!=lS3OxB1STq}Z`_9;t?ekS13;~1o&wwZ5dra@n z8BDLIH`6WoGTk5HHXGSD*zxb1Ig9DLhMkN>z#kvd!p<6?>h}2ik{zn}ZDadD^3)Xxq>e z=>t-7p(=!fGyG*2adxU!JdW1L?!RptG(Ek4U_@|@g={%hro+5iQN&!m^mDpNYEL&Sy*YY#iizgrEZ*3aP2}iY#WtX`sH&OdC;E`c7!s_t`x%lv6Pk8EFp?dEIi9{?C>bH@kets{!ALNglDtvJ-OhE zk&khHC3VYMO+C+6abBTitn)hba0Ad8wid9?J2A_8A(wN00^He+X4RoJpLJcffH#L@ zJ~lrDd-mZ}Y{&k_`EcH{Hi>i0PUV~fN?4D&64qT3#gD#C?KS>ORq{cfUBI>wxGr`=ID49Tq%84)dOW?B`u?BG3D=J;WtzGvo3ke6PRA z;hbJvPOSlnocX0h&T<*EwS8&Cm{`dwR~j|F+iFc0RKW<%1+37L&dG0Q6WxhM#t^)h zHB}yFtq=BamXyt$Wz>3V{BM&e&u)-~lPmSywi+Y5bCp53P-Bw6$1&9yWQQ6CNHrv(e0ef?_+TRF0+#1w5tKd}(|MZ@hvClEQ zS2hNRM@`fQAN?!9qPf7K&F~4ShqutYFnVuF7}x$A4zzmr{sHbFcks?r;BQw0htko% zjKKA|5_@QqfIT;WJ->!ecb>-{#s=tVPU9LxZv0&x@XiAK`2e#s=%Q{yL-21u zG~J(rkGT>6e`nMqnSrD`44UJcz*~53x8t;0pUt2AXdahCP zNN!-pA6q4k9=BQ=Q@2KVymU1kcdL{-nX))>Vkt4cTcMeo@>TsGSsH@x8S69HX*f~M zI=9wqjwdTv2OeI}@wtq!I@QjXEwM?j#SRIDi-rX6V3sx@f2%u65VkI6nXi#eqmNc? zZIR4Cc8p+if$!hQLLC=Zq?3}%71M$;#r#Z#Z1}7~F^s8_joy`#c~7}yoLnsF|0+;) z4OvFzi43z&&eoak0*6fby!nMJsvE*M z`if#X!*l7H{#uDmEGn~$0)V*^R=YFx8aqTH3j<2Uv z>-<#eG%tm^UC!g2KWYGHy_2!@RPasq$(RRbE-Z>(qsUxojAR;M>?$ zrMVB3Fz)5Cyvr}4f-`|Pn%i>DJof{^EfAPfvriZqgIf=9Lw}$z+lJdU2PBWF_*&}$ zafIuB(Iar5;F90MyM4QxcowW@y|@Z8;&>M4-i4iyJmju5A?x*xMS{F0l~XFVG*ZP4)-YjxtCV!fD`rwEm)f^cG)AbpuCO8wX~Zq1YV z^QD6DXeBQ&YY4B_5#`!O);RV6>(F1&P-`xm?rW@TM33~P#@vG#q7Cxx!ajhl!ig%rS#XIHx*p-Ji`c>$k zzQD|F4m3noXc$+6E6N8BZH9ImJo>-lu#OSE&>O%2U_$o{=#DsOldqw+`4x4IfV${6 zXos3H9}7Zl(*eKR7lKIJG58Cez${$?Mj%6jmaa&YWvz@bgxwe9H5oX~$&fwLaM{p;X| z80a2<1Q-7~`b`zTpONs)MvhU37w{Hd<=qp|-$nVU53>WbfjK^80DBFO3=#GRPsZA8 zcXEl4u#V^--@)o)_Ho9WyQ%AsTlkUXYk9AReEs;j74rBe*7Bo*YvjlN&DBkqwoG(9 z9#5pCRK{=_J_wboW)(55En%(iq_Yl-3N^7J!_ME1Wcf=A z?W}ImkosDf#(B@sex?)+s zq(s(jgs!ozTsB;)63uJNB~x*UWca91(Z^;R;X_56$O|pXLhYY3YLAcM;Lo zhT8e#!8UFd&T~O_QZZlC-bmNff_zPQJOem|o~SOFn19Ptm6$SB_fsKbc@Zp0iV)x>>dF?Ak;efxgo8eUya-9E^mj=z^u!}isj1-+b0NG9iW z5uVgdi$x=YIlU0Y8%{;=hR9m&UjE4a zkV-lCztgEx53`SfAumTq@JetTHP=D26qZJ$fkcjrTPSF6gbVDYrL25&wQ452 zHHVBA&MI%Gitjoqwbb*vldDAKof=Vm2EHn9=NkC0i}b?v3Z3v7X7+V;3fNRxpL2#UfBy({&;I~}7GjQVMjyxnb2_nKCSulq2RQUBZr4Fmc^bHI z2A#hE*u(Cqld)Pu|&5vT2g+GW%V^B3@4Uo z+)qmhw>62XTDoAHm@C`P6e{dnB_dyd8h=%uByGi>$Dsxzok&uyT}cCI~9AVnS$k52JbizJ0DL#PaTyYS$<9s93NlAn{R~i$_jWFomn6$ zZ>3P<_~r1^kK#?ift;y-HfKWL=+OVZ?EF`Y?E3p2$?F2A+ND5{K(P!>BwgeaGn{viOa!y)-g4YIN!}=onA;|9o~u~Mknmu zABo`gU&m4X=wwcm(h29bjAQ?arQGB3oZ_3uI3z%qaDFYZj&0MIo?oF#msi+T z?<(8S{8anU!#Sk)v6-~zEz~6p1+#cn*f>RW5E_dW&to=dO?e;1tX7~s!%=m~E?pZGSe)jsrbe}N-%LvB?(u*e_v z(@pRzzr!<74}YBN$bVf745|e;d`{u`koOxMfsc#gpg9T|j=LT9Rv4B%-(kj3!=a3~Nz zHyQ71JiOJ8q2{xLFRDj91;6UHr-t)HF_)V>%rk=U zbk};+X~=i&LiS{LKjv_Scwa|>LnYw4%8)g83ZEb0*n#;FIFx{l`+Wbwq2Fezwn%tU z_eQANA1VFX?sY@b-QC38^FD9+{19=B-^V(?w4KwB+N2p(>^g;P<1FJV=;)MEVf^+2 zdHhQW#_>zz6pybK@m48RlMaNa5ErecM&X@&eZL(@aj64y|%xGBI9BBc=}U z*s0l!p=CZR{}ZS(TalA-VDW%ZkOD1jDyMq_I8>I(i09K7VZ$OSLa)aLXYktF*}VE$ zp2%D-w$#}tY10F(?|kz^&glHt!>S^hI$lBefO3d!Y_#HB>> z2xp}NZ##cz+K|dkAGBYeX=BC(+S%`B+S#^HjVq4V^r_erUA>&M1SV4BLtxQ@WxP2k ziJFfUF%DrHHPdY97~=~Cr`;vIOGr6&eX@+YvjxO`Y#!&@noZr_N~Nyp$-MQ#azUTD zTr$0vBw6+*iRSWn(Y!4|u$IF6bus*fw!#0bW40hzf(5Z5T$HPzId@sY8BWaMbaR7Q z^RLmY;|u#07t$iTUuls>tZo%YcJ7x(6&;X9+7HR2$RTm`;|Ii%Ch*sXfJ0X?+kb1X z;CZlB@H8FZJ$CP>E|)ORWGh*>>8Y&iKq7G%AHx|gEu`|w2u}JBw2W()vGQzahjI%D z_iP5o)u(d&yey&{ok<)9G4pJ$g$}8jSNidLRKf9lRxQbkaVm2ZX=-yDxO`&3l|G0|6DbdboE3(wwW=k-^G|N?9i;A zZey$>d<_1xm2voE7i%58MKcc8tH%5ao8fl#pw6jsfX*l#(0Vd$_SO)t_Z;{iA8)?v zE_$#==uUhv8~XvX-&^3P=YTJ|fV0qjp%z(34guTzfxolTB z&I}wEWP|Xy9f3LaReTSb?GNHHM-N9oBI2_?9J`#|zS6z3e$w5&{_^c|=mi>}UpkNd zG-wEK_Fz``E&8?LoG}ORzPI5&Xh4neAIzBCfIk+@wHx4(5sBWV72YE7>FNFp+MRf0 z@+^R6Xa>H19tDz-#;hGlRFWTQ(71 zM2Dfj_##N?NcNTQE&>OS9p9eX0NcP?|9;yE^npXOxc*gX8aGruAZF~h8MhzhEuS3~ ztcO0}9V@o8&exhaeefE_@IfV&rf${bUp6uhyDRun=hJkLUr5rAo0K4r?uirJZ-lb? z_h#Gqh)kQ_d5hind$a0@yg+B)<*aTff$tWvAKrz2U{wXHpIWJzHY{ZHKl!QT#hG^P z+FUy;BFpSz5~IwEX5@(ZEI%E6+p7zS@IwMe-pgaz{2Z1%okPfT`GlL8$C2!Ofvm_A zq;q+^v?qm^+j0cuaJFO)&XshI*w62SUr0oTC>==S<-sI)^F*-XZ9kO@o{rsC?;$%p zVA*K@A@ZS*ojn_@l4Xlo>D*$X>xtmxrxsH=BZ(TuEu*G2=&4TTF-~(7Fjr4T7O zk4TOyS+Rc=ks`{d7*Q#T;f;dsw>r_VDOWaJ&yaNe1)>~WE-F)NMBSG)f_%FIn$3LP zaITay{8G;9@-nFumneuI!fWkBp`e_t;?0+u8DsDP?8fb79k0Qc>iu1e6WOXc+}gyL z#8sMpM~O{oDjLu^R`=_b@<(E5LBDn^-9B(Jly*kWknXq4klQTar2hd1Jq4cgDEz9o zqBnaIyA+<-sYr)s+Iry8NpMCv;I*Tn{ed^>J@6!VhBbX-!LejPUp>6v;R9WCG4zNL znCbtE+R1Y+-xh}XST6cA%;oQ!z_DgS+k%X)hrbQali>HBH(TwyJx6`83_gNCpeFem zJD{tPwU~qX`Fl9XeeL@_kbQt2vG*JB=hMJpjf02Tl$p})pP*-IKyNmj-wc2LTl>+M zC4hUrJgjF7U><&t`eQG8AI+ETyXH^&yfM@M2fi6U0o#s3tCIpB)`ysre}ODD%%Gt0 zVS8_Y(|HQl`Wa*d%*ShnFYdh?I6q*nKZy651O2iGFyJLh6RkH7@y`7XjLSJi~7P*2ix9+TW(BzBX#Y)V53=) zniRT-RkqD%g%R@!{U(km4a$XCD*=~{)651}zyxmWNA-Xpj!*v-3rg1xk%ZH)6%YZ<3y`JCtW7sXv1#k1r#1<5G^yT}p+jbdEnVtFBDB9(?w%hmZ1EvP?W!`fG10_q{z8~ZW=r!y$d=0SI}{~R1&$qkfMO1 zqWdaN2`}fY`x_av?Eq`}=>TymYUP~Hv=XNqyBUXbTU6r@wWx=4hoqCGkL2U49_lqJvKu`{lj<91qNlHH~R-TbjnZcaD>*{3QbB9^h6P; znc@RUPr`7nCFW;yF|&UWoc5G?^nMC< zWoyyv9L2S?0zMZKX4+SS)koK64Gs0ou?>0S{#f9U7qFoj-)q56=Kwg{Jm63ZW76Q&0d1QA!h6hLi_^ESvElGGScfs4FpzhiT?3(7wKAen zd(5g%4ewAue_e-us1ALR;0LcV;M`QqyO!a0r!UvZ`_gu07>iI3EdUNRp;tPFTB*{9 zc~AmelyU1ZgXy~hT>5neyr;eGkACyDKUxMHn&hiKyb4e3QK-o%^gkQHb6HWhWes~& zqJ|rbd47E$*JDP#`YrlsA&BoVV*ZDH+V;7~yZjb)z)qeCQnl;Bn)dd5J2yF!;nHWbT6hqnPKs2uvoRX^Hc?~Orf58TBZlW! zB9p@kN)AUaW^;6F0@V^1inihyp2lYgGMgz%Ny!2~d5Or*ixo&qlE7vpi1yyals`XH z;{vDK?Hjz+p=SbA`*lCf_KvrW$@3i&Yv$UNBgon@hG;Y~%uc^r1m9ZtNhc>0%MRq) zzmHsb>u$|zd|z|kcz|)vZ-oC_F&$9|erRtR=lIkj(L5ULOulXj&L=h{x{Ps6^4=87t}Q=kT&m3-$Q? z2tV@mBf@BB%*8@mM33H9(F44+XLzeHq7Su__de0X(jtt=+9Qm-xrcXuahKpUbq78_ zwozAMGv}1BmUE0QCSI>4a?Y8|3o zJCz!>RL=4leA{-f(sVXt0lDGv(^n9A%4$v*QoI~ZR z8nn3Y19$ZPVplPa4E#yK>4&ZwI_pjw=HD;j`h+j*{pC2tz?uuF6Kp}^T^Z*E)In>3 zM|SWi)3A@AU~YDJIPVd>b`mlM4}*`si|oH)9}Ek;cy7#5``?;tvjqT$J^?@cJNOUG zossd#cE#g9w8-5pz=<@>y{^FT3!3lFrRW=@P`i!B9Nr1N-*3R8J9xg~j1UE{1({^` zUD3yk17G_ovOZ@c7xF4R_g_Iz=7Up*9xnzugdkwiv%u9Ym_cm=XEX>NxEmN*4-S~X ze*?8&`xWS#a_}DR0eh~Z{`eMqAgy?x7vYtQ-H!HU==rkHUtRRk`u3nEa`IujKO6Rz z2Dknub~7Hw9IY77=Yd-3A@Xv6L=7|wHPDYZe!w9GICN%){ow;((JPqk2LXrrfI&$( zy#eZ@Sj_%6LBHsP{SNGzwBJJQmWkK<68OAOwmezX4nteuodo1Zw>g z10D@6h!|jZRA|P@Ev&<1M_AW_-TcV8yG7RwU=XvHcc2^X*0Y7g;oGG~qgY1y|E*C4 zxq?v!YFXo*3eNm~7Vq*_7InIu!LfULJ$_H~0wmg@WK4%j?x#)}$}7 z>7T5&>vC7xonKq8ImOg6j&l}khSUI+oSCb#cS1CF)qIUKMYG)JQ7rf0*{r>05o1^5 zRPD1^jnqUjbT|AezE2}UT|UdtM1K~Jyc^SUjv2Q^Q0FJH(9coj$y`yHmPYxdkpg2` zg1qzuo}6FCYo9IQNJIdmeK}3F)%j>cGiPeHmEh@z{8e(pe?a$B;GjNhmR))=NaI$| zv2&5AkNT1rLuDE$I*D*VI_rTq}$ELm1)31ek_U+|8 zKHAHV*s+I>cw-yq@_RFHiP#EXv}Vrr-A%;hXd~;QmJqK$lQ?Hj_&2_uK}-&LoOy8( zXIWoNjN6J?{YTlXu_uw!2gFhSu|*@*#FAP~K2?IgvqVrf z6iE7_Ji(HOytfPMi23X~PVcauwLH0xasK#--9_19GrQLg=_eOG61~bFvCmZx+Wjl- z564xj-I=Sj9%G62U~m!Xli#B~L+|n3eZZmrfHQLP6FZ;r6T2Gxgs#o#$ByI74-h(^ z2oQ$%E<65)rte#5eO?0hJe=WC06u7HFuXEwrb81rytf{JnLg%NcZPRRuVK&rdI+?K z$Q(u1XlEyKbT(r?zdlTTSQ%y;3Yujf+=^Xu8F&ynyb}-prS$(id)V%MxZR5$YFGy} z4OqzFHMYRtKMnYK6Z^WC@bj^6b~_8&fk5y#$Pl^*zNh^#=1j-Xn~nCt?m4)jXMkS~ zxGnMFJE1}D{2RG#4^aDzK<~2@HP@&>t?wZC?djl-uYz-a9oPF;s4e;D-L|gL(x&w;X*_9p1}qoMCQ?MHYbq?0Fx5 zkNRl6BhaTkK0|#NkMqrR_2DjW_2DMW?x&$28vzUo$LmN&W=O{@=E1Mf5q%FFa>RVk zj(+ePXfkE=&(Jg6UkASIF7W;_?8<<kU@h8OonSGqip7qoIG7_Osb*>?YHG*74o_ zyhr&qVbrbF@`UND#gQLwlU=&DsBXtAY!1)Gt2)fE#mAd8>2wWi@L$QAPLy)yi%SLb zf;4LA$YtdA7*_C@#c0v~*D>=_6OXN4DdRCNi@5GgpKiP`v36;hbQW{f4lz8k) z&p)I(-fguxY-kuTWfczya!J3ITr+6@s9JsKzC!Eyy-9tzZ=E`{BbOOk7RdDu7-@fM9^3I0@F+7}>)na$(e3lphkmoQ zhkt@2K7#t;9n?S5!G&0GPJx?EMQ!shxa-O2l^*!;9cR&(^A9yS5s&+1%$@WD8hi>@*x8UjcAK=g${EYkFLI-@gJI0~zdW7D` zJ%H``$)D++hk9f`=4|_suN?>M=)(2?3*MtQ&R*1B=YVxFz`ax0Yxy0|I}WVKFCMi^e4`|8Cu_~Gt@`_%&_;j z0f#d2y>QISzVTE0K13Z_y{M{NTJ7lK3g!0%z1O$1L!&9q4BJOR}ylz z+x~-^X-WXoKh;mQrK1n}K1Ayma|YS@4K{t}9>(GJUOM8rjr?PiYIILruTmaMSgnlv zxkebZd^u+-yj*j-iv0MraE9GHM`JxBG_fTa z`sQ?k-2kE+mC6F!i1>CAOIortEf;wX?`N~*d+dx%2S4;o0pVxnu~bXrxXw79%Sfj5 zZUrYB))O<+KulBd89zRPlH)PFeRMq4E-xWmS(HjXiPX3+7c;#30)}-6W!Ruu8rL{S zrA~oEWUlWJeaeU73KtN;F;P==v6_xc)f_UbHHR$dht9z3Bptb`S4yF&uHfAkmr~E> zGUE9OyeQY^^DeE+1n0Uq!I}asvp$VDd1Mmjr=e{=hu+M28MUM!2Pk$KHNP4y=(mRP z(v=WKQA0W1Ks;|4lS(ZcfM30M{I_9((YBItJ=nmy-Dn~n&jI%quVkz@S27O44e*#< z&pD0WP8|DMIoH+OsAotOb=wGifdGxf!Zn zlMF4<$Yq>KM1I-LaKfjej;+D`Y+^j8YhFra3EH11)I>WY;rksalYH#eyu5}o9ovX> z9rJvr8mfD~ipnA7yx~%@X#6fmmOsdmg-KbG9GS(-hx0l9lQPOqsN~qjazc@bEi9;} z;>-q4E?q%&t8#edt1QZ2&*1peS&THflF`*|(F`m1s;2*~vl%ax4v9yL2H4J;LCv*R zeU!9X>-~JQy+3P*-4?z|vxm>;2J@%$4;|2(#iHJThf4R;0o()hM}sE<*r7d`+jj=g z-c|6f{uOma2Q-aksE4j0m+KVzp>^2t7#yA{fhSHzEi~-Unu9;bV~@TJ9w6|ozV!k$ z)~Tp1hINa5;aYd|JgwIj#&)j?W4qRclJ0l0&(My(NCTe|3k(XtHJFGR;AwdLsMzyF z)>ro@nBS}LVg3aCc@X&OsnE@wL!ZdvIbVam2Q##Pcfg0%8=Mh#MB1P`?umr2><_@A zA#bjy1-|>y=Wj|o#@5hMJ*H!U9}DOZ~LnKZ~JPGR)Q0%K}Omj@}^&fZa~JI5Bn*d8s0w-JPiU4 zjmC4pOS1C}G>f&E)5YLD$ACxw0y?MR8KB*Vd33^0Q+Lj`aUo$^e^|ARtXpeWGPbD> zr*@N($xZxICAIPdu2eta%~ECjV+G2HDWRhD);yk66*0=FZFcjQhcuV?R^oEFnK=BL z$2t51?O|n%pxX^!uPd?Op%-(;-(sjf1fI)t(-@__mKei!+T7(MHfzEjBA?pJi8ofM zj%qdIRGG?{UFI>&ui%DuhH7+d93%dni8_7-(f{iSQismLYj8%yKxsX_-I{<`Qm0Eu++IWh_!NW6928E&HO0|oZc+yow zqn>enrkwK%h7Y520d@1uW39#cjKkUz;%2SpTwNQ9!_tk6^BB}<*Yh#Ai>9WZfj0{Z zsOzy>&dCVeoZCn&PDP^QiWt%OScD)|VDD7}KCfEJ>8?f-C2|4FH^pee?o>whg?HNN zWKLNQ50sbU1|A{L9nl`MfCs1WG%N}k?X0j*IzCtO&=8I>}@G829V;i>TU z$dU{V(232$=Yt)!&fRj3KUzxZ$zp+pv@)%|H zYRx!mlWGcIqne&Awd;M6^?Go{5SzcsKKRR8`@@uNwt=`kLw32zZg-4l2PaPF`^0HN zXFO_!>)=6S;aUBwKRehKKn6<#xra{%au3q{=>rkm#60MY3!#6Ov8OQ-ShNTH@UVv> zvJ{bNA>1)TgZwo(>r&v*PdI+4i@ty#^}E2Kr-4Ix^R@0oWV3%1&h%KrNXG=!R;!^O zng~wzDC(i_(Nn#QUMw1OtapdAt&xHH7yOoocmE5pOLY$%?aQcVcBB7E#CZ~!bPn30 z7G#L5fnW8pIpW_hL0|AI>WAOpWis7|JecK6`X-_t3dh+6jJbg9NM~Q+UOupBA2=Kt z^~@g3(^tKxJ$MKlddHjVdIeR1*Du9>E;3E- zjfTFm3N!q#u>*Gtdm;tU85!Yqe-VAzaL+d2%XS?HU#;`S&e(L)W%eQ6A()$u@wE>; zK7+wLka@tM7dwXhppU^jU4%Xf`5|rBP!D4FqywI6?d{M`9|mUjLlb@l&vzX?+4JDA z?_dtN(wFpqjrr8=U>o~ag6zH+0d|_)i>Xx`rKs zPZNmlziCAGZ4PHWTF*Fra#*#z{()vn-liHB)M}QTeB!DsqWacZjQUcbW={)dnC|%k z35!znUz8HVs|}37y@V*m$hSEV%d^=@ocee=Px|tSv@DlZSnMS9WfQ8TQjRVa*zy8~ z>~BzIc8hA+u!}KISjiZEPa^y`;gofn&vEx7SngUfL(d_b=rw#6p99ZSfWGm)#f0vR)6p^uO@Se|8aX>c=ie+5%Bu5+xY}Q>f)C_Q#Tz z^2S$@3mY{HIX*$0(uLeJ4jO^|S)6%Y0jIm(#5(^792%W0x_ucfTUV!02Xis8Oam7+ z2K)GPmh%oPvw7z)b9uLG@L+39;1DE6jW0mgEyI`S^Frc*3~Co!vFJ1%{qs@G<6emp z?$I{japWz&SiC8F;w=;q;hp7 zl^0d>qG1iCrP~AOD1&lB?TUBhOs;PLj&H72L&3dBDZa!UUS6*MK@}+fl zwXF&Lz&6ZxcMaLU-Dp$mGZ|ZGAm67XcIKZ|FnWp6}+dJ$|TlzJ{i8JM@dO&^7*p>uxu8MSJlXPr=JxL>=S?zn#nQ zy?7Ov69~PY6*UO8osvILM4_@A47vtppw|L?5+c2HX4fbmqYYV8YMflwIM|x)-(C70ju2U~YB? z98@z-EowUs@9zxiz1IR$pL&u~+bN%)3z8PvkWDeaqh#BHh%+qCXNWY_galo$H zQ{a`)piav2p>5EM-jBy_S%#0=KWduRr=l*Z#a*NR7LHG>VtF{m68uzM}hHaX)rIk2* z)JmN{XrP_}dGe_8WWnoH9%DWqN{zFkb=DtqiO1a4+K7)gF)kF>NYw_$>aw0O9a_zp z{Hj>JAyQDVGbn$*nCiLtytH8<BfNohrt~F_w3_ z7D6RQKceZ7pK*E?Yrnog(8kQ+m75u?^Vmks^vO!r;F-c1{>diN@7XNtQVbvILQdyZ z#2R0P@9D}^j{KHL*=XRDoUf8e>+Mp~PSwoq)+{~En%<#=l`_D4z$;bZmJ#8pbi$v` z@r?Xf>JV)%QPq)oBI}#YVF#Dd_hNScGkgMGjpOy5;8o_r zcd@sI(?3?q>sjp9XH{}ewbh(cK^1jgTTPu8l<`i-as=ydsCNbyiMrGX!Qc@_4Uw}s zMTJk}_W8VV8GO$EgZ;5Z>7036EN?s)!b|WSmVTZk${Q9*I;DVp#1TA_Nr@K?i>8Dgs-L$ntD&EiN-u+OuIJL6IU7I+?y^dG5!sBSEiy%j_}QBL{b6&&wY&PiKxss0<(yPxH<;)N2Gw-u|xo9OrXQoG_` zYd2P|vYCS_?Z)qFH2vH)nzE=tB{}QV0go;Afgg9+2S;qS+ieAyT?BKF`e*Q69pH$j zqyLJ=?ki?WeW}QzULLN$xjx=-XLOp-HxqxJhy948P_AuOINR=kU5!BSB$sdv4(~nq zpm({5>vRO}v(Qz41)k_CFvuSqQ6$bI%>3uzF_@ioe;>;BBD1~wS?rTNjX7FZ7~8of zgzbEQYtf7ux;Jz)J8}PQa3vb%_!+1ZYB9^|_TjohQPW(5zF>}z*!G;S(B6al!#N}$ z1c5Wh^UsD}AQ^kM$Uf`*8=kk>*j2d%jIBmrHwU_(Q^23cy~TUssE2ImrDndz_Ixsx z=`HYP9{7S&c0#Y@0xgjPbkVnfEv@LyYT+^RU+_=y&`XX2&a|LE8_tx@g|F>3c-%&z zXZso4R5j|R8>kCk#vaN?*qa@RUD#ZB?;rP_tOCJHvFI3c+C5^!yE1O z8QQ}i{Om(LGu3|VrgiTLW{_{jbWcK!=n0O<1zL13^niC!6N%ue*Pw>80ejB_gD*gX z8XCYp9FI9s&72|T%jHANJMaabut&9w#&sKk{l;4dh&8o|u`JACojaBaPV1sX$BA*0 zn^&qd;#itE_I#c&dKEl)MEFimSm7AD<{ed01*cxJrtKp3P*xTI&kH&M*3XWgN zxoxiDUEM1L=gtD&;Y092M;8l*&B!qPbS|enH;-3FVSjE)2xqz*O7%ZS@j9PW&b$#l zThKyD7dcyyngazPX`Ub_CR6#ZERIgZ@7TAF7{A<%`Q%#oJAxNFx{0{)b;RXR3Uyu@ z$s4nmaPkE7o|jS>r6o_(e^jXHt{|&1yIj?w-?6?~LL9TIi8Z>2Sl4Huc1_|;U8sq6 z!vpPBEY&T6#(fODPzuV3^*Vg(%!SnHcsBBB%iu+Xdh~DwF&x7j;z|~;uP(qod|yj@9Mbe#T8I+0i|W4ELiD+tiJ@^PdbM@LbbA%mzq_6Y*BV)2`)XDgyN2Zh zRx6E4lO~*P zR0scDr#{-Yo_Sc_MEbpp$WXu>(*Gj*m*M%+eR!hvfvcVfp6E|#qHisg@2*^8{P$YA z)bV#3*M}X~w&ClXpHJGxg>Y?C@L3Ix`kn#YEx4jPXRs4mhu#dI;qYD@o_*7M1ISq3 zgMELwrTi2Bg>KYfI@ul@NpWD&N1HiPXxHI3=LG+ll0F}NFw8P+85JsZ49=Pta4 z*RjLW3U84N=q1}w@0^5Z(XAlyrV200A$a}&51L8>th$3;kP_&-4*?5Xu&cip{nJyx zXa#zP3HZIpm?vUS|4uM`{M~W(;?MY9-`S15*A>3Dq5A>$p{vj^ybC?mSF_azIna7$ z!Y}q5yc&amMSq|sdJc7A60m16-Yd;tdh<=& z?dDO3HOFH3E55r%m96=9@%d~<|3?w)T$V}3Oi33W>ra)(Rb)#eD=Vn$FVLgC++a7K zf>yC@J!=w?lY9?3rZ3E-%C6be`u;-EaRGJ-&gE+^MVXp&YaC}Ngg^YLOzQY@Eo+&* zOLI8VXm?7iv{|w<><&@URPl)+5`%o#qve_*F++3ADkFx-LQXlDO7$P3=32QE8AJP2 zhl*Ca`*SVCGp7+=NSHa_SWH<%EN>eXPsr9PMpv{$(>)6e@;j(mU)sw!UTapJ53~^L z^o@-1OgRxJppKfDLJ2h3lurHml?Zabk6ppd82E>k{;DXW<{)JyQ_4xxCc5NYK5E z456N#{l;%M5oz>JBD}MURRVVs>-iSqIIx+qc;<7~8S$uxma)2v%QV?NS(9UPG<}QcX-^$AZ$8d(U}?=lA}xKNpbONY31~X3fk}HOLb& zY|AAk>Ub8HTX}?-d^=Ye_>92fdU%iq!9xi6qHq=9D zm~9%1sm?8pnTMtdnD_JMgdFU(ErbuFN;R=sPF`O_CCz54`%fJ;{=JRr{;A{jmrF%+ z|5cK{sEqONloH`ssVX?vDZ<9}E_qLdOVhqqmEeOGlk-*Z*Qy{aQ}}61RAKB&6#$}W z-3t}n>I&85xkYME>^w9C0}0Ww0xZB?E6a^8(>Hj`z~81JojJ~ z^ZKxyv#qXjncLP9OI;SRK^tdoj$?+wp&}Ef3RdL#^q!Nax_@4v+8^QCg`UqidWIITr+T4~?gIhYMeNYZBemSST0G*3Dm09u<1%qz3M$BEo z3_iI2zB}0IKIB-7m-&?H;dMZC%gHC^xsg0w2ppQYkkaeRINdIIP_|>ANYDZ7gg?T$ zy?;=38+=f;@7T#%x=V>ZXDQEr5-%z{;sv&Tp&+k{Ll$ZhZ*Zn?_JGx@?Z7ULG=WlU#I<4s}csZK6Y4U-oU z!{$V)F|Q!ZRH?A~Mi=wks<6Rph~|^!%rqqn&yj`N4p~abvev%7K+p_JWCkIH>TcvP zX+3m?q>P%Q3#e(!GHNV`&+K6~(>5&UO>40;dpf+jA>`!mfl&fNV-B! z8kFA}Q<2S#%*f^qz>^8! zn{T1cNypc9z?OLv#m5%Z1|xw(t>9OdK)bzWI(hUb@a;Od+()Q`d@&<)_RPzGLs7t> zH4}vg+p&)jp47*66QS>h&+83%Ec&4qN*~9y>48H&n6bBlH_8W(yb!+DTJ&U|c#jRJ z@kmc?)UzKF=RE46-!ap_4$YJa?|&pToqq#|kP&o07Bx`?YNJ5x5V?lzqP8I6PCIaD z8ZgK^SO#9pH&)}ZmVg(7@1ZRcewN>%E$@cM<=YTf>#$%~dna_CtI?~WHhbcS_x%v{ z&|qlXdHlDMfJ0V)XV;2gr>co}5_5rzMA3#%|-DJkyVtG5KDC#1a+= zj(^M4Ufba>`?r#_Cu44XFqSvmnkh&uoJl8V^7<#~%ywxrHC0s;YgP{P7`2v~zDAZ> zRXJy{7ZBZ`6kazbf!Xh2z9a|pqJN|$H=!SzzPr=@)~QZQ@o8c!IV;)Mu49%L7YO1+ z%;wi1%dB52C#7dl{rEM^+*r@7xA$S6&r#LQ_kiMv0Pe-3Kl4cw_~2-bb4iTE`o;>H z+(m-+RJ^Ev4n5M%C921Xje_>~-4bs(V7KE~EMr#qi)^U)wwI;?FN8_UE#hSA8*qBIswfx z-sqc0&Du=n&@U1!*W#IR|1xImoy~L`im7B>M>YQih7=SsjVqUF7C;j{9do_?@sia8 z9=Ub#qUGJCg5^u#P{kVTmo4XX(G^595HtLX*@DTL%^S;BG0nIvK_@N~&4bgCHHmt5 zcP`O|=5yl3GF8YbRfOxMiWF3Wzb8B#n{tRQ7~1%Z9EDD+bP1jt6n$_pXUNVXdbeC+ zZK@`Yye*vR&l;8fwpr;qu}(OhSGx< zf~CjE7izhQ+qHi3{VDLf|A883DsTt;O&@GU|Fsla<}iGZ?D!^M%(K10;f_Y#;|B~H zhFQ}K;8DWhft>(OyvX#sD+^gnvQ@PNxTlDo_grpxJd8tc zHX1sEFM&HJfI}8=OYcKp{T2GVljzT)Ceg>gWB0`xWcHkbzw33(?)#!I`wbX03K<;( z;KBHI9BG>fEcyd}`isaH=#RQD3wZ+@k$v40?e59Y6ak?e7PwUH3wq?qVt@H4>`-14Nph3V?%h$>BF>LU0w_oYzLZwb>St->csEDG%Dt+j9l|pR)y>c?y%4`tc%k`$FAh#y^}P2m`<$C&%)pOUOT(xrDg| z>=I1ZckzY~_fbQy!^CPnMC^xsS|ksSy8;@DDtkk zc+Qx*gwtNcOq^TH83jD=SE?!{3*Y!jbw}I$NifuNd^vKr|Yc_%#waU9>^2m&=}N1Hek~_aK3$}v8K2%@^A&}p}&Dc z;h4{_g|A{Vu;?4?fsY7P+PcDB9apC+9q^j9^4Kr<9de$}0f!1EvgRScqG7lmr$c9; zK}~c6{nZEHday6(K`O50vD1XRuYj|$;581yeCf0g|M*Mji0XW3YZPYahkeMCfyg1d zfcbeZ;LnH9_Dlr^m4Fxe2)RM$kmD)=hkh7KS`)@`?WL%bkU{_C6J*o=2%OjszH2qG zXa#B@AH2{0*xM#yf7`dn9U!O$p<%c)0@y=vCZQ)ghWh9#zJ3lkGzEI6v_S0*PvB4* zYR8W;UmT75ppLrT29I7Z{EWl>i{O{@4JMCH0*j8rd-4tX$gPv4raR!MzeYVY8{E)G z{z_{Xavbl>aH_jgpNeZro(bism)$Eletv;VJhe;}KM174vX3RT#E)=$7dy3nI|Z>| zi$vmUv@nBm(hCWS%{Yt6lc)1yemIk#W>VdkMa2B9gjv6@RP@`Snby{*Zi#!a7UuwQ zTUN)pam$J2wP5Tp4qz+`91!ME@{JI}I_C4@-9oCR;Hkm6mkG8n7YkMqIOGOw*|L;cr^Yke%1GYy&1|OgLPqSzzC6pEAP|>7 z&sO2@`Rgp;5bCABlXz<=YNAa?n9X>E+LDe?tKk^6T|Z78)+5AjJiyudW2SKZ2xtG} zFlGw7IBi6gs@uGhv;CWlemPz>z825vqZV_9CrdcrGyt#LJSoZrSZukU#-@EX-v=Y=o z>F~<_0ey8HJQ({YlD5AwA4{IVbzFdc@g^{6CU9r{IB1vP!I&{gZD|A^{f$iNUp`UW z=8tuCoCSvz3p{LxzS)TPdK0t2tH7b(z)Rl*Kb?em+EeJdQ!z6e0c@7hZ)t%$T4NKOcU}Y*tpg4@0<<^$P!s)v9<7J>R?w>&v7_yi$?_dDaP9x??#AN)_qJnZ2pn$N zgk1d9zEaa!%*nn04p~qS4Mm+85X!Zr&hGAhC-s>yrrardR#Ps%nvkY)Ralawn3j$a z#H%AE^`<|eR~IRkz#7h^*{D;-6kFOgjZ7Gj*6p4*SrywRG8z!T)DM?3aZeR7AkWHl zz20S**Wfaz>?Ce`4pO(w1H|JjyjYr5#Q3ETPsaK1w08vf4(!j$o<`{X2wt7AhSRn{ zyA-*Hx_twGTS$%SWnV(=ef$J^e5~a9XdLexJA(=56Nvm$7AH2M&acX(`Zuw=uyQ}K zUfjpoe*lM|OQ-T*k-VtHGk#SHWnC$ZA0ICWdy_=yb*XMJ^0n5YE*jdvIWFZf!(ZUV ze+d_*l@TJJGE?G%=1S5_34-owA~Ov`-_(VDgqu>4ZH9eiKQ<8KKU-Au+)QSVU&6cf zuH@Vt+f=v5)vBYokhm?)<1NZM%$HDmSqkvlvv^xJd=su@!J@(J{tjxCf;4K&OQ()0 zvCLLDhZ=^>AeuyA{y*acS~H1>KTc;d_V{Q&ixmtb;{{_IZU>zpZnIBN$MmDjcIz0k zzj=zfRh|J>pWqyCA0d_zM~TJr7-zY5j5GcB5JAsME#5V%byS{e9FWH8K8PpAqBvqY zmq?Heu9_F+s>Xfrd(E#>#ev&7&Gp^HaBnx!g*9-RrCT}qST*WS%;iui4{$e9FRW?%N{^Ok9<8wtbZjT~tciBMb0}tSMW9QjQ>|SgNlJ4tJ2W>!4bqaYN|G)>j6MRU|{?=*0ChYOK*9RvJ zec33~BCi39=7Kkwj?=@RjK=SM4GbELUZx;ayz4;b^}Cq8)k0Tv1KQwQ&<}NC2jT-{ zyKV(Xn>tnPkinN!fVcJrcQhZlKk%JA91jgp4QinmaV>You`Kh1U@&Y&_!&u}YjR%hmPuP6aPaW{X?FOGyk1UWCAyShBFH28vx&^)# zg4uj9UIX?N-3tOI-7_P*2fjKSrxpF!WYj(H;nsv&2;RJ#iJ0ks9Vp&84~;Z5N=+Yu zKO2KyHVw152;k9v%=yp2H@gd*T|c}>)mOUj4jt7rcvY7Al6K@iw#!qIFE`iKU9;H9 zlR}r=yINKHmpQw>MP0Ni9-e_Q5_@Zu>{=B}NNlvrT#yT$&jv~K+^FHCeZ;8E>UQX6 zahe9~xhM!A2L9je6>`BphQh-2YC>h|Xj!M(6vu}5by?Mz>RTprK! zA5Y`>)saMVe-`6Q7YL*SGo2Nfy}f|U^d;NT#}yKR;;hrgpoOC^j z$lF(N(sL_`-hUyldmf(37b=)>JM_&}^{Vj@X2~>xGpv&5bzaEu zcr}Z7zt+GRGB@Bo4Ch(4TQ5(IwlXHB#n{)qgGw1lcfSI1=35GxO(1)$$?Yl8k zD$6Fey-Rq*tMeqyVsO>n=wnOZ7j8pM^zkgg@OL=Vn5I(^e5L>&g0^2MHQb#k=-!SJ z4Li{vUReN5=sC`2IZLeTj`No6!-BO8nACiZ^Qb*T+^bJeYr;`#+6*kZc8b{i&QV9s zIp#k4D0Mqu%ULh3B4&F$HPu2VdKNr+;v&x6m5TmtEhmT6a6;ftqH*p2^#pEE7Ozp^jHIFUBPNLOJ9#Txij?Jo!Ts*zhsOauADDu#)&|qv- zj4joeP3(eJ^Z;jAen2%u9&(xP9)hm=0B1b3hZ?8tp{DOnbC&;JP%O>IfNixLf)_~F z?sC$3J)P_73Z-rRpkJIkk+r{xerEuBGGxI&JOZxB4`&bd?nDB6b|7=QCX7AUfVtO1 zXcu3@=e`p-^f@r-7%-?e=JUbOG~YlEb`5pOMBvV|X}BJNOBKL}OE`7V02-zXO$j)q zI0ozjsRX|~5Zb?B%;|3-PvA)ckYOz4nDEEPrGr-dzv+&_3&?t7I_w+?AG!l3e37taT1k%2B z9BI9RIw;NuI{hI>#+OqWz{&DngnZ8_yCuV7S_{Ti3 zBcm^}*An5i{|=nf4d@~tK`%W58Ly`USkrRM%S_M;-3C6@2aC5m@p>q*=SSdC4~DPB ztv_m~LC_q<;5GjQtbP-`{xNV)J-JRU)OMM^*uw-(f(`noQSi9Vz>k&1k(_Q~l&Zb7lxVXtU!9M6)SJ6GYuhomRXxQ!vM_m?-pRE<0`5< zkizSc6)hGm7T8L7K6=56mYTstzY=0d+@#pfp*Cv7|NGtB(96JY{{3X0Hu#C$k(nCt zvw2$0W$fD-hTW(QGiBosa|K%_bjF34c|VR5tY?-m`!ASTyF*i6aEN$cI!N6+DtOIe z@U!mVW@B=hC8Sif|5c&7iJ0X*%V3tWSY}xkN3DOYV3x{6!Q6?xtxLj1Z8ms&WRYk` zO=HHt!jOM4Q!uv75e*yXNqYEA4RL#k;nW#o%09&`mVL~+4SO^c)IuKuhyHVt*vB3v z=E|eg@)7oZ2LXesKI1+A`J8#ajQ-8PiZfr%phgau+8=e+C&*a)I*v1amZj=8ZQ$e! z^+faCcA}S&&uVTY#u3|ovA0r_v(pd z-9c*o?liHmQ;Mu^Q=Q+f=Q`C&uFJWSb{0qQPi`X%wqX+O=<82gGXwc2g+b6QqZTR& zknfrBdHs5*c<(wgg%t41yHWFG0$Zj7Z?JnBxzWPC?ZBa}m|^z@?rg;8dJaC*DQ=hH zXXsn*m7oHBj8-5Y4R=L(XBVp z^Yj9KJ%bl@i7(d`gj%Q+8vR`@-MZ@zoSp2Yh>-SJIvM(0-=(t8)o`HW6;k z7EWk~r!s3Y(~JnF{N-dt^ZFd8G%}RRzlBrHkI_!Ul03ztS<4w(mM9t}Q8C?5S4;!a zoNmKPP+vedSifJjzP*>&PwgN!kBzGPNA<)DJ}0-3Rh%h0SrFF-QEhRss)<>q%3tT9 zE?>jRqe~S_OtEVHHit9WzyXiP{?{`loNjWyqDv`r8E@@SO}&w4d?c3B3{E1FXNsbY zN#ryeqnK122)*`nNj8*F!&UGTINj^$@z5K2X6|Di37d#qmB|na$LpW1CFTk6>ILrN9Ge?qNwnIq}5BShUx zWz0MeSv}eY>e$st9bP-B<(Iw0`raYpmUW1;I}ZXM4l{G^DQ1s2&%0j+4t?-B?`b_v z9nTu5L9-TqF8DS&r}LT#;k@QjG-oiTx@;d7sOB?OoB^5ix^U!0e^kfmn|DxyYcDfK zVHWqwUe5UO4$h$2s+!`eiT%GDncG)enA;b49c^{kDYTiGJZp*GqmF3e>xgz_H8J47 zfoWwvZ!KLR^?s75f8o<4!}C9;X}!;^&^&h~R~k@ODD-==LbQ#p;Y}CzQ!_cG*vv;2 zo4i-Gl)!^iypb42LkDpQb?3cC=Fn9!?+q2q>v%QsXx_`*CgHuVJ?5e#k0?*g^=jLO zDz$S)iP{;Sq;{T0hS**o(*7ATEsBtT@fbKH_)8B8!4Z9iovitxz>w+E-Lc3P!k*~6 zkvNwzpD)J@sTdqJcI(`Q_v#LIDc;$Ky*^8T1If7FoWQ4G%*p;lJ+&0~>EW1$_4I9c z99^hzzaM;?$nAJ=2RiEE$Yb7#z3_iQzxT&v($3+UUIhQ=2yi6G0C+qU9@2;Kj-dvD z_L;S6FhlAC?^y$KBt5YM_LTtX-aFv2%D~?=b$WIEp-=TI`>H9LV1}kJ0E?aihdzhq=qfTi>d?dG0N?tI>#4oi6ZBG#yn&+}yfJp< z8F8qEuq*cNFyM0`YM(;j&?w+g-em1ReQ(IYvcM<9zYAZmdWom@liT@wT*Oku`3rK;d{r~J#HB4v zaq3&95LpZVQ%49Dj?Ch9$5W_&YYHcvk5{$T8Hzb0&FOt3n>tJzR72-Z)D4G-V_Y4x z_9`GAm3hMRhIGF7v?9gmwuZ>R&qBsvFy?D%s@AZENZ!RnUsa%b#AK-+3zCUt3TD-F zvyt;y#_7^lahjez7E%Ro`>UKczCW86#F>H^w20^$V>s8y)LC9d5j}{4r6>ErHhkX~r_7l5x2X*9Cs9v{{s9V4^)CDtT)4KUuqj$7M zw+JUA!!<~g_qy4wW_<%qk7kK0`?^Sh8?;a z(MPpGlR0E3vF>UlhHv&#L*@x)JAiuV9pKP^fkWHR^KPT|3x?_{e1>EZZ56!q&2!-O zn#bv1j#I5%GUr&AMeI+@iD4slXBeUTd~XNU)BRLG@p9?4>Rj=aF{!!%ojKY*Luz>UIB@L7!>YAlJ7-w!x+7QE~m z(9if!MYa$4A!KK?4nj_2Bm5n}q9=Qi6~7+*$PnnNJ;4#Vp$>Xuf~M&eaOn{fHTNC@ zi%#J>?*-1~5-=zT-u1Vj|6Ak3o*Y6?^%i)c0N}z@)G1$0AZ<_3a|KLrwV$5EJvtD` z-}@{O^Sl7z-u)iU5O!z8VBgPr%*&RdCW-_O#o+V?z7)a>GV2qi?WJ+9_I=2q4#5oW z0dlAR0zQEAZ;wL0*RZKP$MnFF55id(02IKP5_5$!BgLaPHGaeZn3Me{ZsU>Y8YmD;p)@c zxs3G6Q*^^?6yv@XR8C$XXq%>Jm}w$U4=-|RtE!y3oJyzuL^L&=4dSI2{X`ylNh0=; zir*#>0lN;xk;o1zNmV?9Q#gc>Zk<5}am1;i?6-_Se&RKxH@N?2RWY#mcOG%Pj zATsB>19)|Bpg^LqhjDip<0~SWbQ#$>RgS1|T7!|V z`qg|%9~R5Y;yi)>JzCOvE=2B5mL#VXX}AHWiM6;<@s3!X5-IbT z%kvk&AMXuh+8?IyhA$%oLhEnY@ECueZp zKFQR5$~@6hjSS135xikku3+C=*4;m;y!*vp;h%4)M$d_wuVgzjR@D$wNtJ47-KrY9 zcJtPUM|pe4dFFQYbLNrx8Shbkino1<`bLl6c{P{QdoAG%-z-#3eWO)Vc?@T+NF>&L zcmgkO!QW~(r}Kg~AbUU4|8@xPV;{A+V+OF|FlTE*eXB>lY$m*cZ*4}FS{3t%FXnr% z&k=g1E|=}UWXgsq$kR_)Pj#68L)*#h)j6UqFGb`2YrMhxL4x7=+9d6um5VilzezN_ za%hFF|A=+G_e^9lhGHMgzw3#yri>U0HgK9X>p6{gA+`RJA$fnFr0XLr(Z0|>T{B?% zO8JG5T){J{jIz*Lu4}+nwY|GmZC4vzT|Za2oNaTsr#EnIBKy6$C73=ug}Kn{xIP*& zcfxL@Zp_$qxF!p*qvAu%$95sh{va}%CxEv`X6xhE zaoYgy=r8cLOTdrZgPwRgu&768orroU+fTlCev<*dOU%L z;}`JRp74eAL0@KrPO26f@_7?z>q_`re*n(tC#g?%29igm(C+ktZ+|{=#f62U%vXARBrC&MfE)KY>r}8Zhe$?$aN? z{{(u0WZ=;E=<901N!|0IPnwZ`^da<#hcV~d4Bg|mz|M#0+v;()q82)W*L@VVT#x?p zEW9Y_KOg=B&BA^^+CCnc{5F-DJj9 z#qdILgeW`;68UxElAM!FbS*WC{OMt*;mc~Lz7o2@kWg9Uz%FZey7*)BMQuOKuId*u z)8QPdpRrxF_9^2$b|i=cAM2Tk6fql@&)MEBRP9~Z4^+K{TAn}3toffJ@90zJG3YFF z<4+RX)%~2Q5&ZD08IospmgMD^DfSA=5}&)XN*Lf@%m((}%Iw4T!?$~gS--AE9gJQ! z1>XK&%ZWUE4P@+z8guV?2Fs6ex?UeA=mu0J>j!Wtnm&Et_gzuJvGFyEGkc4xQ{CNwN#{4=K*XJL8 z^4&X=bhm*+chA9(J{Z^aYGfFNp&t4hJIs#xV8=cBu`A$_mji>gqeeLZ4G~U{W(ave zO2rJ<-+n+5~6o0c4o< zMxI#}ZqK6*`Vl?kQtSr0jojF&xX)$ONZV1%vY*5+dG<0sf(`v|uYJ=lr&<&Bx(x4@q3;DnGB-uw-EvU}j~Vvt8L7QVcQxz4uX zk;=0HQJnfNGBL4TcOH}7Qs_FmzckqN30X!S8a;r^{ZA;F6^6TQL zuz#T_Hu*8RW)d+5MhI5@Jkfe2lDBrjuX`s^u!bf`W;ba3+|qf+vovOXHb>BZgSk}J z0&4y|Nwpl$RrDjOUB;00s;OWllkTSxY2*Syx&*Ca#8jerZ6edu1o8aoxuX8pdCYi! zK4+?r<;*8%2)gnxQ42EJq3^NySS_5FjK7~&?MF^=_E_-RCgc)y$4NOj0Wfn4xEAa_&n$Scn7t=R7$P7VyZt_LG*seCmNn63c1l5>9zSppt@9i!9RyMnkp3b^A@KwW|y<$^=kELbv*0r4=v-5m<9cddSVOurhH&dG-g~! zP}jT+>==StC;_-K1iGML^lI2Ocw+|qo|u*2dJ$Oj2k<8mwa>foK(qsAuy6YINZfBC zYNJ-*)qKpPccAa6#rNpVZa>95O~m6RgD0uR?5IbRR0}TqJ7^J4K<{{b3Vo0cJ>NO> zYZ~ChSD4{v!v`ziTFe3Wyk!z=9Roe}PsmDz-s#@I&^h(&I$MD``gUN+ZRi2|BPRe_ zqeri!ADabT^>*C$!^~_D@MtnTzOoP38IC?G6~S!Bv5Us5EnoY(+NMo%wtf}hYWF~nnZqBwT>xhN0o>yh?6)3){r1O! zM<;tS8L=1W17rpa1%~+HdGjZ69S42YE-f;pk!#Y9J!LKbB46kra*Iy-2=|8qf83By zatE{edd%sIalc698STgX{xP&u7UbaQRD<#d8- zITA?tf5r-~6d#@;??>8&Y`~czoV+oZ(@%=#j1S_dK|h;HAEE|p@~2$gSgGTqadO8s zAC2qPP+qmq709(%m9)ih`eO^2E^3A#Z=5A+cS3VFJ(VXvEzoeT2rc*WbdeN>@dAgv zjd(Be6?olVh!e~c7l`JrnHt@?$+G6-SYEp_ov`3c!n~4*Y4ALaIS5|VnG=be8YbGX z+uEKUB{=57voZlV^z&lTtWOcGeN#m{bVAm%;Ue}D@w$|`%6(BT56|Q*roH@$_Vl-ijicwR5O`X&5zrKZBW&QK6k4B=A4X6J?*X zisOZYs%hyzWBn*At$5pDmu7AePK zIoLprFJd1573!hcM>u1~K2G~fwW2X#=Xb?o)sU9PnfflH_6&Gb?m-*AwTgJIME;cb zZszs@G^l%b3)a`R3C35q@b)!ZS)WH$^f}*3W*b(9Eb>jv`{(VvXV`whma#{$o!rIS z>JC!d*^|WKeT+ITH8Kl4g62cTs^M9!qBZPxX}FESwXJ$wm@J)0QZE$4zi)hzpg;m>q6|gUJp;oRp5v>`ocDx zN8pGK1BdkfYF83)=+()jZ5T3Mk%iej6nL2e?j1WVA07i9Jp&H?2e0)P^q+siXNS!H zmXA==-G(3SE96t}nCt3jT`YHu&J$b{D;ZA{iKzkevAcnUQ>Rc`97nWcpnL0+sM<#? zRqTVY)3nFy@ey<`Urpx3Sy8I|PPA%KvlXLvr6TE9t70GQ*$MX}V%$Wgy*`yUl}GWW zUbA?uIv?4KiA4A#hG~p5Wy8e`RjS(T)cMqRTVLAZv=pQ(hA)B{{o9upyrZb0D~U7z z8%g1nW%@y=8(xbhy8kYu;`T*Unl+EeO+j3D>Ljt_+9W-)6?wiZh47nlIdUUk)rDkp zhVQ~f&E@HmW;DFy;qztoEWHGs~Z+=jRF1ZKkmUD($c#^NJhSw~IS^1UJF>(#{$gbo(4{RkK|G{kPl>^M<)t!PPuZmgY ziUg0U)ndOtmJ9vwrSZMm(?!qkbA{fSWz4(1o_B28AvnHoUOn~n)Iba8(5=i^m`TEz7sCf&=W4RKXi&OZQ-nin~CMi24e6A*ZwW^0EtJag}cBx zZhghsW*p~?8|ta<<4WGxR?0ifs|9y^f~N1^(Z)f&78?3*M)q4E_QovR?sV?o>T;DV zBd+KGp`#~L9y?GT39P&pFgJ8947iC+&gLqft!6&rbuNdS>K}z#-(t z-hKK1&ms!xnFGN;)dC%L6EMdUzxOBZ*R%h1GjOOF_0S>oe(R^gQw9FI7jOu5)9onS z?+5gK2ccD(1TF z5$c~0kpc8PY9i#BHNOTe_Hta`b1_HX559N;uxlFd=svWK7Su%Zp$SCJbXyBB1X+;C zRFrOA3xbv~P`>F7zW4@mI^v)Qu%KVcM!sUpB()75`S#(cgB}2fP6sP(&tY!$Q;5?3 zMX=h!2dYmR1J$;@z_uI6Rnx+Fxcy2yiTd2bqR zq0l4Q@w&QzL*?Lwo}&NU1^hY(e|;5x){K9D5s&$@AJ@@4*!lEF?7vlGT~BS9a)*+x zZ@*V2u=8=exoom1UBsLyKU@%hKqlB|1o>$6uL5q*?w%Up7g7JQH1 z%Q&MZP7!8@iQVo&8X+%{T5F1^P_tH4^fg3tuEb?~jD9N|8tE8tMt345J`VfLjv}*t z?qVh)|5Z17j-buK^LfTo+p%QoW<&n!%>=<*fxU+(QixeWc2Uy-RabqGbNsr8^O(L4 zp4cLy8L)!7eZQ1>?}_C-mPJbL6XFE>ibTPFVwvDJa;a?jVxeq$GDl;$0$tG6P+qq? zl-It59Q+i_;0xyqrUi+-d~+eMjYreb*D~!cD~JTdSH9HmN4~ zBdofOioJc4YJa%~yipyo|AD+rv60#S*v{;uHV}&@lNy8J4LrS^xxbEC+(z`2^~;&t zP~_acQ^MT(S5vqCJ9)=f2bfzYZueDDdwRa$7Md-1zmiG&)Mk)=)+C`9@*h1m=V7K^ zNj+<@BZS+=dluF4z2$o5ePtVX`fAixMZAUO2pYdasvEYIGmqM@TJ|4s8PaN09al_^ zi`Fv3UhET6H**@#eMCR}46*dPz}bg=&RJgh5}x}DivH8x%-FSsHy*9#b;fMb(>KmA zAT`N2ASp%b_;@X)E4L`lkGCq$mojMg+rfNCIBExYhaT<&4z)n9*o59}3Th$`;Lc&p z%WSA=z5`$MKF$>25_}E+J6*V4f?DPy^j!miB?LZ)1;C&Wzz6LIMqL8VpvSulF6qwm zz^yyLp=YRveg+l|MNQPB1?Z0&$`_B5fPO6u&wCO2?SaSua^gGnvH_zWn-dzc0Mfo{9?b70R?f1!CguxK)R(YKJ@=n7Du zEeLUT?+oKw^l`4I=hNAij36!OlBRQ&b%LI5D%eHF)hHZkAG8DUHcT3U*t>K5PvFt0j-T? zx}Xb0F8h!a=z?;=#pLp)gUH}{u|Tm5$Da824?1lh?s8d_0>yl0Au*y}(qD>_4X@9Y z47b2XeHO|KzTtv!6TMkh7H8g)ubR{HI4z&Rq(O6-u?hZv&n*ItD3`d#ts?6#R_#A8 zg*P)+60}jgxGY-W)kT8%Q!D zDZE>iiXk1Ex}8UeE%1nHKe>l9HA82Yvq`ajUBcN8Abae_9La4@oZvW-BskVBk=(kH zCF=z2ZI$N9rqBh<><9cg37x~F$wclw6CPFIQ2&LzVK4Fq_Q&z2y^E+V9a&=D<J-oI%;VEq;y$C8IH1LJ zt4N~u9U091lMKP*+c?>g6D`|rq{xn|d7`J^2I3Xfz`K9FpLdt`@{U^M@E=@9ZB-@I zGCH4gyO6{6`FjQNYR+cn&|KaYzLt5vSxG!@LceflEAx1*o_RiNK+U+7y1!b6eYClv z?msz#aZ?d>jMzwQpKnri$wi!QP&#kmkgc{bi`P!BrdsoUX5daz>qN{i)l<~?+&7%| zO!+elMJsx>M{jk5e2%K~%^iH#p;p&C_q=!?vC!x6g1bIQLkzuwNv$Iz* zFWUtDbs@CRdyy6NC9voUW@%@7GO97xUmZYO&CpIkm)B* z9ksPiQ|~;N!D|s`dKw|L&c@F0{gy3R`=&H+idtZdM9bf*RcYh7L9E7SW!q@APQ1E(+0-=FOS)T><#P0 zbmoXdUlu<{HbqBjbo-M8^8jdQLk5 z#_;BQiOjYzj=Ht1;*xP)T3`6aX-6~xR1=F9@Z@Cc$&#<1K=Y`$>Y5-_w?>sPpy@;yt%Sbv|rgN zc)i!NPiQ;ei)^88KW!kkYo)~QUr6kEMeyk76UUp_uedjb_ehVIZ2hxYuck`!+_zhK z&(|AyuZSJI$A7jnw}Q>Y5sh4`e>M?Q*d}7DgrB!*9nrpANeu%^(a#kTkHL7{ZJVfV z0-B@YgFC{kbRRP|94DsxpA%!wd1AhJkYSDoZEYhJ->%_w<-jtJN{-yw$UTkPqCD*Y z7OhAlU2Va%YZSD2f1yV?g}K%D;G%<3_kd%*wFR|I_W$Y@>Gnl@ZvhV70tPL>F#%UT z#Lu4pA0CO|wS~KGz_G2Ur*d$gE5M+usD0oCxxE}X^aJo~HgIS-?l&KO-*{Y$50HO) z8CbLuxMvBKZ=D4e?FA-5e|`HJZmUp3RpS1=z=7|^WB(U8lmiTzh~Dh4@j}at(c+U= zM~RQ;e=Iil9xL{6y^l(89d{#x@eJyvlfa?VnAvMFyH7xGHXHtsQ0zsb@b>-yeUXA% z>09iXj|}4Pb;5r-6DK1`yj>h3+_?)ZszZ-92H7IZkZb=^fbw)rfYK3yd?W!Hhnbk` zr2&JUn@k>MfE!N^MvfwOI>rKrWf7^8M0-V z@6AH*b=60B^e%c#J9wdF)ImKyj#RvlBJ7g0Bk%2N>_mzRa=C7Vy1G-s)sDAk@oiz@ ztZhUnd0I7{kd?D}$$zFK&zm7jd2<9QF^L$`F++ByDsKOcA;x`kiEc}jstbx&4P!Gn zd2lW>g(8QiNsL1O4u7LhsCEvUL_QK7bC!2bn$-us5i? zj?-R-zx&!k(Qa8F8jmbumhoANS7KGS<-@Jr`i4@c=^`|Cd!YU8JB=5{!2_BKZFlK3 zroDij9Q{`j{#XXD`#D-N{TeHnTvgrnqMc8TfklEmdW|eTStJ{`B}1&I`UE^ z&+J^@d?;Jg)fVz@=L>Od7Ax*sS8>MM%LH>rjAR=Pj{8|G_^w#lWQ48}J9(@vvnAsn z$ZNb3A<4lL1-99jia!PuDGxcXp~y9x1^oLhj@cf~5gb1y3x?MhnK&+2t1FHdyh^7* zL;8`Z6n!KxUmvPT#GEdF3e`rT@A+UI-z&J1dHsUA8(6Xcehp-5 z*sg4(UT>E09$&3t_CAHwea|}PRaYx`Ua#l9p49W+-_|kj9d*=m`Zj8d$1MN+KHfHR zKQm`EQ0=*TqCK&NGi$3<%l2y3^nM*NhiqpKu0e1sujk!Ux9}dTw(;&auy<`zCH3f? z11>y+m`)=P>4mk5yt`D<9AD2F^Q*7}sY>WAZx%egxAPWKPt9u@nW^_4z5~}c?a~B1^ZO(vPP9=)N)VvZQ?rgHLj-zH@lpcm7GfoAy0pU=J+1w;|1Vs_M;y8 zFKVE%z?ymRjlBroxIZ+B;lLKuGk31Q!%+v^Ai$qhIN+aeZNj|kH}KS7A}5|H_ioj(shfDY;=beEU0 zx9kI)DSli_5xgEtuv@zs_2ygPj4mMSaSn0@7&xr{@U(p9&o}=UuVDlFvIF3{9s!R| z0Go8sOHBk02?4_6!-4PtPFB?)7PND&h6)Drav3Oq?-SarMHf&`uyL=Uw6FfbRW`~PTgGh+l8GNDBWd& zg&3HqSYS7TqS%BWV$V&t_nZr3bK&NDn>qt5(iYBneJ?)0-yiqm03MFIc`ly!^}HfQ zP_B>n8dhWq@|)RA{bm6-{x*(VkH?|6J)9aQh6qyO2u*x_v>+m5hVM@!y2b>i+mG(j z&~$DIoJY)4*J=iEsOB}}B-_ncS;`0lZxhM%=dwJO$J?8Y{q{XKf3~8KPL|VFwwNAndf`&%$i@YeRMV4Wy*Mb@D1JA} zC|^z0^OT7?a(=u*J7FFgG=?h|CNdrJndMq!&@TZGRh*_;A1ABkVdx19PLK`x$Oco= zCHtimo&9m5s{b@fRVqgrY>tsS$K}zYrT-|dH-p=G9l0O@X_84Ar5Zmn!Z*E+bEI`A{yN8p>rMf{LbaF1|aO>krH%#)qRs%luGgJA^#Odr7j~Jzljm z26KydFc-snio!=dM7AP`;`@p8aWEBsDrNRnd${x37V5JIoXh+oR6cJfP6Zv{Et>?( zd*G%2SSr{bVK!Qgu0|JnJP0~a-dZj?joU=m!gB7^pwWptKrPP?QrjyBxxH^0I%fAW zlX)MrcHbwtEqf%_w?))>XA5x;-An`97xI9d9o%p8P9D&ECu?(RH}g*`NyHqn)R(Opim72Lj3AH>a7Ol(nh&CMOx)RAJs95s7ypy@J zHW7Qw5@LL?SWx^{c-1&~^7j=8&g=pnFm#>Nsbrl$hSE)WpmZq50Vk^0=-*dz!M@^Zfh5mX@ZKnzu_Fd0Y;i%MZc)elcc` zFCyg{1-P&p9{J62>|WOxdj9}+O6QSB{WY-TA#ejYQS%7+^AGN1Rd@^p4qXQh?M8NN z3ik2a!j+o&xU22M`}uR^>O|t}J;D2Y^#ASCs-GdFV+7`+Mw~zJ`&^2Fwm3<=_g)IW zABdgV$H1rGfmPlJrKSh)FADt7DZKvG5b41x%sU4M^7_L)rMj;Y-@NIe9UX$l?nzSZwb2P(81#Us=?yj zW5e0~gYbiOMUI&bIb|vEsU8oNZ#4stRzO>o5UM@8FuditKG^%n1{{h8m+k-tNfD&( zWAug0$Bc9g8t}eDJWm>tv(^sXjZSb&m*7t+#(4$zyUxfexd81@9y0MyA``!KIIkOt zz1RX^&oJOnYZlZna6*OPvLmst4-DqWsAP|MidVED6Zp|;fwY;Y@iVc4G;}B@cZ0YF zUmz_=l$C-p$kj_^`bFuQ?dnX;;>N1m>GvXB05FybI zM{t%kgmY*K#IWoZ{f#XzOy}~N?Q>=bmd#@Y>5mA;B8N#7c>yeQ7^7{6GVN3!wYji| zzPWv{Bv>Y)?{5}&d_LY_^rRS-;3Pd^i3S#sU=-gPWfTWZR>aRHNmAT+t{BI0{f053 zS~`^}P1#)jejY=Hq~scqE?T=LN~X1u+|V_Rn;R#JmiLl$24rT+hG0pw1k1YJLuBJU z^oK-Ze>V&K+1Z&ycP^7yr%#luJFp|WiOlImaom6%pE4r{Flc6{jo}oF|qL+k5ciuFfKk;%x5v z8NK!$OSo;}5oA*1oH>c(?j?>T#b>G3A3k#{=)a}fF z2<9Q*T|DsNZn4d>y`t~Fa?u$Ez4n!(qJ0QXP8GNIDyODHCB%5VOfwZ#dCad>dCd>u z0eh)}nQxVerha9TrSE>(`Rjh!=heNUvuX=9o8cd{WC^-1$XLi*tC{oH5qHyCv3;i{ zY8P#mzVn`Gx^~|Aa+{BH<+gdN58di%sa&Tu7mcD#cfqGL1Vg_ATo{Tu2t8gkM`Oi1_fpCI!ihrN2k^KC3|Fc} zc*m|qiFZE6j=dc7N)hIjR!`Z7_*P zbq5!Z-@}G`+8*H1FLCgLB#3vf$4hsP$H+B5VczNoKIkg`3|(>WGvay3xvs(NbZ=n< zuWN_*@#%r+0O~D0$mk{20*CI$0f(A_L;L#DhyP;!SqcqdCUEB=ve2BsAmC7A6ZoK; zn4_-X0C(#C2!f9n7zCcUw$~v3AP?O=o#B`N?=S`Z8uFcB%t2GYPelSxL-2PvgiejY zBU+ws#?IacI23{Xoe)myCgQw`dFTeT1=VNDV^2m*(1i8q zq0r|E>;y8)&(HGeK8+>1JE4S=V`W0*juH~)ug0! z%3hBYg)zfK`b{ts=Z8`jHc<4w(M$I{zQ5WWAF0yDQAQ;v$s)xjn%JWx1N%L}z@NvN z*tK}0I5ACC&!jW;6ucj|kk_~hnI0sAD|z#{q{I86(|W=275tv}Gl}V7Bvao(_gYYj zZ1@{Ec_>1X?4eBZ59ad3AW1nI%GG*wB3(`w46V5tf3Fq{la~`y{TxxZV6vo~1IM;I zjHsUiJO7GhViccgk!_4})m^;4QOxjcxlD75(LcsGQn&0Vi;%-+c*oLeo z)};%HYdpBR2Xm8TXS%$a7R0id(Z30bo>x^rF3W&dk&!WN8q;)MXu}5 zIJ2Q~{x_K4n~JP^2k__#?xpWTtKAEFqtVbWHbY-I4E?J^zzZ3{4`oEqS`p_uW+V|h zrwQ=O3c$ie*vbD7`kyYqqJ!AWj{*POa|lCcG_SiI3hiPjukV5GR))`8kn=&2Wzq@v zM&z3`-2x8z1hb~6;p|D^cn_JmNuZa@c+25DLdrH!(r0Chf+H+;mVI-GzGSvw+&IHyJ)P|_c811za~zeuu_EmqLD@gh7Y=y`A0{H(B0)6gkD-Qos&kEJ+u}iTbniiT=bQud;rw zCf~#J{tg8P6ff#@;H5J&iF6)ahvUbKq(5eg??b87F`A&`i|~DYlf|G>^#tn@UWbuuCgPuSQvtB#)0^(%=|m&_*z2+)z<^0WYmzjA$61E|@K= znY28gsQLm;U$c>@KDjbm3O&;Aaoki8MWwkZOzt$A8$Ovxt&Y(|9~Lh%5&cQk$=sA2 zNlmkYnNl(kS(ZVJM+{-)k02&U0~vi0LZv~_j{g}!l{j2JUk|fES9yYqjR5r6CJrafE!UB2uc}7|HMY0t^eO4&ycI}C%hJ=$lm`1c|sXO`2AHw`TY#&JJAtP z8wtI+4Ls39j5z>jhk`PjMWh#cJ72Z`?Ka-0uw$I6CBwHNNa zM}R+1abN5Ue0l{l5$-khV?$Zve@C!}bnN;5fvQ(ww@6p5NAAz|7W3vxulY4(@D0ZuXITW1-$|f4!#KhG)?~rDa++qH zF`F17q21{YA5w6(X5KYVuw^Hp;}CeVFPzGgM-lf1@bxy12OE_V!@E;)CNQY>$e z5e+xTaNQ@!QG65H$VTuzzeiE|W9+nhA^1D%>M`s<#)o+nRbL$8f^Qw29qH zvdCYi7*&0eLH#L4uPhDM@i)?B_4YJoj?QGpqS2DtFj5wivp8FhJoX>=Q*~M;mAhAR zW$+fS^^e8GF=!0<(Fw#bXd<|*Ntzk?K~7YF z7d;9G-(>K<$(A91f0ALo_pU^W*>?kGY0=` zrvaRfLT6h%G^A`emEcDbKS}0lIJE1T!#VHNL-y=G#9h0}koygOXl;&YzPpI)3y|Y& z-a`UP%BkN+#mt3HGxx~3%xAUwDbbu7<8KhR2II)(0yZ_w|kE#b~J%bBt3Myg&f z5_I41hK>>0KEAuDc|tKWNc(ZWs-n&XN0{4ol(`e}_{$;Y`_BR1=C3lbeMyqF2k|8Qjl}=MHj{ zd`PrZR4P{6e%bo&9&UMBK#fD!YNi*^Z_9g#nD=m@b4s30oaLrAA>o|aSjEiYzlX>NIITeH@8MT;jU zUVF3zvjH-e@7(}?tVS*u$I(YAHGQL$yUQb#yT~uSLx4j+12e{fKZ16;dKKoBOw2&o zncW%<&GbR&j52{kq4?ep@bNHYp`!Pzx;3xy9`Gk4g5P@x4ATLpdgIPD6wk9^N7e@O z(`sPaV7%VjVam-_&|=?Iv~D(j?Q8f=(!c|CMP^bu zaA*k5_n3#jBi8_3A&GteMSIsp-x%YI-(_ntDL%ykZ)$4@i-9 z7vL#9oh+(3=`G3*+*8!G#QxV}(S2vC;@p^`vwn$eomYWH$W~Q-Mq{=BuB4?f{UvxF zqDOP9C6gGZANl^#^sTkIt2H?k;p2HiQ`J|1X;H@lAHXXla`@@ ze2~m_6q#kO4OYD$#7U$O9XaYUDt4NtD8e|S?o^u2xNm}N+LoZxFAUL(wJ`?qmw26g zDqfYa$KXW~3SFC`(!;reZhR4ye?G`mANV`dD?F~t`Cfb7Y{A_=L$iOI?bUxh*Q@&g z{*}roS?wDt>PRRvz7d5E!cmIh-6h=cW8QPil)@*LRnRgY$dPRQ=1CTcxhrLRt{wyLT*Pc;Sq9J9iRLG5=Suqf*qt3l#@y1Q#MdAG)1d{L_1|@x zdCDrm@@NIIeV-?o$LuE7Q#+~8_>IJO@-pf;w47PyBiH}jI%;3DkvjiCuF%3FYDnIT zJLCaoyK#s+2IKCPbe!AI9cRur4v9YUe%b$8ncOB}zuY$dfb5@8F8gHQo_27j=>JAO z_xY`W*?SaH+ohe>)cO6ID|JlF=PvmWxA>f-_E|@X z?TZqs78Mb)5?L5hnrO_`jfgEC84M12Xtq;4>QQn?yli@sA?nvpq`KiFy>e1~3%{A@k<1gk zrjhB?a%mzrMW%?dX%sa2qo~drCGj0eI^B{f$XH!UgeUowtt}#khbxKmVTR=TI6-GI z$H;2MNLiI9Q+>r;q6|SFWAS)m>=w_ATa%da!~~`e%M#EXMBFcp#J&r6wn3qqxM_@6 zmoQ#4-bA-h+XyOk55o*Jf*Rk(O!PHyZ|($UJUU8NmJd^DK%&foay4njdO=;B1-zIm z8T!r=4ee)f-AqdpP5cCqvrP)P`5CZx$aG8&YDcoHFyHE zeG*HR{|qOz>u^eTg;72bI5aDWNgtxq489n3RSZ|iU`D&aJ0Ez8_>3*(_L+-S@%DJ5 za1{R9Zu6P{8gzTd9U#t7WOGa^7i?Yj2yCX|C~}~xj!}P81@&v*O`Y-i z)Jbz$z~>9ZwmY)L_CGA6ZDy@u*6cj4U$BQ8r&e%Fw?m>i8yU1Gj&t|?!;)X0a>e)k zQaP}+Om6!!9{(zp{X6cH0zcm=wIN%0z|VQqcmF2pGbo?So=L;Mn|e`O6Z9fEi6-OZXS z0zI~?pqc2oj&wL%B)SGxiB{L=%vF4ZIWO#G`gH|FNXXM(EXTg=N`6ao4shs;d{6VO zDcYl5;D0-Subzu+oc6$p4&ba$;_l=JPWlsI#25JZW#Gm$;7CmC{DRL9VdnW39MPcd=^@owdx&*= zdy4gMbQkMS_vQ_I`~BZ;zrl(3Ju*8m>v^9UgS^lEka2Pwnxb*gIGzVTJqmZT-oPIC zVC(${@wy4n^7O)75{~&avM2rvG9hi13z^IzOzQ$)t=#QcPLbAoQ}@6-SC(# zg_jjM>-EQgKh?m;uQ3~ai+z3~aOk@*))W!O9%>naJa(hk4DFcp9%dC+oNV}W5;g7} z$>na)9hc8$$^&SQ^TrD5uxKyq7tjUe6FP9%4^V9eX*v3@?Axr1X=<2T`wJa!bkxx<;fe<+iuhcI3jA@Z11 zNw*Z)&0j_8r9SaG8at1PIk`k%x0IMqu4d}dwG!#Rj?>3EqS6<0!w<1~@m`pTM2#@; z@(5Yc!bCY}gd{x*lT~D*S&*-9Vk<2qw74vt%wu&$!HHHc-GnR<|iD%-6AxwM$&S=v>E-wux`rp8L zZ^fQ18o3?spAuXb4l?JP8A@|JCEv@5mAA(*%ul$E_?NC`arNk|lL8XYuxH7cd`P4m01`M9rsn zGSkCKX552JS)W6qfB$mT_kNk`a~0Tg1{~3x64}43MD}~RSoY7}EBV`YasQn~)c-iL z(F9}@jod=r$Md=SznhtFQa<(fY@_akUDVQ0%#F(rGSf8V8hwF#o3%*2dSd{8ib zf?V;N=;<{sr6#|Pg5}d)=&(fpZW1~$U*3s+f?YJQQ;Fz03+`$(?u@6anEOv)SvqIOcq!E5~n%u%4p#%`@92lzJxc}Jh(E|-Jdcu}a(Fbgw87WVe7 z81helvA$0)++V;Aecel}Ux>4~hgiQ8Ut80kHTA*0Y&rI1Ut(WoLjI2t-DJ9ftZ^`W zXYU{ng}Ka(d8h|6B+*UV*oeN`&oBdhjhvDm;Ien2`^bbp>m%reb|L5E72FBG2M)D` zzyCNoY@fgf(hwsw<${;{5r3bPLs$cHsp~~x(Lcbn2yjNp=(d!ALuHY?Y0yGJnN+G- z64BY}8P5!#rAlU>#mqEh7E#J(2vS%sG5nduES;f|ITE3X`H>!RX0#@*ho7w9IA&Zt zQ8J}0CdT#4ylUz~kM8DpX8$jK#{1<6CV~#~rRWq3+vPFLMSsvT_{7?v8@X|`sIyL| zmfqOipku>%3cH+-ClgcHR6%_;T`*oxW2TLf$S4}k3~7_7Apkq=_29s-P0}P!GU2PE z7#{?GgcDw?m!QE(K}JSif-Fa+DC!qcdUPBkLtv9MR^6gvr*9|r6%5>|0_~bx{VN}`9oxJV3Z^sSj3G1$jk4P&2{KDHJzO# zxrQdozMm&b)|H8(%>(_@UF_=rfJS;WGCl52;5rs3DjUHS9S9N8pA8>>787bVdU*x< zk5}x~Orxtfx9q0gxJg8+NEI!!QYC8|vhw$hRFt$avc5mEqASCh_)Y|oreUY(#EkiC zh$!CcE77il7(Y2e(BWQgSTKS~C(Ee4!x7@{afJF6?xF#Ap=&fOztZtTC1nU*c2 zrrW^auw{bn&}w43uwKw_fM2b6hhUGYqV9i>F`vUnx%-c!sCGR{{T>};e#duk|FLUX zyRR10_PWV9@#ggSn^+5Dh4|D$$saH zCHH|+$;VqN`=*vkes7kD{vms~Pwp<}NZ83;3%7}Wee$^fq_y1t**auG=27d9+lX}; zG>{$2ndS9E#PnqqF;yNSmhOiH`v=JHRMrT#{Q1=J=PG2l8E$Z||T+aWfTv*+@KWi}ytec(7M{#8D z^E+O=MioBy!duoGd`x5rdFT&c#Ch<`&-$^4YkIP#x4VP$?ZxZb_Tdeifkg()Ln+V< z9RUtq0S=wUJo5KI+Tb2Y8?R#S$-*xEC)~%1z!g0ME**vMLkHjM#i8^`ANcT(y6WnwqW*Ph&tL=kalCGho;U8pbH$~EhccW0Fht66nvW(Jj zXZv#mX$-)w{s-K-jL<_LA4(pl0Pg~!DLR3_M>lXu&!TDFee^zF#!U7)*pp{|5N$>mlfl3};PeVxZZ~)s&~$ucpBd`BfG%{=0~pJ}M-pzw-qhe3rTeYXr-> z1>AmYv|@M<+;VoLSA3C3q<|>fn%@P>1=pPof@%FyukpnakAB%)&5X`N z%efeC*a+R^JJYFEg~#IiXl6J$MbLjWS(9%^2;wnh(RxA|`!b5ljz_lZ!ZM!4H|3(#aW$mG^Yil(>{|xHWEe6>oF{*9w7}fs% zT<+^w&f2!$$X{NtRemXKm*V#l<{&rr_kZn{{lD8QyIK*KsSqVj~I}+o%_dcU_J#axT6wY*B>`fhd(l=-`c?(eb5PctCAaCRm^^*0{^C< z^M=0BaXyE-&n=_wwfV$(zJ%I`9T8lkzku)V2z8$=W6r1Wn4H_keLvgIoI_VL$H`@) zb-_xecHBV7s7;!u3Y@jy?v@vO(L3{Hg3x#cJEle051q#SO9uA?9nkFPH;?`N-Jfy#VW)K!+ULudVO%)yXw>ut{>0+IcYpgYaMIY% zSN|C;)x3wt%lO(w@J--`?sf#OUBUNM<2CYuL31L-nvZ~S=qtK&3a^=gIpjOc7!kmP zF_>SDfD7u`gEv<6r2|+~?hw}01N=~jAwuIQ%rx+qHez>Q ze+MTBSW^d%x-GD(2>fvk@($tge0pafd6J7P#Rb42hMj#2vbam($A5zRU_TP(+VDQS(2cJVPE#VG^S*U3oe5Gk1UB+p~ zhA=_-8NA7U=&oFg<&Hgx>Q?w1M>+V>T?Dsp>A|bNwB3 z@5SeG+!QoUl-EsFo`=nmNZ(9urtpv~f!{Y|q@;frT^*I_ z#JCSLCpsoXb03MuLR&N`K{PH#cF3_j!6ym#oB!@%eqHx5|7*wxdIJ9CbUw9yw}Qdf z!QJTc_W61;bACCF+ipx0>};mj_p4P}fZuN7N2;jLz(drpyn?lJ71MV8i-~`91$95D zqORC&@Wjq0zJudLdrxF%>tjXBy9u)EOsd*8YL49bgJsgon>UHB-8&_pw{}Zyi?{PO zcejf^7U;B#c8Pu?i^YH^<)UBKe#v)GndI(VD)~+>lLAI!M*3+xb!Q<<@^p@9d$5u_ zHmsvQ-8WI!%>ruvqKKJNp%FJ0@xZ)7=9dDD`f5G#TfCb3F9g>#U?+9xkn7!qOtv#6 z+dne%c8;05ICt%Yj;AGwg2ONbA>)$Y2Ea@)SZt5X7^uWG82l(^tXUhHGyDJaQ zz~50nfHit>zxrq}t$%YUts4YR={NArGl4;a2GNFm;pF}`k{Tw*-O1Mz}S`?{E zjbir>V4iy$fBt{MS>4yzx#a-=qOl)C4pPI1xPQ%s2lstw0JcYn4>+=_gRmQ`87$YD z2g$XYfkB&ab_PqeH6c>{&!JMC8X?v9PGS$=m`BKl`Ai)=Q8GW9L9O}d$7n!L@du}f z|M$m;Z^J?A%eNq-c|NzLpzpmJx#}Y_(AR~2i}#i=Gj^-K#)as<$@7|h54Kni??J{W z?s#Ko5qtS4+1dkp>2~NA%FGfRzs)C3VKuQ3;7;>$V!Vgm`p|5_cgaeRS<7!Re7M77 z2t}9ZMSQPyieQF++~}OlU9r%=EKL<{!{a66BFr0!AynQD94d!~rxH5Bx4>unCn~x% z$*S~Eq9SF*=%o2kiqH~gq@7dD>gmxYL%Ri%L(O5n(_Ue3) zM>ux@V=1^QYZ%akC7kIWc13BCgT;kB9zcNG=dP0wWb~qEE8IyYsk<=~FJ&qp8m5w1qeVT+W zo{7v5nkjiZ&*kE@SQ~7A#RufTX$Pb>{>40CCvpOU*NXPlt3;>o8s>Vj9-P)%W_z%KS-a#hSHybW zo^9Z5BepTWD_aG>jo1O!=TUnPWTLHuU-#?v)N*tyHU12)sCb9w{$exn@k4L!y~W6G zS;Cc}xm0&%HQ{5{5^Bl!w&=GtHxJtM{Kd50fgs~aRXtOpJuBcyQ+@~>NW{T;B!e;A}a85_hOv~>+P1ilwc=r5IBAsNFY5ptdOMQT?T%&-hGQmr4}M(CMYS8@^?;_a{%!Ep zU64zSOpiwbxa_%+z@bp_!SUg|zCZT!p8Pt$8sl$pgIy|>J37_~t|=O?khj(f}@WWW!}pw^H%#JYaI z;8?s~vs~NlHJ>Z;nl9xLW5*o9Qn}1y*s!TZ*L}OkP@1n7r^HE$Qv> z;hKgsc0E*}U*qn2B!Ze2hGLEyDybs}i{hH0j2psC+qZ@x|Ih)+Qwaj$@%Hu``ZV6If!^sJEE)Rv2Z z@QU~c?+{%_H*>qTk=vJUrmo3()Cu0s)@>7Ylx$$WLsql4EgNZ@E*nW;#2V`7+{ny* zHi7qA#mpnYU;eg{=mU0Wrdzwbjwf5Z&THVl9;_EcpG}(f@kWglZP3Vb^nm_@OvcfL zFPdjerhhW7Xvehz;&Ohw-o^w78~wZklO6LZc4?A3uocZ%WhDnstsu@LoUDmvbw z6}p97BIMu8chIc}opftPwRk55_|q9Ub_qQ7Az)Bz-Zi+P>K5RTIa;Y9$oIH}a|ZY` z5_69SxKsol>J+%6IPl^h;`tt6-z>~rBIcyd5$yh_cwfH^-UU6RP456piaz7@)t^WW zK|OfWjNbI&2fgT{9=(J|j|PxOFGH)i5_cWkGwbg{tJ4K{%^>XHUj<*i9T_0~T6gH! zr$2@V?I85aCGg?@0o`C7@aqOTUk?uSK7E30Y6Us5e&|>{GMv`lg~#p_Ahvo zuLC6VG@Y0y7YoL>zi#nqbFw8M=nIeAu$G$k&fvcFSn_AR0+dcXrgJ|5UzF^%-PitMmj;0l|F_V-yBaZgQgR6O0Hl!hOVUWIn4Mydhff8mP~u0 zK~K*V99?l9rxRN^x&&TMBj(xg(^gF7J`d(m|BXwT|8IHRhivD*e-_dV;+(G@9rpXFm^ zKLhfI`mfdOht><$QP3)ttU|tZ0rg!~EC&8kDFwO?NP$l(q`)|EM_Wo|_m6ud$AE3z zcD|4~Lbg#S?wPi4p{pK-zNO&RwB56XV&`MoQkSn5iZ8vgRP;HtO0-FsLEm3UEdkIM zokh;bH`_Ivx($7<1zz*ze2?;Mi&snD>}{Th``APH%N`cCH2+o5^1SB?@1t)g(IzoO zu6qwX_x}KE(9v=CE9jdkW*hXc*YpC`Ks$Z65MM_|&~4ny{%_B99y5~xR1GkKily6FdPwDqf3~r1_F!hz@j&! z!qB?(wGAqYC1?yUY|cd=m@IHFC!p~hbH;ey`U z;|<6?#Jux(C^U(KfJ6JxJ2VUX^Y-AYg}$We7P5h~p`>vRuxt+Ip8>$Dq2Q9>1AC|f zzfQE?%?1ijGO*(xg1nC5!?4>5Cbb=rQPd83X)oY8D+~dTk51MzVeI~G%tE7qMOWbK z_#60hFI;H&Gg7FZ8A)sZ2W$!fcN7&4k3Dv4{V_vTVlSD9UHw>OC-p#X(pvCBsmKR- z3@yR0(6sB(6I2KdBX(v@^CL;aXJb51Zcg!fI*lTlslVts+gs86p~b1%NQ~|S$m#>1 z8CXGWxw+I@J5F|@AIC5+hDwdt*%#yx6LQo|_cEw$*%XiI!?_-LY@t_PRjTQN*K3B$ z>ClIa1s{^mtbWr4$7j>1;hR}peK3`p%NJ|b`W1q6`3&aD&!DE+Q@#4}c^>s=?A6-s z(rg@=ru*}V&5W*@xw(SlgBjE%j+JbE!4P%3{B#f^OuWwSm} zHQkO^^{+%o%HdF|X2%O^`-y_#`Yb`$XS$cyuW05O>t2}dFK=;_Bob@I5T@$;$#mr) zNt+zbgnv>*-S^{w@#)lbC7W3G;J@?W+1wO`Orn9~xMk`@+=V7n>whp$%F_kgxjCA( z<7~n4@@#5vSjK$8ow&bULw%jthx-(Azq&luHe)?)Uz0}yb`}tq-wtBkSw>A=%c%L% zK4J}ncIbSrVBsrT{66384O~|w*w^eP`uN>c{|H?}A1|kNZKmk+A9yKz=8AqtmowM+ zMcnWEDUxd#_IajwiRZ?1b|aCCJ)kFlHAXaj7A{%Bqfje7N_Gu~kJh|WGu5xtjOSJf zrj@{=4(q8a1bJ)jO6FT##r%#{a{ng>MZd0L~-xs&S$&1)3lel+~{I_vxwNQ zZWElb>uKBX(BY9aM}9eds;<+|nYx!&%##DF(Dm`*Vq$$4*>=^1ntfrBX5GJsSSI0A z?9lXOTQxQ%PxIX0uBbj(6GfInB!@%k??Clq|nCGg18ao)gvYBc5+j$hk_BjaNO?roiMTJ??5z%1@W}g9%*qpkBQ39z3p7d$Gp9`;te?FwfLOueb|)umRYsS&_NX z0odgazS@Hx4l6M1By>dt^G-X=ME!8bn~vw*!Pk!AaRT;ee(ba0J>RpJA-&Z#=JmBx|@Z z#`~n#T(9QN@k-w(QEBl2nea%7zdc3LZ(b={g*D7~_BvuOnM)ntVjd|CXX>}`7`zVs z@}RlY+-EMe?HbR_2WEJUo921+i`IMf{r6~U6*My0iv+cE7UKf)g>sQu*abaa4(ygB z_}1S}6D=K(cX$y#_J{cSS{=irRnT5<-|jW+E72?;ATzx^_RHPY367a71Y2OX;A}#7 z*WDQS7cg_+--fgzR1|~ZdozuZ4AUmc=8$;3;(~YV#~6ctZ;WdA6kf2^X^Ik=WT8t_ ztW+9f;h!birET#xDK^R^Z5*ZQJaZ*u>MTXSbvf6a-^|tTk9bUT%e?B)DO_GY$mm@< z#Q0)rxSsgp&KCh3^5D+(6+EuK(cDmqJdbx0WYbFUMlYkIeo{2iMWatU0GjVLGmsZM zLlc)TYnIHoXtu`-1?Pk%#A!oc*n?v3F4@U^CLqga60&{5 z^SSST7fS8Foh`N*iO$Au$OQ{6(Nr&bIbx1@o&Q#7_P)3eUo7%k%gU&;YZbANhY!B( z0cPo4&TTI2r~)@q+k>T&U(*t$?eZKvW@0Cwq`DI0RiiqJ%kNLo#38dZwFEP9ND8-1 zfiKP-D_Y?Tw4a_&jqj`yWaRV9U#{`$o3LxWzm7Ro@Kr1JGh6S&g0ti>aeEFkpTBWF zfk))+1Hifc-0$~t=D)6#`HtH~-T!P8+*6SM_-q9aEXkGv`^}fyZ&@UEx|Pe@_g%?s zhP8rq%LOP>wei$1yD zCl!;u&yIw%$GxGco`f7iXnm@?;S9k%l#ctE3x4;vfiu5FNcZlbA36p78q+YRoWQ)& z3G>nw+@m_-yo|frt6}w`VdFOTp(TZH?S9Xij2;PKlbyRmhjCWp}n zKY*uBilVifqG@d!d}e3Rs}YSo+RxyL43VsLE~>j8!RqtES%U}J_f6PKBBQP00K8BIUdqwn@8=t%xfMuoT+z*O0*}mFXj|Q z_CprWKXa(#`x(S_e*(2DVd!-TCrXXj$WBXa*>MYIJQgx=vgTAQeoS)wd|-kQ-JiGH6r(eM(w z)4HM$dJ%fdHl{IaA+qoN#xUcBG-8>ZAy_+4^IGDUG#lC#dd)lcQsW0JRr2x@#)qd7 zTXKqOtBX(!X7D_TNwVI5lw{mGPBh<0lgw+NkGKZDYY8xD0%px;lelw6265G92+j*L z1?TXEg8LxyV*lJF`N$QrUs$Q+-all6U(nFQg7xs{#5v_l;u>{=ILn|#{`N3;Z8^*w50FJP5VOge?V?}A zX71Xvgge|bm?LdEx4$);>VIA75r4{WQL*DRiRiSPiT~#ZrV{hF)48pBJ~w}}kSb+4 zg4DQDQ2JtzYFf^Hr>_;AmyoO9YcH{$fR_8*VdClzJW8)3ey<)Pfh~t}b!{xiZaaLg_nu?QytRT$4_a5STZGk}wW~gxN*=~dyuAdLr-FN^TT8}yB zLS$iZh9LuLuufqJN99X`Os14BWJN0IJ6lVlbwcW4 z`*_}nmX;gA%`MkPc%C0eKiY0|jGTpLb2IXl(vYG33_1Sbrtja5X7@GR!y<8?`VZ!x zAap|x0YCJ26nk(08Y4qEZ@*cpf>!%XZ~^^!qk>?Xwakb11TZst1eSnBn4!n;D$_h@3p{0^)dmn&vp1 zqM7XIuyJML-|T$ue`YZc{2CeUt-0#k$8+o0XfA;Z(HU21=F&3Fy#BCek2@^*CY>aH z{f-H~Z$QrqyrfM%_!NN`J8 z$O=Tp{G=G!P&9(6Tf&K47SH9MW0~|>BvBV($IT~k^Q>v2qF*3#+ayKFjWvkq5Mu@F z1)7BY$*Xhpa$&m3yDZ%xj0Zn-J<+Uw8fBD6$D1T$tc86SYo^b~8O62pCFS~BK{-5= zN`vDSMHi`x{UcPmI7Z>8k*{_rQIeGeQTD=np9~y|Na5B$uoM3-liJE;#L^6Nm~RtcSEzg5xg;*$sM|B#JCvUWLHM7iSXJr5S>23i`;p=MeYr`(%IL14=;uVkMw_ zo9w){3Z0AdW#?>g#+OrdzJ~F#b2a|I{egUZ_&V%02LxM(LxSb}5y5iis9=jeK^)h< zq%MAvy7rx5?k>l;`_B{1dFhm3|MCmsRNDrQN#K zEb!7!5^xbd++7EV{bsq~v{VWKcMg-lPF2LGb2)Og_c6yiMZ^&Sp6J&?YAP;bhQYg; zF>4pI96}!B-kpM_0Q1~c+}{ha^Gn&w{il^mf!PPS^H`N+?gzixfq3$>P| z$o)8%=Xo)2h4wUOy7uHhVeB!1C%qJ!UUVbggP!>=@`moA$Mr6_<~#Z@r6vg{6(29h z*G{2dl(^DR_JppI^Z}EdhMdj0o{Q_~QDnphu(|W z?}A*S!J*RaeaPYd5TE-%ThNSq*s*9{n+2)!_Uk0nz~K&S{9-ganKyjx zS1{kXQ$_mf7_FSPmfA!TfZH=C^|Zw zOEmT5G@`p6FG}VSjEqBn^Zp!UVHJ4|T}uUP&@RC~U_Q0Zj}VO`qGjF1D5j2#qRM9E z)h>_cN>M!5-Ak5r53x@lKAjnUUrpq|g`$|9Y}9h5aDI6`CFi#g>Dgu`zP6le19P}1 z8d;1#jW$Xj#~bOeSR?%)*32#@nWW8$Cb?~lLAf#{IST$ju^|tw-aUE@if_TIfI*~ z=1^TO?zOLEQ+w%b>KZbGxMw3*QCPqnQKgFi_6jB7z&<%33tFBdvlNHz|B-Z^aZy}* zd(Jsiw)aM}BqlM&MAH-FP3*=lc8VPlMG!$n#Da7M1?d7JRZv7l1r@tVG>PfunqF0G zG!ZL1^FG}7!}$@GVO>YQ%iG5}+k+F-z7%@d6HUD1l_uV`;52jIK>qZ{rjSF}sLuaRv4Iu9|pXt0C6I;Hxd#&n%-c zA3O*TrNbq}^hOyoeo@Jcz2JlWRvs}mgG2Hmb{Wg}DCXp1&goxFde!fy-pA4V8H#xQ zzvaB8qa0eEGMRQ2D^JJ3gXq7|njP4IUD-zE(V;o4bL9kff6Y{(^<(5dcjCU^2^{(d zbK^n4pUgkvb9UaBDJ&W@S_=~aFf$fDSz6*6vG<;bhlasE3k}(UCZ{ z`|;pu?x!QC$&YtIn~Kb?mN3k!GoXLd!S`eye63@Fqwu1y0vL}vi2qD>Us1Pm8qE7 zVE!;6fjREQ34KOwV}15Qj}!#1bjxC7i*4qN(a>*?tKzMsiF*8Uo_H3Xr5?@(<_JUX zLSOjY3@wrk<9BfC>~v1WHY)18W!-#MIFZ>VPB@mMXeL)F`bqmZBbOoT)C&b&AvnY_ z{(?FU-f1KKdEpZFQbR+Tp*}<~JPr{|HJGbZCt_}ej6Gqomc=eo@rSp-Pjxrp-!A3E z3%eCMsen^LQy6(OP7wGgi7r^6qcMI4Hg1t#nm13c`om8rJqXZ9Q47`5`jvuUU&rL= zH9V(T%uDbU7skUY@Xei&S zGm?2tjiL4^WRk4P2KQmR+iPE5x4l=fY-l?qoAuS**1oy0$3dR!q)i+jlEks;>xe#b zHMFtonW@iKqUr#5;SsVsLN+juv8$PLeVAaGwni|`uT}Y6IH>Z`*NDBALoe~s2GJ{a zk=A($UbXd+q7!@yACJ{S?~Yi$cg<$%eQFElPuWy6u$pN~8i>(wlC!>uo@~fTXe^op z=Zw?5$Fn%KCj{rC2EpnJFSOT@|N7ZUW^FmiTfaQP+Y<44!69b+PYrMW6PZ9=yQx)M zsc0L))p!+oXuYzDV+;5lmsSa`^C2paaZ5EGDJ#`px=3~3;0W#Whaz?TN3K)97`aL6 z$8G#yp0wxWG@(~Q7WEvQPmI-jh(5WRn!bb|KnL=Ku9p+@XYi=~vYMLD<#U!b+o*MP z8oaYJ72DlB&aTO#KEG|}U-&9Tcy4Qg;99ps(0XR_#xJv}-YbW24Y}MS&t3B4?~2?{ zKFN1K4M}w?Q9<;vV-kC?0{ub5O!2lE_th=n!ZmPzs-dM1fgbDwU{NBlLycL@X>j%q z0(+L=yo37@{ORkxfg3%2)Zm`k5I`m`7EZ~lV*YyrL&hwFRe`QdYYs{`{X3Xgej)IHz7FSP34=i6_LVE4WQ zck#eTVYf!;Z3#`vmy;@BR}ZGt^$jG8qeMPYrNc* zGtT{RE9#+_$IB0O@N{jO#I@G}!`ji;?S;>4>*Vf71E$IkBfwpH7n=Gr;4{tyPpB{O z(Sf;D8ZfC7*t`{Z+T-DE2VRcyvy6wJBtwN;PbWBFOQa zcpEj4DN0d|-olATc6ICji#^uM1&TI27G4Z-oHZ>+HXX;@V?+*T*%QzAN>1eayc!SB z;#GpV0A6OVu3|#J4TAcsG~P64KW~|SmfGXbG5agX`0NjVtd$3d?&o4fU!1|IH|5Dv zSfN`rd&d*;TB4iL^$K&R%BoYS`#-N!3iVRsbHl8zGj$}^ja?v(5R5Z_7E}FlH z=541|Q?KB4it7+KT44o>by|+%OiLrWgjMj(kK=^bixuHB=)hP4vbvHub0)C=aIRw5 zyoGqm(Y%)}Otg;(g1rIWH{h$<%Xv`j{q}yL@9i?yyEu#X`ffAtgDh-&TQqa3)-&&) z)(PGlqC^iRLa=Yg?@(UHbYG?Nx`<+??S(og{0w;TXPG_kEbp9qns>f(TyX9`Dmvdi zEZ8F94eN81THB68JAr!1>oji!dy&tF+Ir3)eq)1$Y!2p?U6`m1>`$RcbHC8g;J+ z>(t&mH;FFGR_0s>ZuPBVVtKt>(f?;Jr~d`}KH!ky{x0l0z!iBFdG~&)1pFpy|7yF? z>)J-mb1$yeKI@E7d+OGUmgX&@{mK@>@!up~T)3Tk+?6dqa=`=SKZQ@9hGllkZ%4T0 zLsQA)-{23ZLGJuI+*gZ%50$gjH~fGr!Tcsw@04=e&VyLk(+58P@?UoLgbxXC`-D=Y&w-_4Z zmL6xOe=32|m-4W`*`r9$mMFs93Pl`KA#2ihE7~1tR8t=zsZP$(NxFG@ zwQasu^Ys#~sy{x$mq-P#0d?yJDkUcqX#@JOBha3WStv*~O9XY_Xku9l4eryqO#7oB zryB_Wk`K3X?CW%y56_lG4`|I+#xldWDBh%v;>{F()vt%BOtDK;=5^o^UI?S+;uz}4 z2o-FXp!wZ^zVzmLMOs~;NWM9o_Lt4X(hr$z38ilR%Vo0uK@v4ptP-7MsbndGHvcbZ zi%(UHegCNy`!wtqd;bgV)|N8na^w^H-W2q3=$nILnAhDH!FyJO=xPs^%p;-8<>AHO zXN6>aX^UjNS0U+Uoq`YlMds*rfw{6y3D$|nM9YqYqN}b-bbW#TMqACBmm;(0-f`aX z+bP~24y=kg!@M4!Vcs35c#q$jsN+-vXKg&fnLXeuJE4y05{fXVPGR~(8>#n8n~3*8 zTv1Q1;M9MXD%!`pIUQfj>89s#nh&;99k`B`8}MV?zESPDbDhfe<~q^( z!#b7FZ@q?lXOm>iNx+P1hr+2cx}Ut1)&1mD-cz?Z^NF%|b+__#y7D9v+&>B)aSPt# zKLl>9f$u%CS#MkcrqrYV0_XYGQDDer%#a4-K7==Qa}4gk2f(6-xb9Qn2|Y*QDe0^gFfq zzE^=meZc|zW~|UU?0vrFmG|Lsh#Apbcn)10hWRi2ss9;Ix~ibrr~p5x0W+S#z@cd5 z{;;X;N3Q~lihx6Xr_#H1Q|O&DlexAPljQsTC&~~0p1^fpMtwA75_fMmzK@}Q`*S>L zUoZ(79C)k&_0u=tWv{`{gZKQ~FX5veIP8lP2mk6uyv7OO7S+RFAJ^P^3)uksfjysK zS9TG2R0*v|3wT3kfkUstr~d+Yj5YA1F2T-z4sb|;XLdDqcF?cg`*04|RTrpq?Vcy! zf4ois@12^zs*^0S$lRNgE32D}WX!SVTWm1tCDG2>*+gdUwC_QMB> zCk3@OTDHh-_~J0}dy zk=4g4WU0AIR=v1a(f$P9;;t%AC4=rP%hjq&^EJXRi?n>^B8|ERe5C2~ zR2uDENmCUn=stnof0ZB4+QS(6dj*q9kge4PKX?CF%xV`a`sPTY@{A@lHy*hp>9Tk$ zNfwW;C940dVNy;k(-C0KbI9?Ch7Wcl_=!&z3FZahnpeyaEzr%X-a{tS;LS4cn=1?Z zGcl)MPb_`55OZ>YY{{umw9`|0-YZ-(ON&+JEO6I{tmjQ%RET}OYNXyj!u#jGw-+0 zQ0GI;|9&~i*$-jv-FA$aE*~R?6}3#YzL=`LGl*^3cFsN&8s>;yL>H6A9C694&nwYF zUti$QU#nEl)<>$I{U=J@w{MiD?}1g?e*UPHo>`^o|JO$GnP=0f?ehXoGh(-*+PX*9 z{8Az7PFHdI&r3ON;%@8%Dwt(aDRb0k@}{eqRQp{Hr>}>v%O`2l)0zxk`e-Moznde| zMH%iVlQP{;u4H$+zt4KA+>Msqc{8|&@4&11Hn=h^;8jlp79GR64?OY(Zj1vCMFCgl z;q!esgMl0Wg0uJ&4t!&qZ{hQ1V8|@s5IEVlG4r|AQzKo%+!lOH@r2E89<&&W}|yA$d_4spgE21(orSDvU`{{akb+(PWt{2Y zEBa35J9>8YYk^CTz#DS;lFt2eNLRpo<-xdx^8MX09QA{?@Xu4kI_zk-r5e7>)6u8r zBfqp}5&DgIw(cdKUWHdD|006iD@cL>ugcm4WE2u9S$bicENzV;;{8}c-HB9FvQ2T`*+5K|cqR;9 z#|%HMCHC)wdHdm&T93wcD*NSCy!NjhGQC;qW^VMM-z0FRQsk@pY*k#pW^w94@w~7N zz1@>}lI1FLA|p0a^WQ~c-*GikFD?4Bk^2PiRAi;T2p@ao&U^7&1n;I;-lK9O^OT_f zSP-Jp)1{LB(-q9LD+rm5OC(E4B4`Pff>D51{pW`zyQ)Gore+J)dFg^Da^oGtw+fDn z@uF)_CU5@+_x$(pObj|GSelPA=L_&>Y=tiV@nK@mJ`lLcc$|A+YI?}W}dryd!8&myPkCSnL!>TK#MgW z*%epd&Dhg7%?FM=gP!a%{P25nq5Xjqz2W~lb*APTLr+zXGZHv-9k{d**zp=LXx$v? z76Ar*hFU5Bvn6;AU7rs>p=!*YpTiDqBCzMr9xTH1VOHI&2EM(7y6Q6OpXoSX;(0vq zJfl%}JdYDPQ@HavG+xQRtTk%_ZU1^C?Fa#erdHtWzwq!F0DMZv+(?A3^%C@`ahM~WK-NZYcsQ1# zKAM7Ft^atXYa(>}Dey%57{8Ns$P@|%hv-}AI*`@TZty2yEBgv_8ndyS-J>9D;AhnE8^{1Mk5t z;2!nfPE9NJ@y6lsD&AE>L>=}kmutIw_Vb#8or-igQPvIGpg3|-YlR^*Wcb!62!50; zBcKJHyI!`gh*Qitdt~)9<(x_ne?Cuebz*~O8<1xa$JrmpbGq}p+)^j>ChzW246Q|+E)ajGFBB2Mw}|6AvN`P26!FduLauLT z-Oq31$(1->RUN3&q|H!k*3VSwp`X=EL=N@ysT?`HQxQJNm!(<7vT8{t(f*dE=pvFi zwYX8yG_Fzfo8iHP{kD|8g9yK8DZ;*d#n4!+*sAi;XW<_CGndn@%}~tGY~Z~862(3~ z@siiR7|uL1*UjIoa0}n=ajU~p6w}8s#AD}r&U4cys(Uq->TidNR%3u@yS|K>m&f8h z%oBR=sS$ma)QLVB`$W&f6@s%DGK740@IKq3RUWHjc*kpT#1)BJtbUnfY(`)BL8xe? z=mTv_Mg7llyeV)GGyJz;gO9|h0X@B+cMtdN;qMVxIw1+fLg!}=C_wEs3yXZuNNuWY22N$}KteLvMbsz4q< z88OU)Kdh^S*dpQO@pT6A9Gt}WeLq&+XUuACulzN-J}iiLACV)R-Mychq-tv1Udzmv@ZUe+x`P#N z^_e{~KQ&i*`d}yb&>u6R;39YTl-+JcNmP{Ue#+y=J)C3QtItdqZy`7O#vSxdji_55 z12+a?*M1ST$|CR;pT~(rPn8agIsmNl#x>!<5L|crLtM89x{c>CFk{KaJbN4ZoI#jFMPpBO9v&Re zL8}osiry<2#ya|dNBt={jBAJUZ7aZQTn-Pk?@z_^aLZfnVzzla%{!;Cp|>`5yD4Dx3yj+8@B7pvnB* z$G-e+1+QiE4F1j~=*t=~uLq~|RszmQ%*-xA?^*=xnKPHPdCgba7YCA-)tC`|i=3db zz@cv@2p#`or*{~7*6hjBodeL4A&czZL}YWU_T@W{%%EMb%#z*h!Q}CQFzz8V??knm zi0a12hH0ldEZ<(}cJ$gK*apOic6B%}eX^4i|E-0N{*+?>34dFpjv3$HO{~p@)EZI3 z8SWzQJs{t0Y>(xPswhtHlPar!#N7E1^aq|HqVYN8CU@h$xsB|Fnb2ddhmJ8Shf3>9 z+^T}Z-3GQ#)^}wpssUTvn&w2=HX#I>wHQu1yt!L*Iz%zwLT+_uf@1t6Th@&&aH}ft z9`{o+xGXDpqdSaB5xDja_6>{>cQQ&g7w0M3S!J>m zctF;}kJv`E?WLEyWUV?2>IS#L0$Vv)sZ9h27$lTv@Ya zvur)Knt1%Sih9ghO)Vd+Vb=0+!P$GU%C5$odI7lCd-C{R9?)&I9u#`b1P<|gc*lS& z=B(W;dM;U|vIWL4`#%Z9Hh(j*BCo?Ng^8AxYnfSC%hXpQdEM*Lg5|YL!8EN@w7ym# zxbnB~y_(hv9t_#@0ad(lXqjNSvxhlj%c$ql5_m3bryheg2|kausCwT|mU_LKAb5S6 zN?r2{Ioq2Dnf*5Uv?FJjbNMOe%sELNe>G6E`vBA3DJ7a)MMP&rO+3Dwm`;}v{g23u zJ-w4U-%1rcy5m%i=b|;Pqp|AV@MQ7qo6StabFiOJBpzQSDYjp8yN&kUZhZqh{JrX^ zb;u#!tF(sq@~K5$Z5_3=>|@3}a0>9ZY5NCSq@DYS!CoP&S+PPjIo#v1*~;&B2eizp$52)S?!5w!tI`{)_cI1U$Dmt$Nqk$zj%8Z@TCpdauawGihgSd zdZ`*<5wsjPQh_zk14Ht`xfXDp7k2hNp4GE)Bzz6^PV;i$SuX0LNx&gDzIO)j$At4K z`nqAbrpJF4`q$>)fIn{3LIMAeLwE9^tw;eD6+m~ocr<)ip><6jPTS9+HcAH$mB6RE zW;APmFpl2$o2Wdz2M*&m(5dp%Ny|&<&$dq{oji1|mr>t{@DZAiy;&IisHZ}c4}aFX zKciO*z&r@Ep-$98U8V5X-v=I&H~d0M;7Rld{?W+dx(jcwyGLg5w^}e4dIhg98S}D! zc#Vnh@^1iVaZ(^@eQTc5(J+_mIFA{%CIDWtm=CS($&E%W^e6b#ov4QvPLgit1CO3X zJ=6~0Gidu;27&81CV)JGuf4m^CZ&5!o-AyuL|zQ?Fw;JgEqPZsOMVqI{+>d0_7sj? z+by%Pb+Y~i%p$)#Ld{u6sp+e|#M)UwtV1i2cLijbt@>ri*IUY4zQ?}#nN-$iKoaZyRuXT7Z?uMowlwCDY!Sgr@{N@ZnX$5cFzTNd zLy_aNnloMur24%1Os7v(^b^bA&%fI(>?@arNX&6}#1mcnMwz~nD9gya65rZHH1Vs5 zHY$YI>{~4AQ^KiD3=tix7ONZwgGJ-bWxQ@#GSU57EbE7ryLDqLWt~^0VhF5|@g6O! z`4RO1s$EhBczrk#hnb;;*s>)g}C>(W{3THCD2QhKJs{PSdXHB;97yh*mavYK-|8%1n=P+N*on1MwIp6A0w z#|`WTXRoHVb$P5;U5(JIxK2Qh81KC#ADpff-u_2CZ@syRIR?a0k6>^s&Zbi1Xw*K# z@%S_8)a;u@^gGg+AthF@?@JJ^6}f`ts|>;8%e7*!pRrrpmC2jG-YZ&cRg%+GDR@Qf z6}-+DGiO)=?~xxNdYoQ^48zSrucz_6k6{b-*`GmN-$7T=_bhexKFd3%okorbu;^pV zj@Bb5sMlVqUA%{=gUgvdp%z{z2dK7gFVj?(QX_KjP4DIinzZeL?!6Sz`FsX%zlis$ z@$hQAmQG#$Q#e<5s$vgAHtnh$YRD?$&FTu3tGr0?=@0+Ow{}sJZyC|whjwn@Vd8xE zD0MtNKuzIgiu%hUg}QesPbU=14__{oAG}i5{di6B)9$AUiW@p8cjkwr>%v5?{W$LX z=P(=o3mDM>T=*Tm)=nHVa#mA;E1$yG{tz@~VZfHZ!Ry1E`ubYn$ZX({t7mtQ8mJlg zh3t*%YFz&fo&$Whn@u=99z!C&Zou^~0e`NbE*c2@nS#&9;H2T)LtRvf`@F0NhwwVK zVNPsAKJ0v)(c}1*OW;#~g!#}IaEP|yV-|d>*MUbg2srdAyjbDGdhbQ-xKz{VUG!$H zCz0{|8uVv>A%i~1m$psE?(L#4e`hVY0BQJH;h_i*AkuafICTj<*>QLg`C?xE6t&QI zQ~A3ykRvo?l5m?xzUzNx@!%T>H?QFJ^#z~cJYJ*TkKLIIZAYI#(zY#tw2$#8?MuNU zItgs5n#s2g2lkY}YiJ2H{^KyCj>EpJ7#@s|u}3>Nlke!9A+!ejA@gxL_t=&obML2i z^QAf6(%DK*6<*1-u?I!tFnE{87RmZM8PEb0%6wu~xA@?IYci5Gt>Q0L=C@J$o6RXHBMR1>Gv;Sx@+)U92vxG>yQQZi$BwT7|1k3H*tn{ zuy>cLWZ^`$B7R;eYu4T}>rmgP@%hZYE$ zMrh;DfDgXA@v*M<9H)yvO)V?Viq^IUVz0@ko)aQO$CCibIB_Yj8<0d*3Ve&+PUqC` zW)L=^P+?1;_jRYbjSphow#Zn;6dX;AbJkM(;c(IAyAu7=QVp`;1RE`3KBfBw@BX!d z_g^)<>wWZ4pKoQBe%bhcmB+bs8KloE+llAH9mM+5c4~c?MV(RTu`r`?z^w%!4wcMeluUMTeXz7=No0?W=24UP28~t}7E4z?dz)Q5gSx8-mEb3L2O1(Pbne*Ca z!Qx65Eho|?ucid`Gh5fIyq6~O)*lNw!*tAzo;j-6moyTGSclA$a;Exox57M-UG`>~ z`^ozi@}so!r;irye$;&{{;ASEjXqsFhIU3`W_uC$@S>Ug-8jsMXJA${4;X}+=4L%G zLIH+M0+#gX#ccQ(ipP2|B>AiF2T3_X3s2THcm%R zU4)rZj~{jz?rCtAZuZQFih*sLP-jI0&-_qp48&YG4tx7IQG+0R@YajiOCgK(Zr5MjpgoFOrdQa z=&?RP&G$TdtheBE2CQo9#vD5X`!V!m_oLCDE=OJ12Qwx+=Hu|Nzb#DSZ&zYoJ`Jzu zJ76adZ-Qf(qj})<&zr+;`v#J`QU0X02^upOPBe68QJ7VypdMNb%vn56byq+Q^d5Be zv*8CDgp4w28sG8a4Aew3g|=DXFMb}WxJRM~=$qNC-kv0@zDy(ZuSCkmr&4wK4o>?d z-3<>{w`Ml@`-3q18MTko=hYI!9Ax5o<30V8M$Y*Ze{)5R#J>MHv8}DAuICRh=hykf zJa#M9zO@SPy>msQVK%i|7xR{%Lsh1Yp&FAjTm=q`=pilVO^wJBOo-#H6QPsJfZtg% ze0rA5BGTyDM0#gAZ?HmR{`E4ZTLUiAkTs%tY(A%+T_daBI|we(A-5sAMm8iAJk<;- zkcFHAweqHq(|3u~b~S}+?xs*7WII(yW>DSsY-&8VlR2Mc6H(gD5viEywFjB$FZ2c7>54^; zA=b|qO2(8$qV@GfyeVfnd<8<8ISPH*5oq1MjwOc8@DK?(^;FAF5@}qMAl^8}Yjw5E zwsRZbd(dKyt8Bj7v;}=^5qx+3(Wh-orRE4QAC%$2y^bdUSV!b17}a(FVvZ&WzkbKQX- z()(t38Nw^-?p)lXuYg+(-Nmh$z##NrH+}_n1YjN#2%YOj;LumVpMOxx^mtC&aJ=xj z1DLZK7*q)@eH(D-S7=ph@K_?Q5B^_GBsOmc4$T5CS)n)c#vUyKcy|H+PEXC$qgk7W zvlLh~0r->%KGPTQ+3-d$^gA9O=!;zF5mM_2WS1pkcYhPRvbf=V>)8=PD-XW)Q1oBd zF@tsjTV6n&EAfUfn{(Exhq6maMa zG-elZ9?z!j@M>&XivzrA+lrliF>uHTA2ei-ba(@Y?)VCAUm@Q$f0ocXDUf?`JlxH# z&+1lhKu%%cMxtK3lquIDIi+j0Oq(*?;>6r;T|$9dKekZT{;^BZHI@_YX9qal5B0LS zUxVWCKgqe?Zz8ToC#iGhN$T=AMLnBOGmrVmjkpco>vm|Twnhrh&jNT;a}YDMgWFiR zPGx;%m1uo?m067Ak`1a7x9<5;S>w4!RxK#)mfkGw)(Fr}fO~75 z8AqLaBbc>np=3#1q%y?LQ)%-SXfz4nD#~FholhLIycQ|ip1>D+OdOR8!1?*TkkgFa z+mrjwJ5`4`kSWAWol(^IX#&xIxPiEeB1GGaM#1KJQgFO? zK=6dGhDU7yvqnJ|`c)mX+Zu^?NGnUoxeVW?egPvr|32IAkAeNi;)bvFiHN{|$ajTkXUqk-S18C<5?PZoI%(n;T!pAzE zcYPkqTf1XL{mIp8YjCL6D=JjeXL+Q`XYU5V!y|>6ty$D`tB~nF$|F+jPMOu`bSu60 zJf$rqZf6#tKy5K45J~4%MFNWvE1yHyW@dZ|M3@Z?!wNh zc?{+)=*?E39(sU%*`*J}mLHHKbbbtcGvMFxJ?f#`z63LA+WI~EuuqVgF%kYm|DYxs zj~R6cFy%$mK^NeeI30N$i{K}H0ksc&(vac9TK`0rP$A|@_ok4xQ&U;XLiBL(LAxD} zo!Kt%rF`+b;Gw;*>&bjWUuMMqekQaWH_?-2`;iVmKiW>_0L#Fw_JIcGjcGy)51!B( za28QhwH<&Lb;@+1b>K9y1zfDVAHZ|+6n13;=F21^0r?pvvT;Bv)y)s!>C8Z?RK;}D z)WUAzd|9_P4*a>jyA)$V8Z{Ib5Z!`W=-`o4p4A|`4mEMkk~7E_J58M_z@eYdP>;nI zsK=(W#N+e{&e;KP(N9yT>&HlHO<5*gzxDV^%49i#f#!-__QS?{JK-t;CihnA-hOIv|rq5BkLRlRIH zwVyK;7rU)%cFX!0T(`F7sb+A+6T{2p-DaP?iuqWcVzML>!!`60zl905^+BS3`2w|S z#sZx>eW}J+6eZeUU7@o51b@Z1W00|ie(NW2q~aQ>b@FBCxK44ldFN%v;8WB&>6~Ei zeFnSi$HGwF5Dnc|V-mA{vXR&;qN%MUlvIm^Us;`(=+@XXj9 zqIJYNYAIVK*v3RN$2Te1ffaD}RAiZqsg(_WN8Q$=MT+eQ%yJ$k6KiA%_C4Uqlou$# zbj}ioIrC>5iGCXR0EY`?@w;@z&pL{mfa7 zY_jw^!8IQmb>ts-hE{OSsqpnF$cFECx+v)~L;<-L+MGR{_TOr%UVvO_c^AeH;4Pjo7W3@%zp^ z%1pl=Va9of;gOCkvn4gWE~$nYm&1daK-clk9%|W_OKkfyiSf&HD%B?l>fE)W>sW-^ z=dBQ(hkJ?M_IZfbGHo^Xf}2JDN-`xwQe^&AMmKw-s9W^OS0IpM4`Kr~Es4{mMK((ymZ3Ox;8tH4 z!{6(G*7a@d#*U&MI@IIO3OqUh96Bs2sJ>WMAP{60~GF@U4Ci=bQh}N@~7{*VK+(eJJ*2vjb_{ zOTeKO0i+|_kG4h5;qQKppKTL(jsF6NEaNekhOR8hS7>bl4y~IZwn%fJQ3>bn&sg}h z^W1dzljmk~%CTUo@l2w|O$og2kGT^2dI~Qq8)V^fVYl={dAIgviLB2l;0*K9&||>6 zl~r=O@+$l-Rw=IXL!9-=QO-8_9Ot-pia2+{JK}?L)G1yd9w$&EA(Pl%oX0sgqv!J2 zAzKD!P}}#p)De}+dmv-k(Y2P@-V0%tsR6`XZEgujyvWy3@lcR5nHL{>_%q4p2|6n?UF5Jd*J1RyxGtU#q}Uvu}0)6>coBBnqR7) zfFIOt?z2y}SgT~?N$4=YOrX~9q0K@*w)H%G=%=hunXX{Y^6mgo zX1|q6?KhwSyR(s5_lNRkC0MYxuadl%u2lEEvs!fh1|D?Oa>2MEh0|f5Y|5`w%+6Z) znARzlwg%a%t5!@OWOKIVX~dpUq?p{5(3BU;)-%Y>X@!QN2>u0$IkIHf;}%cn%i5jD zL|c838LJOq*S?$gdJKKT(sbt9luvBoWz-&AC)fwoi1yiq%$2u`cwXL399Hb1nzss? ztQ;ovDI?Nmc#+tuh@opQ(a#2_sv5ZD1CNcLD+Jq|y^_5E*&8qKQ@NJah>l9=OySw0 z|IbEZO$icgIYFXb6UFS=G1Q8=prs~5G2NX{QEx8vTE ziR3{l>Y>Nj&v#;HwiP%;r_&bXl((!!ADW9Z7LUDwx@a)8^#kzzD%3(FrV6(jfI${; zsA7RXGccED(A$528txCwerBTfSq`23Du3EuKbN%63WSF~=0Xpk6K=!LI~N{q;nVn5 z=Qy!-46x`GXo*(xH>c2j;dteu|+xiOx5Z(=Wrmdq@$dR-o^x&fuJ%iiXqOf8Is`Fd=MIrCb#)q9cTUtd)}ZF-ZUs5u?s7$k&RNVY;LY0M$ZDy@-DJx-VYP( ze@6)BF{^p!cWZfOo6)Oy+H znV0WJypQ#);LNBMjjM`?_1R3$>5KXA{6yYCJ3=W~YmZSbnh#azFbH=W23U30fUTc647^~Kb31+!kS62UUL zRItw2#heogIgfkb?KEfb#&FaT-|iy9?gK>o?LlfLTqu!rkso2`WN74pFG8^vrfS`_B3$>HW5b^ zW=n10AVJS&>({`n$XB!bHllt!Ms0V&nfw?&ZBH=!$*U$B=mpim*_>f%IyJtS#?;tB zs`C>BO-=@(&*!`4g?nUW`yN>+%i+YlSe`8jR6n{pS=GUhlsaaN6z&fmL+($TD0iO2 zEQJFOr2&V^fkV)`-+Ugu)p1})JA4*@19veUSksf)Ap$491V(`qeX9%y_|n`8T)G6j zx{B``2FyWU){Oj#n^VwN4a9vp29I6CbM?dhT#X(l8>c7l6uvbhHk!Xj#8Ylp-VBO<2AG;es+8U8hwi!I? z2!G+uRp>x|1r8mbM(>4;XPxahzhgEOgx*Xun|A!Py!+vT9Z%iYc08r2;SY5I(cQ+T zwY>ffe~s|b4CMUz3F<`=qWLB`Kwl+snl|tRKTV>VQQN4dUpmz;*h!?t*cH8$%~|R; zQqMcf&{J(uw4dh4#!m~WsdK-e8nB;e9rd`!fkR8qP|qdDm}g}f^B%s7c*teg(UhY; z-%UN2?_j+aZDg)@f}vduAjTPiME~b}PB(D@r%Rp1N#|#As>V4~V_m@+n)75`=mEtT zc7!v%T~3XqnK)^jE^3FY&D^P|`eCNN7XE)en~`6H*$!Xm)(yxYx^(E)E)=`<<-nmu z#}vy;wZ!-vaE!t)`&;mE=B=h4N2936Mf5wt$Zwnn{~}~Gu;&-5#i`+<{=Gz|FM*!P za*8u0o~4fA7m3Hyv(&rlBKU#l1lQaK(Xz9GH{AuV@6Rk^t4(C~1L(H~Vh;%4T1#gD zGn58Ugy!!COf7{3(|>H>OLL&+m!)$yBOlk$y6NhZ*}xS zHc(;r;L-G2pR}(94ydVAkwLW|~w_ z48Nmq{j5+iyn>m*ba*;VDORL6i#hdJ@H&dBy2+9K-E8cBw|Kmm6F*ufuz{1*kJ3h~ zJDNs{_fC)C?=Kuh?q2|>_zd<$58>Fse24Fa@Ad!c zEN~oqH2Tfk@Oj=0p=VAFUZiw=JnAHPu{Dpt{eKmDiU9aje*@k7In*}k!-S4+;Dxps z_%jmP`?^u|e#D3Dz8zj@vB+~S!kjq*{Z}FQ(_!%P_!=`KC%p8BqelxuFZMNZ$bhvi zJsv{qQSTfFXF3jCNjpwH@Mk_|LSvDs_}CZ!HgKpMwa_DA?jm5~fAKnpqGt?(p9j38 zI~{)Ho-P31kJ#H+0)r;tRKbsEI=qJ#L8Fg+*G_nk-0z$~A55J_?>oS;)&!D{zVqe# zx0iQ6nic7mH4%z*Ayqbfg#U+io2itwLRS8aaO+;!L=BTS!XGP+GX=yGgOW@&m8ncO zCW$u$#R>X~wY<%56Yo{BLh>;B^Lkql(`(@a`bDJ5){v}{E}(w7k;805j_@887m-B+ zzRc4a!Sh4`^UNqDwmN8@W@jv za?aEU(P~EiPF^B1L7?Fn*(6&XXE=LGtzZ}nFNm6r#1?}*uS#&5K0-c5^$yPb6|%@W zktz0Gprk5Yq}D}63TDh|bYakV|8$JHh5}b7oui&7(3ib=j`yg?T*g=<=v($s>%AOe zxswDx4e*tAhB4c}^H3+uf)*~2SiV^ZUPCZ6;o-bJ1v`Fq1n)=+QW+1f5REzD7F@$@ z{DZX;KYgvBD@7*BuQ{Bl0C@RX8nbUpgZEnsGa(Dd@HVs&-gTUDNGbf(knL{FfrsKQ zcrtF+(N~gnVrMLGzq}2*EMzSotKywsK-}f1pe7N3S-Ycl^AR*`kp#Cln}#H4U=y2%dXqK2d)ZPjxep|8#sK zXM4N_HERN~_#$ufIn2#x0gD#y=gs;fyyf;8=E%X{%RyjH5xhlekMq{CsEe}C5RXS^ zn5*Fo?_7*)R-liYe1clvuV)q!^QsGbIMd+0)L_6q_b=>Gr2U-!_k*&wvCb_ya3lE5iFpYLh#eLra4sj)L zI|(di1QTfF*C^ztP{_ zyoj$=<2ic#se|#oJ-hzNcs_VO-sEtS@wo#!Eo8o&;orMl|7V;~n1akLh`a_>Pm%BeUQYN|ZFpoPXns6QNJvneJaT-545A6x4&QXV*O$kL4o#@WK0HY4dVYc0@pB+FWByFPaJFEK4^SDG z2CMZEA!@@HYei>s5%GNeICD%o#T-G0MCX`XP&m-Lgv3*a-+Iw?IY?{S79gs-;AQ6T zPo!heu*C#Z%c!M_{oA=u%}-{_y72kbls#K9UI&#RtaDCnc0J#AVcTEgJ(^VJS!-C9Xqz2KRA z<0x-G0&k2#XPC!R?0hEd7j<_knI$}rTER)T++7DQXE3$C=MQcDOk&Pmh<+-FTE~ZS z9`Z`+d=g7N1L3DwzJ@odR`Ir)6sr9rlOxB|S@-eH$gkd^xK0$g4Y_5U#*|IWr?Kx_ zhneh4+oP(I=;@~Jmysp7fsX1qj#v-?`hx)=5r3QhJtg*duL^_ zKJi;wpTBdN6TYfF931a2c2HL~coOxof~q2y@^cOl&1*-fY2;CALyoKc5i}TUz{9}RE)Z#@QH=a%LxCr+3TqD05T48;De&ysu-r{DY{X+Wy7EFP&=yS{!a!zpOp&y=clDB6xP}>H~!~)BhB@bTO zvyPz-Yvk;~jhyYbW5n8oOzN|T;T2M+XqHwg>cB!p^Fl07f1HY#r3sog(?nHU$!PIj z>nPe4f}PU+soaBUQ@F0fzTCs}Gr0Q^$n9~Vr@H4a-2MbJk9c4SyuX_J1BVviWC4?q z9ev{neDV*@5$^njYoV|IpKk~F(#WH zm7-=lHC4QM9ktbM;E>Uuw%L%|J$0IJcm717<@h9_^M$EGd(AAqC3r6B_yYJ-K9AfV z13lTN;0_JTCYte0ie<@Jwxu75IR%hY7q*}hrvT$V^oBC>o&U_T`yv&R+3 zJDVcZ&Y_Dmj+jMi%O}`_-vxi>?>S7f!k6M{ust#cq4Q*|NSrc4>QO zar#R$WZk#;`vxz>Y(FJCt~V)8|D(u6u2bybFU<1*Y?o#+Sn!2q6&ng}}jxKphs*&?~ zO>!_bEtt=no-7qi55fe~a^z)K?VyhFS;YRrZeo79fwxYe^>P7qO)ey!0lA##ke%QbB=W}h zH;Cf-Oh(|XC^?Q$<6!s;H8c|E3GjRFS5RH!4r-W`Dm(XPboaTsoAarIUSv1kPyB!l z8=H8maEe+toIsXJncGqmE4!Ws@@C&9is^KGw`JWPML+Pk0v~L}{CT}%j)ZSwaxCu} z6|A!6z$0L3l49(ST&SulS)Ymi4jE^rzsrcuRnH8Qz%AMb4BB;++3xHYY&WX~>%gPD z^(wp}7XXKTIwLsyU|#h85oTMwPq30w?Em507>&Bgh~3B!C!ixa&f91sZ`lhyN!}r1 z7+6l!Zg5lmV?_SL>8kr-GgU47XQ)~}9V^|d8Nqk%A4|H5rjo8tfJHy}DvvGGxCcL? z2KfN_TyJ2%{x&d#qL%Rl-|z#RezU~uKg^V_fm?J9SuwZTk;@H6ZISGpW?AYf6;D9ogPg|2KC_G90GOLPdnDKcL3S9m-N^P;Dy zks5$cfA|Y`fwvvQCW~!sHs3mR9()1*kEE}TkK*dTp1JqV%x>1*lAxuulo}Oiso-u& z&=3;bo#4S80zm@A-Q7b3tCu=$p#`3{v}gix0g|2fZczP%!4+F<`eMEpT0n04u;Rh2aDX=t;=L-&m2A|gE-B)Rh@?aaP59qC>y5&hl&b#*Ei|Z_GOIQJm8dUeRb4v_bzaI^uOq~uw2(Rdoyj3bLPj7TEZ z704*O4?V@@bCxp!#m^C zm}yEpuV_i(q?_OZFDc+mBKSs2FUh)3OL%iZ9c9j`1g`Q#ekX!=rh7-&o#Zh~jKVB6(x@ zF{*zR-gLglC~L&7eoGNN4~uxyCGb&ugD3wiiKviIt1})Zro9oIwafSUFXYr8$rr8Z zC4#Z}0yB100V}F_^ACBlJ`ny$ZSzE(FrRAI@8Ap;^hxm*obkOX&h&kyY_7~8hMUl^ z9D?T(LB`UT=*{{hc52Gw-MTf$dE+G1dwi^{s)RRZRVg)mTEg^!xtNEiGQ+)CYRF3w z3?tyV7EvWw@+t)De0cfK%!eN?Uc0$*yy+IW{2yR$G#a(klL}^;enBu_xgeN+L@jj_ z8l>6rymI|9rs%OuMgE?uX!~on^mN!P@yYG6QscfMVtx21WJUPU`q7wCABPXcet4n2 z4z6D)@+S<~K@S2SXc4%H$Q!#q1~VK1Ij&C3k`Liz15*ld?VZDQ>H@BSbNH|;Gr9%X zvKZJi3OKY6r>h6+;tZwPrEjjy5(N}9xK z%+QgYhfeGaFsdBfgx>JUpMX9sYLeWVI?3H;LVmp$`m&#aKbNqhm!=30i{bOZ15awf zUHaFLJ`Ds8=~2fR;U#$r*zy&yC7a|;SKe%vW7b)t9$I@)ibwH-AH&G9!8xLx0`6}=XuS%L|!upb=07JRE$GzeZ)p& zz-)Y>z7gyezB}O7W+XtH717DYM|4OTQ7@301$|fnG~-#F`kN^o8c*a!&p+wLUX{}= zT+JJ%ACh$~;cn@~30ca(yy$!2nqduZxwqPFdALfpL@fuO8u_6c7eb4k@d@P zeGV(&oo^$fl#7vm;o=5dAzQ60cWr-LWcA* zh2ii9W=z@0!z+#&6j9V{$INyC_D@0#u_~j9#t>k(On@Jf2w9C6ZTxm`NS~r6mN8`WtNxW|71sA z_=-5roKT7EkITF@=pqhkEYCJxFx#pWt+#M9G@| zx$yoiCWg<8sXip1X@ARLnx7JhrZk1>KSB*Uw@ffc!l!m(A$-rU%a2Z_CS-P)$0JiZ z9{RO6Dv%WkZkGg~wqJ3!!>i4J_sVnl6n(c>6c+`l&6|b_ zjqRgZ-8IZh!ccoyp}X=122o(p9rPhx9)#<##~KNa;@WA7f1jcbD!~p3+~RwEFgr2f zntcg9lMiYJ^i&Vwk@^tX8xOwh+Lxg|a^v>7;Mx3vSv7n;x_UZrk%R~EGrWHRxr-KL z#kzq*AL4f3;rxp4!+}E=(VI1)&R7gy>@brDuNHg)bmmphu9dL9iUXQi#9x4V$NC_R;bkzAsb&~f^cdE7N9qI+CFQj*qI;G#i zd;T8Yc}84kjvTikExS{{2Od`coRn4N!Mt|XPPZc+Jjs^KP6fPa*sCE_d2k6cr>^82 zo7cK+Ti3%k8Tn>=k^iAwtT1O_C$n}hbo%hKNrT^vEs1xAMRE2o&+!fiwBAusvf&Qu z{7aZwf3%$GLKcZeH#mi<2dQz)DePvTH~A6S=c{*0y_aoJ^w|WQNbzSN%~D1EbRw3` zpwg7-L}l)vlO6L;n3sG&7y`Kv7CC%S)xdW zuiR&G%;Zj@w%lyNR+=ujen}Br*(svqtpvdqdY0JEo#pK};)(q_=GJ4f1nb;v!J>); zKJOINzaOSjmmh~7{?u{c?Tjy`hSj;Ex-gb`Y(7j){f<#vL^88&$(I~<>Q0a?v`c zLNraPL`_sBn0Di_^}^iReU6y_-N`%$Zxo!T_dz=!%bUJH&c_>9d0Qd)WRoj-vn!qG zemes1-VMCDeK*k$iRKgoBYDM;P+t2jxR0abIqg5WymkZh({C0st+s$_qVjpoy&PT} z4UE}`{r!bv!8p1Qv&eiwGb~ro4nV!MA(At`drsDEzZ8|)U6-OTHtTk+>RWJotTeIM{kjXo@5znft%B4 zZ3nn|U3_M5aDY^(RYHIfKjAR!&b06!nvMBwAZnK9zVzu)%##L!@6+YYxEVTE_%`2v zfjXfU7&H^yVnf%?4EK=;EV_mNejW3s!MLB{I4RhfX@EfjPM7!EV4QBiq1oV6k4Ar# z2aL?X-}n`MS=$)a`1f$B&I%j~M|RMS;iUd0^kw76@QoSJ&WB(=^f7k!9g~skgZ=#N zE{#3>i9Db|gAT1>JMt5IOyF9U0gL=6%Z<~faCLRStKpdKMEgh&XLj+H@VqGMv5(OY zZi3eS0cPckG53*>3AB19*Dz=n*RXgN*O(4{{ch;%FZfI}axliLRG>2%kHb_iRepzFwUYrZeU zt~P;F4miVk>^{?}OiOo5-r%xK>EhJw;Y?fi31a3cUj1d3tgXm$tD3So#f&swH4OjW zkkF~{2d{1cd=!7n>CjbYcWRELqs|ToN9H)M9(Y7h1z?x^7xIGw(s@nTd0si@2xhM5 zI?ds^ZrzT2Sv@3;m)cQh8P{=+ev5eT(aSj7@+HKOKTp#7FBkO7;r;dFJVE~l^1)6W z=e7Q+L`PGJ`9LgZp8$TI7kZ9{B;K|)89UxHm`h28#rQ9Ys9)vLG^X<&J3|zP9 zjgsw;4NC7Hmnpn!77DtY8AMq#gDQTUPNX_y9nHkQ;olzfjTFzR5z5AIU zQUCW6=*U(G=2CdI%BOh!(P++Ki{p&%#1P}T7-C6{CoV-2@tTI6)EeabHIx#25cWTj`ApwCUDRDorTV_mjs$;3 zE$LOrN4+4KN21Sg6$=(t!YtqAQA0229^Q)Oje#+u{gn*Cwk%h&+fpSLi4~o3@uJf& zNpKtopLr|%##Tl{TLyha0Q4OLvzX%r@+{`>6;(-xnCK0Ug&Neu&EPR-A4tv@*P|Zi1V31v~uoB0>LezM#IFCwN$M1&tz=X}d)b z&7WbM$F^`@8lA?J*YP}7loErsm^Vb1VXv1js-C6@LRd8A3l4a^m>D3opBu{>SC3>3 zvq!L&KZml$k4Lh)SD-Jyh}o*rH;r^E4`Y%9zw%mu-v|#3QUdVsIrjwTJ=k_F|Hr%F79pFYUIQHJi9W5PON&2+ ztA7R^+W$~9!3X>CYVe5$K|AJvmX!gILxDprz#$XrLxMegJTe_$n#nb$0)zg8rnLhz z>TmG;{sCWnB)mTU#0(q#Tg?;T&T`qm=CB~z zm^_bfda$aqW9Q)x#qOBr+N0So^b0cF+9r4tCZZ0y4i4jyf(}jX1=$>x!AyUFxAJNn z@wf-RKS>1N7x@_}S)BUa0#0`poE}xCtd31}tGrO_q#~0!tH`Zy#m`Qh;XTrJQtg); zsP@hts^$_o-J96Eccc*YXlNFS;@sMU>9Woaf5(U5fpiBqx)YgPr&n;U%H^E(fAfg3 z6x@`B=!+UwQ9~MZXglT-1Kr3OemPIn1bqKH$ow>5H+Kp%kV7fV{#mZz7?4IBaby~=I5dNZ*)url zJzrUxNh3CtFZVc~*l*basD)6J6Yx8u})J&#(VFZ1@dE>qjCQfBx&iRtd1Brg3< z(qr3x-qDgEs}l-4)l+giHLoPgnwL{~J@C{_l0@5wF{0%}GSi>UBidnwyrE|~e1uWw zOfO-E&ACk7lEc)ybEtMf3e|d_qiS#)q}ySH{TxTdBdJWo0i*jMw`_3{(e5uJsuih( zmY)!~Z`Z5kv%X@d4gAIPLs+A07;E`vsL(Kdgi!Z4Jlpeq#K++5JValFJlFrP_qq$+ z{dm+gJJ6#w!_Q(8X0tPaAx-%CZp@*3p&lB9c`W8RkGk~eGts|w<=M~2wYdd2wsofX zxG!eh7cpZxfu5`wcoUEN>&hXD#`n;`KJv%Rx(a!VJAg<3;XZETG3arhLBN0~*k$>G zmv|L>_$A<3evjH_8unz%h6zuBpY?0Hv>w3Jy`yRUB51_sz-yZVhpf0wC~Bb~XjxyH zz&G!~?fx7`8ZyzBt%C1s4{#I?<9Cd}s_ww7_klyFu&1AhlMM`VVrH}x&+Qm`v$4o` z`wR1_zmb*r)(pP!a+kjcbYyoiGdhYr8uG9o-^c9wQ{a#V|Lrzvq4%Kom=nN19UQ>b z?VKYwE$Z6UV^_8B^D|7Y9pb-*pPh@zwC|Ss|Dh z{NlxVOxw$!YQFX3r4(OI+B}t$qNj7>#|xqP+{{^CY~Wo#Eu;?rX}r05iKK^Dk7?Rg z(b~9CY5)5mGgZSoH!_lF+G40l70+B>BnmDyvYoS#gZg_Kae5{Z=RaqtUA>QaQaldm zKzn$c;rl#4NndJ=puLQ##CvrKb-hzbJu9lH>#tH~n~IE#LHW$|13c;bS4uXY3Z;2< zwZiPWDBAwJC^!}%&*w@heqKr~ui^I>zylUO`A&FLdtOcxU0X9n#|rdmAEzg1kw&0kQE!aO!WY%@ZnQ_ZOrr91%)CZBrK?~uL2(3jeuHm$3UYmD>84tsk z@m2WbpGP)YMzLh?T_IR>RSY;J81sNb`@r+*sGzq0f$Qu{6O0k31+Ds+pdWLVm=n^O zNd&IumLW4FThi?e7uBBI6}EmGCC|}^sAU2&INmIEYXY&`Kbp;HJhOTI*)-;ucwX#z z{HUU5&1um*9+;xcBKkv^p`S*dH4Zi6DfouG46oVI*+jPuoYr5$iMj=TDSb~8aR|Jq z_nf3EQ#7agxXb%7gQ~|AaH^6xM73-a$dy?ta%7mQZS^Nog8{r`pJA+7g?gwAnS`xl zS>64~LQNuSnnmcT{BYjF83er9j%%wOpYc|lP+-vqz@g=+mmXm@-4A#HFRz;Im{+F( zLx4382BP*s?Nrkjd1#s77s0doeg}L&`T>u8P$P{;4dIJ371!?`-0m^X64XE`@QTgD z+{ho^^DBUBzQBR8;P{M&24f)n6&Ft8n!mz4`UX7F)(;h*h$Fs{zs=zrQsL2P1`h3?+u0Nn=x+HVkZW8!pKEZ>;~VV@xaRDIa;tG+$BUO% za*DybI8E;(oL+I9GfocUbk~usWlM1Dwv=@mGAcOp4&)2Jf%j>@JiI@%nSOUNHEfRL zbrjj8-p~L(1OxFB<4p~{?dh|mk zUs?$G4u?M|v-UR%oBFO+i5ibCRO&0)4X z@VQudhBpt|FPNvnYjn*9W*@YUnO<8>joMYzG;Sd^d^DXZCr`w_%$FC7rc)_(j^J?! z-up|I65D_vYFj^rGd&Izb?FY;5#3iz1`mPE{iI)FiTc%01xx3CpF zstr}tIi{@Z`3kzab41?^zfCXj4Nd}|ZkC9axhV==&`}Q(vP$pHT&2{!euNnICUNRx zCD61KbGpOWt@q2O`t2!#V?vnNHz-8i`_KJK>&6(STAD`GldzLZD~0|Jd%XvlMDrwz zs0U0* zA9^%L43_Hd0e>=wv*xg2tVtS9n~uRdBW|Kl>%x5G1$6Y&&|7us#kxGGM*xSWU`O<} zKY8*oFlaSu7z*rBU}lnn8Loty0Nlifb?DRn!S8>Ax~6N7HVPiE$8a#Wes~oalno5} z0=NGQ*X~!qv-`lR9_Y&~xQz!e>@IK!xnjzO zrOWp^4}IExJnt2F9@&@=-38t_aVDV;`xU$*=sX%jz!5qhAUB;1bT_;lu$U> zUvBbUCbu>&;@g52bM42L%I(G#FXZ&qZmlDjX@>9T)&8eA-Pkjn*$;U$f1@@D0uFry ze}-T2nR^I2pfkXt3~=3qBHpqGpHZ3&qWw9Us6WNb^p#Xzy$>^^zyw)w3mG5xVmXh` z&vEM8$B|zOu5%GQErL=xZ4Punzu|qn3w*Umcz9})c#S5O)BKCPbKs!Oe;-xcMIK@y zJO>x=<6IApa@}Sf zaprjYEH%B6N34IA@U9uf)b&;gbAFJ+oG;R-z56-hSbJFTy0ler&fg?hMr}aWF?>mU zSBR#2m_t39N)-cqs7R+0>AhJ*({nb}LYtvKHj~#+_mK@tXTa}ug`l6Y5gOLj3ge0W zq}y%m&0d2Bd|e{7+=yjf*O3MPWel}kip6!D#OyQSTRI^F8kc0j9D0tr3Zj_T-{*)| z-U-^{z)8|G9bSrIk%Ds&W;*?H6xP}0f;knl)!Jfe99JS(_%hMzSEjUlbwO$FRVi7n z;`rhE&#ET&Dd7A4j$QS$L}tI7B{{BUNS^yr1mEau=P&`ivSzQTlAHHB8?%hS~MEj)@|PZ3=tTj6|; zdZ-XP^Zvk}3)o2w2DdryB6Gx43yyuj#UrST{>T+glcR`n8RpcweBSyD8jVrVXcT6P z`jAj1JG)XVzq3-*ww&OMhcY?U{UTX?2Xo1|T%tRa3*5@2&JRzrKCf)ly!6p#m9zGg zpou|!Ju?HIbA@ihSj+&v%jPv7=MjwsbKM1NKTPV%CWq_F~dfAgj& z9_ai${{3aT^6tyi75_d5pQj8nk6yr`bl}h+?4|o*#{3jG69X(N#c9O%>wrN?e#(1c zm^(+KU*muwucB63hU>TpJixDjUD&_XJV$>w8FkeW?7g1@hyKFtK7(F%7XD5g_GlmB zz77F{Vt`8mYO|@pg=46PrX%mUV3ORj8a_j#htvA=LxiW_0(%_b6TJc)8Z(+Upyp{9 z0573V)I{N!`KZBVbOMWRLuWQ>qTJ?yckEa2pZ0_={_D`wEd$>=8oT>9fkk^!5ADQk zNQLY~Se@prp03BGVOx@n~DNAS6Aeq7T|a2gW9BZ$Tf?MpoVcGN(Vv7i42=k{z; z?}J)MLJzidj=SZfIi0Ns=XAC>0=ar?5LZ8Mf!y@(D*4&WrTmNIOUa8p%W0eEwhr>v zrsu|{?Ywr&QMYO!boI%}yfr+HSpUkC&1=eK6F6bUKcRh2D<-Ce@HzXpoU=d183B!b zJ^ZS^PbL})jj10#cXHBrMgMr7MMQThvM`6{jv&+XkgOjYDr@RuIMq4uY^IkI%M#S} z61Z^r+05{JD$#F^=XHILQ|mvQnSIkDQLmlK^i{J(-GvRrQhSv5?gnjU&>6S!SY)T} zRv2e}CyZEgcQBKG2ycB9%zHH*;$8bGhOpSnH*4`xUdb?i98?6Z+Yvtg&;IcuZn>;+BA2zbZ;mQ$6zn%qhpefPjB}CMGp>@@w^R}9aNx(rWC0$i zk}V@cvJ1(A<9RHz|9y%%N{)!Gq_fPiE=_P<%cEZILh7AV!d#jX!8R#Zw5-h(O@9=K zricroIT-gHTtUt2(Kj#6V&*%s#EzMurv|uoI9uTe#C&M~1;KF>*+PRZ2@W1di5~G4 z@CrwOvlCb%SmVyAib?URD{OC@KsT$WuqmpEtd0%p29H-E|H zJQfroj}x5d-ss6{@Y(Sh`o{gIh3-w8)GrNKqxNn+B-n;VgMXdu*7k=N==ywF+lIa3 ze&nOADJ1IFe5#n7!;3ePWJRwCcxWC0E@IxUvL}S(8G1*BTj%n5AqKmR->=-!!`UiayLwV z;^W<@nf6YnHP3)Ucuzg~2v~F)JnUV-q0fOsU7ki~fp1RC!)F4Ax`7k$4Gs@{n2+6h zJNmML(1^8S-rRz@`cdravqtlcuOdfLiTO|%>bXMf%U*(R{yH$|JobO!1wHn|F6>Y2 z&~8uWnn%L-m4Q!v2|AWN`2N^ru5mDWHzn{Y8TC)Fuk_IABR%>SbFx#wq5_=5n3cDo z4@-fzJ_^~ZW2Tb&G}M7pQP+jwv0}&kBpw*u8?}(bpVmzR4lVQNn;s*NY;! z)iR6L|2&&M&08opty<2t-dH7f9toB?{Z_f-$%z-jnxh?t=f~apQ(>K|mt)=99AMBN z1;k-4gL=h-P+|1^_rynZPUf zIB4C^$%@g(iQ;{DxNh3-*1ZppoUbEfRY*2x=nEX$44;Rba%%amfSPY)5R;rvj1JWD z*S9fU>|DvPa)w~N?n5+ZkR>q%K5El5;l&T$?Sl-r`fehpZG%VOt#d@NBb-+r+$}r) z+|GObcARsT#q(D909d0dRnr&z$dMN{%sck|i`kFkFfeER&N2=doBdG#z7T*^YxUIR|zyZM4;aH(JksT3_uRiZfopAiG#1@xkX*c&pK z^E|X6u^FQ4N}gnYr$n?Tmkai4VB(f)(XkgeRD4mer&I~nL+IBk;8XNb5_7zBmN>oP z4f`%M^WMl14}dl-3pnJ4_W#Zf-r2f=w|jsug#WL~fu)?97IS(Yo@rsg^4~HA=cnPo z@@=YK!`G^N`0fz8Wx&fIAOicec(>{;>_gryY=shH}*ltFcW$1PUu}@{gn5Sx%luW zcn4+p3lDB${v%v=h&>*QufO1=;ChBuR`+gR!)8KV_cNS^X%;K6;{M~K6{JEB|u(uz8eSPO*x#io{$R*m^ z!FNCQT-tc%IXjTtA&Chc`jD7Ty$-&M&!S}Q5AevM&`{-+5*MrFJYT?vp|FSzI40^alia3~O5?D_Cscr``#cn%+}J>bL=|uk7Ig6OhTl0`T6$n32MFdfPwwIb>_VAuxE$gs8-ssj1 zImwxW3pk@L->q%UNB@V+hih5P<3pEZ#9YU3Pq&AUg_xMOolKuH7kn9-oETuMaVh z>DVn#bE}>NGVRbHVyp}jbP+*h-?J>snj(bla>w10m| zw2!+a!Us>Z#$6DtE%==@_+(!^LVI=tS0DoKnUR;MZ6;>h&OAv!30~7{cToqojk>s# zyk%Y*uijS5X?`q+z8GA?E%|~?kuG=^oo3zZLX^ECf<1cuv`y?@y^DA~Kg1czPRXiI zVr2C(ypP=Fym2@(V}8rSEDG}t*72*D z;p#Rf)oc+tpFl$eb#{ByldcPv1jFqyM=$LHX^5&1KF$zMQw7&LyUw zGKp?W29KRPufB#Xuv^$$Jl{$@*6d>Jt5C}Sah$MI(Qai(irZr-ct%0_(D0YY`ktk3 zv?{JY7t*WAm8i z$9$?;nM+lF=24H|P><~amsT6fIa7}kSNKV4N!Tek%MKuCG>*5bvjqDs?4l->67P@_ z+U=`+YFU&edqpS9Ugc+b&!`yLx*hwj8+&Efud}G5!#|Js`ql&4i2X+Ns=P?&LPmG2CKTpgYlp$Dl77Dh{@|i=%4EkU^ zbzF~xZ(B5Por$^F;3B~(m5NS}V$oh&B04q|i(bzPBu^Rq#O+0r?MRtm52~Q{r5BiW zDzNT3cuKdkC3``J!Wo60Yf@Q$loOc6M-c0IXyJ!mB9H!ask(iBK0aqpfIP@|+fWE-K zC~+FCAB%i0EoRWC!5@wf5FT>a%fAmC8b4L29S9u+G@Xw>#u31W^n*SETGiS#;L3+x zJ2A|Pt^kK_p~s`BiT=T?x=Uwv4|An)m~qechd0n%QhR0r_w8d5kh^+XrX@lNa@KNqokU?6NSgA=*3F0>z|8y=-w3RKKO|bH^F0a zBKG(10EhZb;F`vxe|rlvq#N*I`~dY*25ti{q6dq?7g&Q?)_!=e_4i{>Z^1uy0Q_UG z!RNz_*>smr+KVZqJ_6eO|A0du08d{B&hA0Yv=kT=it{__p@ZnlMBvb7%!#4`-K_`b zyF2I-!+_=BGV7yixUbeT!AHYt(Q&&7_TEDv0_ zT+uNhS+GsR{5bp^HQb07tY4$HCneOmpqh7@QEO+SrvEyd7~ep4X5U0!cLy`+=FQCG z&RVMa`{lS*1Bzal4TZARubea2z;ks2bV|#R zHEqMpLt93j-Lr^&eT-}=InNuHC&D))n>XCeBZgpb)W(Bv`wF<<0l=*fa(P`?Ca>!r z#TmnnK+B2U_}pEh)38H8wjif>CJ|S!T;df|#`pBkWuC_}6q*rvfvq-DmrH*iIz{# zQR83dsqq`ka|c8av-2$SpHHJVj6wE0W@dhvQQt-X)xA(OJ}t&q%=E#VF<&kaj6W7I z!_)$*|1z0rKZnL`TP$apoyePSX9}h^?9Y-ePMD0V)~+e z$w*w&-$J+EI+nB~OyFCeLqqYG57#mjHPC8*t}$r_*EC?d+|qw4*K%blI5?;e{=l_Z z1r6*^{^Envm>DU-r~bK1U{N*p`U`Nn{9bnh zli&;TC<^Bz%#?QGKK22hE(FrrTPyem<8rRiXCBw^4SX~nPbQ7C!Hd2%k~XAao>hQ- znSG4V(0#N}*E&{s;+iPb)T0j^k6rx7=(ETa>E3F1c+3NC^nkZgI`~JcFrSvOD_e`5 z+9T*c8o|5%1T|AL>Zfwdss8{LK1B~%jQRUT;7d0jz99-3K(B#=;DGKoXF7TEF7AH; zFl#D)?-k(CAD9;n0|p&{H>?Ma3LNUAv$>`hbCA)9ef@*Q@{9lN?Br7qJm`;3oyii?A@6@cxm9+&qoTe|l8FI0Q9|T|Y&?07QMfO9tL}qzCl$jQWQtQBI zW*>{aNo1zzEH4n8wld~gSs>Z%>DY0c?0W%)N9(S3fAHI@me?QGBduF+n?a7?- zEWF6t5_r8jnX^6xUu|P7r-(>&n;s_f<~Q?*@g(}RJ;<1`Unce=@b)od54-~NiRuhu z`x8FO+mO{b3Os2AxIjHn8@&&Yib?QOPblT|KNQHCy}6uvEc_?#KVVB55hJRK72Ce~wa-LeQyGcvr>;|jRqdty0f8+xBf=~Vx5Dba)TgLxOTZmOjAot4zF zzXIB-JZ4UdVR{`hcn(Gq+eplM2gWf+|9H_h7ruyF;9u{9UH`CXqT}El_Ye9NKX}$> z;A;`~Zu#I8dl!qq8`1ozKrrVQVpfDY=;J&=pNew>{m2&hH@}L<`3LJ&mv&p0 zNPGUBLhbR;jlPB5=+Yv=bS_(A-j}X)B&KQNtR0+0M zF&na9WX{di)H$evI;R&guhuldYYT8F7JPv0G-~V*O^3dkS#q)@W5x+)e6UkcjXg;f zUu09=pB2QY#}337m}^BJT2v&N$EAtpAE6ZMXum(x}cx-e9%Jvvc*d>S}3 z1Nfl9jOa`BTMRq-Gr%F;BwF_cbRp5$oxMJhG=2&k`T#gohrUb#4(-HaUxh4=FHo1g zi~4L34!i;$KSfW8{;T#&%+$X_cI%I`NNqK0Hq5i{EypYjf2-yWo>x!ckU#VubpdkY zVDJ@B;5BFnl$*}Z;aX;_l-*$`UWg%)&&6NQJeRUhcX+^eS{)J7sq#(kP-o|L>c&F* z8J5B;OHzo+1AbJOp&J-cPOX>W1-#`nF$IJX`$+T;lTrkGc$#RRmMJ-hR;as^_+SW zLt6oFPQe~Gx6rMgpDlX?X7QTb2+nvOv!M-W|mEu z-M)qW)dJ`vbcv#g;rYx0wtf~*^kd_w&Ifgo33vg_GT%eyPFxJrK24Ey_pr};rBJZU zMO`EmG2`3$f^G+VF8hI71pcBi555*xvD<1W<19HRnYKJh(6wiZmQ@kVN%sq$;7VI# z&QVh@WR5Jl%Io`G=hWrbc>ThkdF}iEa*BdqIXwXmy%R%wLVt#gSz_D)PoIee%<^@H zWc@Wo>D-g1^tzIz@O+S`a1<3PtS1U3>mc-T378RCv9qtLX0AUfnDbdVb4@O0-k}+S zcOiVHRvZPUMZpWd1oano{KN7D^ZamX<_|L6O>jQH#&ZzC-v~qBnT9$4P0XhTqUU>( zC)frjf};)pwmnCM-g7o7Uu{^SdMS0K(kpV6X#Qb4(P`naR#n6qDfT8W3aRy83Nzi0 zrRqxH&_ig*nj<+@bCQr{D-@#JMYg-o?)c^wz2Bb8H-T?hA3BCL z^aIzx3f_|wGwLoJdIeZIbb?UxKj_h(06PYvhZ^U@)vv~$?LW+xlEGU%1nfyceo&u@ zq~RpKm#|wKhkWT{*saaOY{&}E!AtNmnFGy#HMo}>F+0BmOk0P?yAs}JXJ>Qu_mIzV z7+9JBzGEZiP%WsH{zGke6a8yBa)T_W9m}ER_yBtQXyDMTx$d?Zt7WeL=?-On;tP+7 zF)!4O=Q}im;gfm@IYFU`9hzr_9foPevTk%PubPuXG+sH(K#Qs69z25A6<}u{4}Y0x z&K@66Y?T?*{81*e%*+xkVxDAMlquQf#8O+?cG2Yu);RjFHk$(1D2+LLc%&rbTNdL$?Jg)!U2Ix+B3%|8@vd?>b9VZ{{-116*6p)xXu z{lttksvlI$X{xI@T@PqC_rNowF_Adp;F0}muwcLMuhe@?SLn_JiQ1G^g8lQu)OzDI zuSt*PwLj!B#~SRWM`Venzu>|AG?Mplq;RTR*ctci(yw49yb3>yLEZREj;NhbB_3uHVT;f^kowp#QB{(7%J;>>udPSHlBi zR;g$lk9y))aE&YWO1f9iL;s1_>h~CG-FjGXeRD{#l$^rrhwS;}ET&rtzo0LGm;YcV z=6{tMKEW&~FhlH~k|22ho=h$6=zBlG9>I=!?n%02|1??Quw^N%?-zl4j@ehoRc7_V zb8635*b>Sl8+K;43D}*XhjjKW6P&r=M&uyNv3EGZXA-rKg}?Q!G-htZK0h#9v;?D9 zdvp@|j$~#?McwvwHM6gQK5YQ_2!B+Gj((Mb>mfdWdPA$SEJLs!O%xmxkFnmbY*O@| zy-e+WbcKg$(KbQ<4?J@V(3kJ2CPr(HWE`G=&$MXfk#&|zMX0reLqydtm}%SnJrt|Q zdbocYE3`se+cx1m{~|n?HZDLOVagQY$q;DkJF#Eih*`+BS+voAK6&7|5cd*CLMlBSAp9P?<*oQfc3-@0Q zEmsI?5gqc&YLF@a9DUtz)Et_btbv~*H0Z_&4OiiVc4!!Dcy*M}IC`|u&~uDXr$*ie z@T>M^@E+aZ8_w~OTfUycHD89t>M=reG&4gL(l*7z2dbOl&UBis6`*_W$47XxXsariVhSNlch(^OgjUjlcQvdcU(eTw` zrM`2P(s*mGLY=meDhtAS#h!HHaRIua(AFyqv~af5d+1PjK%Z7Ba)6D&F`E9%WNdXN?2C z?SOCJz;J3Bvzghv<|%d6)0sAL1!r8diF$o~h`CMzb9|s3T~#c2?kN*pOEBa47&*rc z@Hvr^cy+HdcxOXLa61>iIQh)*JX_F>Lk4Mkp{V}`emvR0ps(N!_5%INt}4vD$Hxk1yj`zRDb(VUf1v$wH?n@IR00nu)YWV?QGOVP1xnX zUMkq%EfAe!GMMLI;k@_Z?bLJZZtD6Fd%#{9qWxK#XzLfrjB_HGRtC#G0d|7s5@OvA4*yK_qcoP7TlWcGzpPeyb1T#iX`{l@8Ybv{a|A7X zV|B6VlBP3GQr(IXq|mceym_2g2JPo{>R@ITXDgLOu^888DY`{A~sQJR*QR8xI^Bh@JN5&`>b&b*6$lSnDgdbog@3 zdwu1mX3U1Z_b2s#FC%rm7YUE11c(okW@CR1EZR4ZtNUZIyKyhH4DaCD9gSH_DsbpJ zaOh9;IbCNO{@zqz(0a^SsjH22GeOo? zALTs0fG0-@^kh>~!Jn?~F!<%VwN;^1t6!loJljsR4^GM2k1_W>I7eyS6{yrDuNKs; zM~Uhic+3#Y6QVJLo|z?R{!SydwQ1Ca`ox(8&%H3reEWwe6@|<6t=eEwm%p2Ot=vWJ zsqhw!jO|d@M#=UKm?`AL7hc5NVasJ=_Qx*$I`-gSlnRDK^fVU|1&@jFS=H>N#>`;e zG<%1vKNUheFC3$uUNPto;iWSdpY1Wgkb{My$&t=9i;;~{10DD_cpsmE7sn6Ch!|AB zbOLgEKEPM6BH#nA(>w6p`np=MET|To@C)=jSt&WjR!G)S@Wx0>6HQyg1n-z5Qjd|5 zqT|a{(RDXX@|+4yYCU>3?-*)Z1Rsu1!jMTF&Qz`hqS}@~)GJQupQZota#aY|6{uF8?@ zzvAt| zPqqr4%?E{U+s`tuvB1Ee5(LZ5M5fP$ZtehdAfqu`IshC(Eo7f?QFIXe+ZQu7Jv6*y zu=5{?x#4f{7z#SgOrLI1xPn%xyqnjmT$^_*?LOxO!}c^$eJ5K|eV!$GEtB))+fqtlKk6X#QyuYl7!%EqKw7p;@ij z97O6|i)c&wblTd6ysuW^&~`9Q^7l(Q|yTf;5a> zF4Uw1i4UsgVBZxWJnl1}Yy5ddXZzcWI$JNIX7C2Kbma;i1{cT+81yCf+|Pi2-=YT_ zje6%RV9)`at-zsY=;2o2F$DpG4q&FeA2{?mX57G{$LmqM1t1IT8`MFk;j4Wav+0hJ zLcI+c_jj?o2Y0Cc(pXwIV1iIP1T&$7;9s|Q*`Zii3N7=Lh0G^2X8BR{Xfid9 zj^NC1VGcAYRj>@r6`=)`Y@@*YIft5SOR`}90(yM^49WErXI_Tn^vIH2=x4k(=O{cU z=SYs7*h#pdM;%Z`Y`|IP+-Tx#US^JutC+nP=54D`@0H>6?=EU^Ig%J6LnQm^Rcfzs zYgFE=HY>gUJ|NkbMls9YpG9X6jmsr8p0y)jGiD9@kO zTV~5`wX@{bQfMHs`>qvH3;jQqt~)BKd+XlvyZ27-eTJe&jV9*3RHLazQ&F&@D1u@~ zEMUQcsHlK~fHaXRy;qT9H_@okbkmHHsJH$fJ4`SLur_mhH|mzSq%#Y-Lsif+KK~ipxdJ}j9^lYNz@Z}Cz6kgwCHU#5cv`@Py$B7_ zNa&~Cv1=NU!E)WeSvJE+W}%%5cnZY0M)HtO!ZbTqn^xI zBM$V0)8hIaqV#(Fpzdq*>6|R1`EFw1uWomXJfVrWrl#qc`!Ek&(39l{C@hiD%PJ+`iXJtc#b+bmJCF)sbO>@r6ie zc;VHUxRuq9+s_)dA_sc$3FmOPZ~9*vcGcz3Uu32J>8$oV@;fdC;g;jiTXqMr2KiEA z+P{Xg_yiN%WOSN2vS?W#_Pam6V5|ysjolu?B6vZy4TAQ=5?=dNE^V>oxU6sHiKd;% z2kR*!ruE<~x=s+?{WF~DG_-kXCpqKOCQh>v_@%;(wi`QRdo@vBD+J zh_bkm*DW~38?mF9qfQGJ{R!xlk>mc!Io2q)@;b+PR{JA7dS4YW`sq2Wde>prxCyhy zMovvL`_0Y5_5#2e`DnN~q7o~vNxqmjdLsg4=ClWEst z=NymRv2t|2FD-_ixRB9B!BhGP_-$huF`caAEcY9D>q%s|j)C7b9-fR0eEhsxa6Dfn z*sCjf`|J1!EU`Ij1smod>$*npM92zS(?nT3${BlN2JPN|n6|f+5Yz9qyk#H0&J3K{ z2#^0~;Ju3LdFw0CLw=9^5euGGn1^uRv%n`}w-s`3nW?Os3^V2z2Uy35UBtaChV!f{ z5*!qCOZ$-xkPD8g0Ui-G98T?DYIlcpTzGI(>16_6#8RdxF_EdIj6QaUp3R1O7!ilj%F` zLk)Z}!!-~Fo@n>3wj*zSHdjx~CSq)8Pix25gd$EuI_2N2`KI7VtAV)I$Gtx=${D_~^ zPUG7AefS60kgGTcGfe|9>=~twGk+M=`8o^lDD| z@e}D;0-ravYI=%CqF$&>;Gz?jia(Dhb4-*&kEy=Z33O*k<& zrPA8{WwhoE=$*L;&fROhU>&_&Vj{~VhL4u;hMMKbYFqu=t-bX2KT#&=sqO&N+Hr(J}$wwv!y!va3In95Yc(vPE-th3OV3tFt zH@unGpKRgK56NoJG_%@=&5ZT|y4M#KG4grH@T!2v_f7++J9R=ZPC6x+ZUc*s!-F~p zdYSpiOr=^lofon>{=%Q>H#M|rK`pI&wVc)CE^pk5`}EyHPM4a&=xYuk(=34~KRCq7 zeRH8dfRA$xd{i~iQ!J=Jc61plyNQlv`Z%kd+Jt^4+%r4zp5TG(rFj*sCLK5M>B;Ch z#s5by=9@|2Z?2UK)++eqzbqlPSD|q3RkQOuKd1Oud z!Ye!H3^DCMSGVmPvE0UPmI6)XW%%D%^nx6(A-Z?dX=7^yGF!H>=5OET=eBR6Tbk%g{)A2K1 z{a!Q0r`2<)Cky6_PbROWTu0++YG&r3*poI${k%hT1uh}o0q80{IGcX_7Q9=x@uXlk zdK2@`x7f?jU4J(l{X)oNyf=hH6Y=xtaJ_dL&qiR-0PbfLFbMo~8}9f+d16DkK6jDB ze`hN=rr*IAhkCOez3`Kb!#or+mFrN!pYkjC=@)=QzfUEd_r1CHN*}H*3wr1&@Xt3x z4?Mq~DuZ8|7Y6jh_ogwy>p=bKLsZ6I48U|o*M(QOFhAj_w~naOM;7u?*UUF7zTknCI%BJKy_&LpU+43?u3b`&h-rL`HckhSgq=VU0iG z^`t6dbbWZ8CO1*~du5EZDvEe4St8X3%;&XJgCv@caEX!}VP(@WSD=I4I2L!dtD6MN z?O?(B+#0pTk;x zvuOJ(nUtj|hc+}L6YM_jOpWz|#{D#}Z$AUfgl9p8y<-`2b}CPCI-gU-*aLsX=dFyk z2|F6{B$O%e=UQrc?TQw`5Cj|wJ|kE%@O$6Gk3YvOBx~Wci=Y$MgZIyBpiO_H8|(&p zs&{~Y{i&MM41j;?$wTG_`kxmhG1`yO4cU{ys=g>>lnoWMDzcPT<(ARXEv2|e*Rsl= zp>Ny*k3``~&b;(EF)ylNb!V!HTHV5^4#X2}eJV1A@_36I=BzV?f>nWg(;DP{yi$SP zr%JF!!`HT^LgFS=NgS_NN$uakL%$3;i!w}3;>Lp!tpxBG|wRPX;b(N1$3!_295a(~wi z3KNdFxG@nTy*ogBvdEX|`W71GH;`H1Hk<0I1fHzH+*5`5rwk8#WB29*haTV&@M~6J zPz!WOL!Q@o;Lmf=1a1Y#t--I`fI~xCog84+H0(4sg`h^>pz(u{Ri6gd6bXo&@%M3#@vGeRJtt(x%3b z{{SyN0vfAZ^IcCG7P@*90>!RzflS*M$Qo){hOA9fIjpe@V&{D;D!Z`b}*aK*CDsZ z3wTly%4$6KGs>CCjOyS0L>;t))4RtrnsIrec1$6qdA*d@jV)qKZ|~vU_AQcWqvi@q z+{RRIZWc86q6Ia7K+x@v7BqKu36c}hoHQK0VIhHn$r#A%&ZE~b0KWVD^}OvgeDJC$ z&M+&P(~c_^@O{Zy6yR!S?B!LNVZ=0X6*0yyW;D|RY4zkKj8+}On$)XVbIvNxSh9D} z;0qs8)mh}yBOjyVG~@Q5jx$}t4z>XKgd;L3TU!QYh9*fT$z=2?MV!$N?afW_LRrm% z)*HJ52i@=w(6_8><+SfWv-i(gV!3{X)fd+BIu$ygF9W|OR|&e1Cc$vyq+l|gh1dPG zV1B<9^AYyg8^EDZWOwwT2N@kV`dP?|e}>NXKbly>JYdQU@K|xkPuW>U8*gQc`ghXN z4U|SSQE9B^2s#vdphLKZd?sxUBUxC=DUlgq0Don!LH>}sg|mksOW+9RxXwmaIT;<7 zm+@z1isOy;Y|dmkO3>TNIyy^;wGH^7z%J%hA-D&Z3+{szf}68K;_xk(xMktNjA#$d zVvS!UGlnCWVZ4)(bDN6X%sg6?4~@ZJ`NY01o7l~`Tb}<8{X2JQ_S#iOm08Q%g<8S$ zn+D!(S)E{;RVmoKa*2mFiM79w#Hf4@Fsds%7;AGZ>t=#>As;yO5BTW0*kAqe=T!@w z|KkK_PHW}NKQ?jZ2Kd+RKv#XYi8odpC`|OGQ&2{ zp!(kTMF*iD(-pXgeUiJLV-Fl6{PG4$y7RQCSW`jE@(zj8l2DQE@?#%Ak4$*Knec(k zraNbYj~)puaROJ+_j>P7JpbVHp&R}uz@aqU`iH?gdlXpIj$QE_d}z7gu@~ZNyfG`G z2jqS;{u!&F3;G+qgmGSU@6)MlH#!v`M|!c{RbK2+)?&|+sccs-cC$Ed(y8-dJAVT1 zd=6Zhj+@&U=%g+|U+o1S{nssOU;h5J_IHliPpT#oUdi@#Y~ae#{$Gw-h*a+o4mYs~A%; z<_!uMgd2!?9&)#E2QsGOzPbrH$qxz{bu?bDKW--G>kD|5*9uNv5JjY~MH9t}2wwT~ z22S~B5_(z6DaGz`O8YhXD1OhPOuhTy9atsNzP(hcB1;A3xhP$3{R4nU22D zqnLdPS8@6S%ZPgX3P!yd`HWZB(8hPy)8@zU&L7&>Z%9DSV>dc$&Yz-8nb0KtTu;ou z6|h<=oifB`iRS09JNTwk`tPzu<9nDRuH_PQ6Z{f4N{GxCev>y_S@SROG4*0E^~U@7 zh0}s{<~d?W0UkU?o=<;1Z~v=8Vw%#x8=o`@rai5K`77YWLu5GrhS!q?y%LqkZcPNA zy`+KFu7Z{+61u~M=-K)eABQ6g4l|qn1MqUu&|8KV6U}~L;vn+J*2c01-y~W$Ihj&z zi(};-$wU=cz#G28zw7!r*18sZ=3DU1%F=oL0{lOfqE{tuBdc@mCgOPm&+m9vmrFdS6$_p@dBom@4#-RB@6qn346jDfMpG=U ztIDF)379)7vRK>xRL1jmE^Gf4-u!Pb65TP}2YWGJ{ZTD(PplK%_Sf<@6S75uaU1(; zFJ}(e!KnY(0k6dl+Wcl5V*`#_hx80BXK>rcpX10=g5@22ue|}Rx3>@*^6zc8fITlC z7xW3odBd|~yyad!Zz-r13<2eWKIsTCt~tnSCS(%+5BLge(8>EsIj=3Q<~2Rdtg^a+ z5lT}SA#^X1ev97Em*Z&pKJ<03PNd`?0+aLyDCVUEk$N}V#iEyz_-^F~$1hMlnmDz!`7)>Bi6KzGLv0E}l>Kyu6m~-51Wf z?rh-|cMh}45%sk4?kP&!1wGR?=sQC9Q^L3IdsRH_!=+FTbJ>5q2RcG#ZETK*VX^Muk)E6JoHoO zorXMac4&qkVqWtGm!5$ij|Nx06?4*P?3ByUt;oP{KLt93bGNQyhO8(lQ*vvl=DIvW#|!6@>Ps7G<@P91ab_!Hc({pI-HssC$s^1le~cE>wo!^53kBqFshe?3^xE?+A(W`s$s(rv8CErivj`V;-iAb7Z!3F`1w zMEf_qsgd~kCD1Qj+D4gHL=URFvqgDABf5=|=jI3PZ3lcvV~#Ki=|NFFGDFnAkwTl6 zr_qMWT$gD|mc&XOpi@Nx-SE@njXVrq9yR#l45M4vS{hC-yL^1-pr|e;=oKIfa)8 zRuk2u??j{5_oBs8!P%4IC8o^%g8oBv$1e$G^sSMsVG8n@PCn9oXuc3++6GY^amCkzuw|Km*wae*H!K*;N9E z?tl;P$8MI_KuitQ$l=XLFUJv1p)2GhTaOX>ygE+xEwl?kxGx}2Tdu2M`0dGzFm5j^ zM<1H(KXH^Id_Sf75M46X3`)@Lqsf*ChW;>}Vs8{t+=3LAi3ykV`7V&(?U^ILGk&)4 zxFv{o21Gd7q9iB#+X0dCT|hr+M=v|(kG{Whbnam`jezTlt>`j!m z<0P$%Z(-CSb&MkW2qmaPsex~xIbMZbW<2JX71NO;GK+tB3!27C@IzyEMZSF&DW3gC0#qSK(Bq`z(AZUrwX?{)etJC48~FfJN8AEq#S7qdVY>hPdl{|KA5> zMXpgkv`G_zJ@>(hZ^vEE2RJwZIK(4+@d}vPDIBQ-Ejcq(MHy)S;Zk(WP_F8mMAZIxvk1{_x>@xj> z4EyTMjBddyR=y&D7rtG{%ja*P)RkE-skuZ{Y%6f7w`IF@X-7rPvQkQUtB6v3okz)j zO>)VS4mzcU(Ukm93@x3rlcDGBrkGRFEa8G#)n@^`cFZC{nH?lp-rdMLVlaE{4QKU1 z8#w(u==t9dlIm_Omg;{(Km4r_*62WY=#z!K>YHUU^}pb?&w^L_-zs8Qw3^rL-NtBX zd=EZBFLDDi{Xa*Z!1QWL@0HHj|B7Z+&qdLSwMpnFJiyr39YmKzn#=fdGH<;bF7fHD%+)%)3^4m})(IXS%kr+iMYONSORi+4}Wl?;z65j@wF3&ZBs zNX;>g68+Q$iSF-OiE&q*)cj=yZ#i|8MK2|Ck}Fx2=P}y27}-r5T4~cc@Nv_?Uyp>3 z0$FqV&B$H;1|R(zz`r6xb|NyAO15$O`I{)SDp52P$FZ`qWWpRfMM>L!a;YTWP`Y=~ z3Gmw?-eiSu=ExSt_!07L4cKv~#V|JSB*s<-JgY@E_xy6+`dPKanpY{cH&nB zxKZzy2*bBjO57JCJH&x3qs?`keSHP8h;mpM0+2zY9zWu#vdmN7j6e9aZ783f97Z&qqvui48C|q-WbEmk@+AU8&4^dNmMuHLis+yrp6G)RGHK?_KCZ7jRNfgJq43t9E}tQe@|n$uL?QMSc!JiCM>bb-BQtbgkd4k1 zg?r)Dz)k2G|gM1-G3cAZ1Xbqk=JVEs|B%- zCV?k9iXH6QJaRu6v(pr4ERhq7`#$<)!N+_Bjr3V)5_iA{cN%+G4=|_Ki|yJ0404;s zb-pqcyCtw`1?HjWkcpL!Cv|KEV}F7Y#4PcLa6Zkk0{Lwbx&mHVybAdfGkn<6S zAEVc7sMGB6JYdjD=&SsJL$54x^?8N5#LRe6F*1QRda99zohmgH0Vr8zEL z1acOQp|tAHKu&&U1EqQ{MU=+nP_pxfX!X8m^sXPGp*5luk@cb?u+}BtoIfb*Pjt#O z`(2V}8BWQZ3@0BLOL4sig2v@0a~@u5V!X$NB(i(J{Y z2SjUGENyN|pe$i~SoclqiNS9>Q6AbuEH7*(9^y92`qpOJRI#16XDpJrou5urmjXCR z(-zv)yN7mP9L?CT#G`BZ5TiAs!$OtD>Xb>u_GcXLaXXcBUtA#Z6rfAKQ!TS5H%JYE zwG#b=GKo0}uhWqQ{IK6rh{*$;i~nRZ+MAWQ2_WyQ0=LELW5kF&K~oJfq2I;Ll165C z4!j}txLLwGV;F(nq1U5$9TiU5Z)EhlKios>_2~?!K1=aG9H*3RRh;%jA!qt;Ix)ST z$!a$5V0G!sc-3iiy;&m|^I7z}hC)~I483Y=N;#7|?r68GrMAPBGKYVK%*|3Rb<>wf z-2Tep?Fsn0w_<4Z`2^@W&|U3@E~0GsJg(NT`XB08O>sFqAjrB5$Y&hEMa11+#=9@V z?d?8tjJ|3jZl1_~tEnQE$tASqJa*le)Rm7*$5&>DnKFKZ(UnnX)%4tPl8ZBGB zn{bT0+PdM>xhaP7rnr0$s=?>93ET880L!&1$V37_Uj`PU&_!7CVi!tx~5BL8NlhF&GMR`h((C*97yZKWo zt$hue=sdh%-$dWfe~!C!^(Sbn4c-n?NsQC5&s>LR`!RH|WAPp|3$LB(98v#hCuQ_n z%V?z$=xa`L@h@eGeDgs@SsKL}--~CorTLViyM|U?sTWm?i(M+ORG0FNgQ8;WQKxiV z*`OpHS=OKKCx(w>IQxzL6d#r+lE1Uj(}S!IOB!x&$TNBY{*5V-y!+HGf}LM4m|hQ- zSUQo9-xf~v%AKrkEc^rYJ6QK+TUp2OP4Eb;mzo)1Q*kglcEEY3VMf@vRHANMDKmBj zW0nbF^$A;9UD!rwc!LDDk+kz`(#*^@e_8)~#uvmERj{^ugtj=usiUw=YRf8-+5)n9`!{i{>Dx$J z_1Yd<=8HUMWL4_XqiR|PZ`&kzM*MPE75ZxRb4pmV3pwAo@jK9IWEXMse^e(pE+aPt z8hG=RD#qMYNL#978O!$@k^6x@a_`-E-->g&pGX%yB7uivp#}K}UNsl^w(EFW;eDCV zB3M2B0pDjVaA;;BGWekf{X3nNPDWSTtV0Yre3TY0LKmCT zLNrU8iRu$%T0KAy+W&Bm=`5jSl|`a(|DZ_5FQ5lorZRp0v+3Ub{?0+yQWy2XX8PHb zEShXP&Z-^o4UId&>ee13$_+;u*5@G2{vOTH8zPwgSC-J7?;s~})Kuol+um&7z3E)n zl<8c%0XMAK@a2E$C%Lm=fpEun8F`=#;U2ZER{92yn|YasMBW{@4oiKi&uCJj7Fu8E7s()&ZA(L=W2!&>XFqE!-`_BL%1R8E|Jc za3~8q<^blRFL2MdqGx&>?)A0cf`WiQzszGhSv(Z(`->L1`ofR{`vYbn9lT{1gPa2o zmO1HjTU@HnJ**>ihit@0;qvEJAm2{1jSTCJ;-5>{!wN0FkbV~oobn*xC zT*B^TTK!uRt*OnVR8r)lt*T*EB{j5WRe`AUNfGs@5|G26DQdn(&*I}|mu6xO(MO_R zV_g}o`0a#K-E~?tho7b`@1f&(dmUw%2EWG9eVqH(5rTVi1n*e3S+GTHAy&Vw$WDx9 zOn=7F!)`^9Vdu7S?$@_+Za2a?N9IO}X;K)ke`hVHUA{z6>K036o90U7yBBhbpSFoc z|0votZzFAPT+Ns>0$JnX`SAAo5aqn-=*^zXDQ}14bs5Gef7t9&1#YFyX5`&`hJ2Cl z&$zVjA}hB6JJl=jRH$kxBRW*A^QvW*?n0IEt0K8>eWAp-BAXxfegXIVpGDjVTajQ2 zgwJ#~ylu6|u-1cnR}D9yEj-j|-+!cvZey!72V-!^s+U5%t7a z#@>a!^G+gX%+2IXcZ#qJA=9k9LSo-iCb8`-m0G?imRjj-&i?v-*4z-zs2jJ?O6)J{ z-A8HN^(tZ<(i8Qgb1WX6u@6&O1BYyojrGJf`WWYS2%OLh$R)a3DX~99zFS#2XU{6< zY_cIPJBD>+hO9Gdi2TQ`@5i7Z->8k$g|RcTYhvCvHb5e;}~_8af}2Gbs&S~ zmo!Et*-bRESXL2^d)e19qVh?ssH;P^-&}MtaOpS~C(y!j>`i}_GqSIb6HQACuTelB z#z7l4q8^!k4V3N~GGvaIi2S@mP8R((&Yjcg{=>dv|GxoFkzVSgj;*H#k3=)JlMvH|E%N({#e2F`1+BK1=#6^a0pq^J)?ZM4wnz#_8an6CCHMU z;3wRh;m6-U@6SE>X(`ucTTa?vflqcRW}s8>&aR!$-$AzizdNx@eTe(jMsUam_{KH@ z%T{3rlK^|v&<`!fM+Kf~xYxfjjeqpehkrN$9M*0V&QRWf!{I+`W@YIqTY#net(*%%G)QZ_a70ZFBCf^{iRM>5qixs zvMH6W6yFC;qP7V)AqzTzeU6IywF$^a2Y0awU8_5bMfHD?fpzf=W2!&H8p5G}{N$X= z_{ura>_QgN0(4M(S<6_xj*6C$czEx3b8at1@oqjld3(S%=yrF|R^b4nccd^@A&T=T zgU)sWaA@8Z;&yW@GIAn#eZ+R6eL0lXK3zjpu0TQ38%!%YcZs_G1EP`a8PvS7%cb~! z4PzP?ELu%7Mb-Us#njK8E|>oEFYj3@i%X%^D2hwB~*kBWp`!HR#zee~K)|zo6HS$RU{9 zc>9GiiS=BS#IgV%@0Ca_CD1v(mrLxv@vL=B1f$y+L936&p`$jJ(#@-6_5Wg4dmX-# z$P7l^o57f76tlLfYR>i&W+4Z>A>r`LjxPsK4G+oqV$RW!&pF;djQc*g>ipfTWm`0* zuLb5_!7V@!&DNjjZ=3)vRRwZq=R+fs4(<7Q+!&cA!F&Ne5dywRhKItf>?Pdp4b6h# zLZigcP%G8Z6}%Ri{<`6{g8l+>Dc^%nR&s{1^`m>TzJk@cXHlA!iL_?kAzHrq2rbD) zN5dP@qVdNZr|uT?Dd%!T?FWZN<=8?|_Ut&V@;%0>n(KIVXdSQl9KLE7bX~p3A5N>L z6#L7ZT*;9^R@mTTkNJvD?|II_LjM8!uVv3@NhtQR9Ta0p5|y`#IpvovM0d4`QEkYj zn6ukimxn*!7qf`#8t>0_>;^Y9+nat;Kb`G<24C&^8C*x@O#ad6IsC(se%vEHZcJgg z*M}_R9yKjzJ2wT8M=I<`Kf{k^Kri(oWMjJ{2YV`frWE*`K4=qpAFll@vLc}s>b`=V ztN?r3yTGBMsa$&r=AhA-m$IRCd=47LpS@juzUXQlH#W*#`~e9T5? z;AtHX>^X^ixDa5`FMd?_@_DYl2hb3`h`yjO%tV!o#lFA*w*Tj~l=NCGtzEtgTws{o zy>YE#Si*YQu$R|Kho4<58^(sn+!w8px$j&r8`&Ht8TIFSXy(HxgLfpQ`F;njn|pxP z2j$WlA7tr#UPH72(8K(JnKl}E(tE)r+>K?FBjSi`4zj7&l!)rud{Ma?8o!C?SG zytO@&w|Yhpb6EtV_t{MAook88yoOff@1`^!X-;ie+Mw*4Oefca>_tbyGkeZzQL}ur zOR;7vZ4in@!_pE;cVHJ|{w#sAj)ec?(OJk7rTFTZbvl`+FE|;7-Q$ zb3D4}aa;3hVeRuyvDW`U^LaTQuS4j5ro!i8L!a4(N@CuEKKmhW$NnPD{T<}px1w9< zNBG*b#S&W(@->%1cW6jv9Jlr{rXTh&>fC*dx;2T>Y(>^%JNm>l@bex*W{4+zVKjK4 zDrD{dP{rG8%Xqhw;JH^pe}wzLokNH0r3_;KCz*467th&GCF1Wcoi+vMiu!TThE$dk z$LcE1){l-$)#%aE5fW`}4gX=hDY(@6OfjXDG@TgOA zWQR-Q@N?0feuJ*J1D-NrLH(|p)#B4dJ18+Zk&=joocvrfr`gxas!Q@|t^qg{wvg+6 zE|7HfEoUE(@kMX457Qe6JX$xMJbuTA>kjeZ9Snne+LFBY;CI zf|!nG>0NB(0lDx{rW^j(}b$7ITm<=AwG+ zX^Xts&Ub-J7m+Vig8bLzKGfrXXE2Wmu+gv_%T^p%|e zM^uWNnF#(V2%i4k;D)wgE{efUwh?oXKl*^q;x<1V+*UF2NbVzRC>Fb#Kd@+oFWuP+ z4{QK%s0~`ifBaleo+6X_n}t;O6m+=W3L5OKTIY0rvd5{^WVp1&2hiiUQR4pg8s&(K zVRFwGL#3Yk*GW7=LM86%^^##1!h{j6>xJhGt9iGtSF_r%a7uA5(xq~bb{c!qDRplV zt$r7LMrAdtzXlz)8+N9Jndn#D%^BOG;C(*CXflpadQ%3YnSz|)&QeDC@HnGyLFT6u zzUhuvavX)01iE%b|w3Xma&>^FvkO?`Xa!{7pG$?Paq&4z7#&9u}($*b(rpS(?ENAj4 ztzdXgt0xBi4=eHx9<(kn2@NzA!2rHxYW#~l;L3eHIVOkfqleN0XFowIhX~BCmi$FzF18`-Y-%;<7Fo`_ z#R7|JfkS1OudUq4taaSQ)LYKTK((6DaLhIU;_Z*8dBykyu3= zf3BxYFSjC#7nx3W+~BsAvf7`T7=sa>%5prbk$L?cZt<5&MA>^$lgDpi@-Qe1d3^y`)Y#!g8S-xMyP)@{gbQBb{N`%My@h)`*>K z=1jI%n924zW|2PY47TqzXaL5{W_o@_epsI`|7Z$y)UH6bEg*nxzX)u~1&4GRp4JxN zPuWbq{mOLAJMfjA1je{Hs1gA7FM{i~qJSkulVbr*baaLE(0P7M%40>|}j@bayuTIbH=n)a~!;Jp-@3 zCo(@iU+V0;wtJBA%NkU*6}xm>lPSHho!CP+ONYI=Q9gWWm~41wsKoR5M!}(T0heDcOSE;QRKww3Cid+Fd2s^~>=-QbKD(O3@`)>ojHNi>4UN z^i?U8wJqIcy?ub_^LFvpS*eU_QVy-E%VxD5(3+_mi0%opr^ApP@}!Klp~LjK?sUeD zhSw?;-yh?TL+jGaxOw9CVroRTGBWv+Du((efIG+mVv7Zx2R!ND=FqxZ#f(i;&RPqL zDE%mO4&}hNbSWGCWAIb*}Te#s~d$|#Qy98V9Udrt;Ui0X*H$4lIsqX~{ike7P z6NRo>-)5)kzs)XnbSq_uz`g1ivX>?!r@JMO)&><4T{|-CLzB@rkNoPz;7)#lhf-h9 zsS8iB+Iy#PGe_QO>{()-j84Jd&(r$0M>K!)J|hjfP0L4t&)8ci*n6QDpkG<`R3OWVVl*X?IjFYaUHu7j-HQOJ_j z7b$jH3G%&>d7GZanBIrpYcQTzIphksp|ksA=#gdthi0ZRCM9kMZzE$|l1J(Dk+G)2 zpW*NL^C^YCFdBV46DkGss7i_P3v>&8T_c#Up{rvlxHpnC9DlVCw;jiMw`6!gCL%L!EHLOQItA_`gK-M-9WPW%%yMAk zF=)M)VdtZeIdZE~FoXbu{4tBQB7g5bwDcdH`a=v;4`|SA@$lu57b{KGD>@>dP5au5}FerIG)A!C|=E)X6`pNj2;=t0`bl*+* zKim({JDD8$wCE_hHyOP2*R!Pm ze2KiXGx)p~IP@*}>93({JO^E4FLEYr;D3DKZ>^e3cW0q@V;Q;;&!RUv1anX}^3Hal zTY51(Wmmut4fO}z1xJ(yjVA#P)%l~B2u}h!6vLOeo-PWe`rG$98T4c*LXh+ERl2CE z#11uQKQVo_gZFs2PB#4awK9*{8zi1DZjcOrKTL2u3=^#OO^o%=aM4t=mEt@0vgCyX zS{_(L=@RNhEq~0V{kKF^?#vaH*<~*6)LNJ6QUR^MnJGGabDj;$O<)|JJEgY$$nuE> ze=!hCtXorvWeM&iAEMLq+!vH(Z8L2fU%+}I*TZqBiZRTprOhjAXvfoP)!IQ(d|01RM6$X z3!%hL<9V8PY;PiVbhUcJFkd!IB~<*LRa2e#hHv1oaw6qVv0#;%_ozQDRh`amp12!z>Pn(miM^a z%(;g(@oqQK6Y|e-&g~{N)cx3LZNQ<=p`%`1EttUp+59VchX?k>t7U>wiNDu#Rh(|1 zn&={MvwZ>Dkq}^FOf4~PuR!M{_Or?}$T5fJHl&(pHi0+Z3w`=6+`SearsN9`qU$_M z)Xqni-+zH!*OAwI8`=6H@cDS1V$6TG(B?s8o#>&L_yw9de`G_pK+79fMO5KAjMOKR zR#dH|rLvWS?6EZiZ1B1P*VN6=2A;;aT-EuM%+N~fp26qi-Ar_rTw3edm%)8|!`FIt zn$VU8&z#wZ?~ntV-kV0cV&NmZia7^+-JKbDZUKwNf~WoipMQp(Y%e};z|WlpH&lsd z95h2(c+xJ+Af4A|vX2X(IoRRHJdRuB>e+#;>F=N$iiAcS8H}B}In?8)$QpuXsLKaf zR0JIIn@@GWyMXG6!%p@+dV*#Jh`rH4t|#AzJbSix7rcE%q5?aVdM>i+CnZs`lx$J+ zAcHb3+fLjX*T`(&hscMMP^rh0buuFxCg^4YgW7je%Be9TLB}-vS(d2$p+eL|)VZ`* z8=cx;@yV>wV;nG}P1P#np@rUFaT*3yH^@rJM!Xw5Cerv*5f1(#ZbIco=AzuW71<0y1ZAI{?490kN9q>LLDQ$gHj*AvSm_}xA}%oxq_ zjM?`fqm!r7ic9e7UED-W5o>spYMDg)$znmZehWG+(kNYB0b`f}9dS-A(IA^emwbvf zjlkuUcq%{q| z*DsN?R+Gm%vT*||tRbc)_x)9AKNh* zAFnrX?#;)E<5VlLeT4oG0lFd=eARfbF>gk{{nG-@ zC#ksGM+Q(m>L9UCv%cTyA2Y~@7Kxf4tHD!eqmMV!r8AXMhOg==;}vi@i=&7oaHBB1 zW-IT$F`Sr^cCzyCcZsUR7*V<<*~NWTfkPKRqv6biNCKBcR zOi{6^K~(ylq_lqZta5T4k@Z$V8-3iReXB~;^n%Ms+sA3A?q~JM=<1q|?}O#Y82O`- zHZQMd%5}c9^`811=(_Db(KKS$J8(8HN%q!o4iyC)a)C2d1dcO<|bTCN=S;Of)L^E+a zV}87wl|Cw^=`T)EBnaK;`A2AB6F8WT8p@P`8{g09R||)3AQb#dcmy$ASOZ_~3aRlx zC~w~z!C4ri@_rReo)wFt8HKTcm zjNfz60hOh*){&{iT2RRADeNc1etb5f^~S)6ms=Q{HVglbZDzL%gA?u9~P|1W$TvB=Oyhpln;alFqQCzg+@ z!CmE1?!9TuFijE}b~TZVn3chLo~z;PZy`f%NdxaFL5|whdfp9lnVbDM>$qLTo7UtL zeGU3v!&8ynlEP`DbCA)C8|~tuY;nv(_e(kLyCu9nx=7H)W$~I_X^cJ|oS6pw*K4Xc zy#aTSt)+}wT}W%+JWQ#x+O`nJ2sJDWx4$I*l0$SfznsFG4W zkG_=ufg376!D-F#5k}+dD)IDzMVZ=quz^wi^hByBwAmglfX zwa+45FU@Cql0#kn_amtOgixlpH&AH5G@Eaq3tU;{B|MUX`^m=h8a{7D-!*#5x-P<3 zhIyyQZyMhj4-6{6txUvRbaxilam<%I{&G6?Bo(=4H{siO6?@mUIg;C(fj5X>zulW$aJ{vqWG6H%nvz`5aV$U)Lh>y3=Cy(yp&i279 z$v-zRL!C$VkP&%j>u@V;1z&v_JKGBAhdjX%{fz$BZhx_F9&l(=5Ptv0L1%U9pfs&g z)cYb+dU!Fdxr95AJF*wYH&Mp-YbcF1Th!i3q8y$Hv^jUbOW&RZe^{)j%-ur?5y>u@ zwa6vCR_2mFMxXEU9C#y=Ddo`vMDDd&DJXZV6_c?O2ObqAq?T6wdz#V(oTBwiGdc~c zM0HRRrFr`xV@!@DiXS&J`s72bIys+H8^JYJlre^Pp)DS$WR16wg|Py2(a{pts6+4J z+c~r`DWB0j#II#Rvo^Jv);>ZG{0qp)evF>Ly}+dtn29owANo1GNxsnMu7oew4>;i4 zjGxE%!+dzQhGSm30t|=;m%JEXQ-&UQ_$4&oq>6?)$SnCFhvvR6V)(Q1jP!4G&Nd|? zt2&9&MdZ4aOA4H-?G2*&wL;2Zg%*2OF#O+3C5Dzz&Xm7}v#i@DSf+25*>n*S#|zLI zUV={^x?}Z2^dIWs70zfT=F`BSKxB@L{hTsZi8>5@O4Dj-_4R6W;{ub$A45i1HPLU&A^MTom{q|2?=9tw z$UQT@0enWTiy{07F@JK1HBLb;)a$s%ZOEk5@cwFlgeOLx;xg485shzVQHEORODE*h z1|MjMGK)C-(?Z^wU&tGH@P7*$IKxHE6_rCBD-Gy=DuD*}Fzu*JLLO{9=e8h)xb3gx zZ0|O4_Cv=6yA(N)U3h+M;2hiPiK831HZ_|uctF#dvyaof9nUGFG8uIwX1r&>(hrf_ zKL&YClZts$R4!*MPUH-km>;hlrp(_KAU~#n)&G=BYloqy<#IY@+88gIh93}>ssu_| zlY{$WIj_UMr>U#qG%q6Skp>Qp!25+Bv)G(QXeoh1ucGTbV+fy} zsRQg&p=OQ7 z?viADZ3%WV1G*E>0f&BDM0F=`9&}E~cFHF;h{i?0ofh;Zj;;aUTuT`?9HaEjHI(|t za#1-SelQKPS{G*ysv^+k;eS9>{SZkBkN1nR*K$Qke3>YFGe?w9PZCwbV<^>{-JGgv zuS|Pxhe~BlV1-!+DaBvev^x11ZFty3n@=IP>Zfc<8ym-37DM-ZI+#~|7Q(7W?I)@! zsYF|jPR2sKzrJ*ow&fNuj&t>*bqQvo&nsEow?zi9x>YbKTO|4v^cs)Bt?hF2P`(DMbw|F&6YNZ*khS&G zdCqtkTIJoi`>ZKqH0z-|DvM&x!?$tff-p|mznCa~4P{L^n^>zUj?%pez2%SPE_*_Q z(^8h{(*KQ)#m6h57YyS}dBCA%yBYJu{k-5A#navJTjcFz&9}-~dFU}@a9}6<16hJn z@D0J(A5UJSu-6jx#sb0kQI_BlS|qXLRtTm>bb}=VkItb#rx|llbR>A)-K_1!NciU0 zFxt_(DP3}=OZ#`G%hYm^F$HgD4cDS*)%}B%b_#MiZlDXpFOAh^loMfdA(8&C7`=*h zjM`C0Yi8Aea|i#LjlB7>$U3Sh7qp)PbIxM!XaaA&3K{r`Wr8&Wy^a(9kE64WkK$gt z_)lhL-F;(tp-zPw^%jb|yE_Degdhp=gb;UkCkYX(P=N~dTcIttcIyfSLWu3oyodLX z{ZJ^jWcS%e&iNknG$r}Weyo6+gA0iH#}Z-=!q+b(SFsJxmhFxCirrGE*w2GW)KaEk zZerf?s6)HJlzgp8Ws3o;e+u|$XP_m33dnt?#*P>N$^pLkh3%wnXZkcUbY>j8zQHcx`@* z9L^(^(Bh(JO+_8bgim?O>w<^B2390?RNJ6cy4Zo45P6j+P>T%6WmhFzKV4>UndTa65B~*nI^x<18MbvxFO!vxV-Q`TTb3GTNQCg>=(c-g6;db!+PZP0#EN z!oX8ASl65t!4WUJ-A8imb9($Sk`&iS=k_u-k)kh3oQc@!!+4RR0oa z0`||Q{R1{#-iTDAi5!M@^-EUD^qFJ-diKuS>BuV^%5XQk+lXM)h0+ zH5JzpeN?qy_c$K&Dh3UIl=uv>*|KR#lA>#lA;N(oMRlT%XxAe*=aoD~vm>2oev6}) z+DyUr0sLh*p`Bk6%WPY7m^~9*`yq|U)2kM2e`j$U4MpD7N{uahu}b&k29M^{ z;MO7Fe0`QD7!K#qp<{}bkkhrqd!>!og3y0E;Js=+LCxjIxgn!YGPdJA@_8Y3AeYbh zLk15T172wm_9HRyJ3-s;*?1QB|8}*_(5W#?CpC^7+<>bwqtQ;$v|;#?Bzv@>9Hhs$ERx#E$5m!>7wD~ zV(e6*mG}et^>OHhde93Eoa9dIJM1O!uP0*{{5bRk4}$S}q*)d8Mw2S|81DJQY6S0K z1+r0)p{7BW+|#&?MwAdsTA^b5ra-pcz_*#0&0a=dG#nY+@76Mp12;g;oq7h2hkM;9WT9+EKQsZ}jk*?%`xW?1 zki8T<8aeeDqb1wJ^_wPUZz$-(y298ecEj|G+GaJXpt? zV(PITV?)nJ3LzeN8jpcDekob8P25B5$M+N6@C2%!o<{Y@bBXouYU+3uJESJOKE{Ik zIN~)P@c3!&z1|_Z-)xuc%TR}W*z;wfZlm|M&PIN4W}RYswMa3lvxt5=yxC1V8Bw4e z8d@%^vrj2D+gWN?pAsxb&WP3&?7DwJ#%$bi#kjaykq%^2_D7f?e>q(o2zo_zYc}@P z$(W-e&lfif3t~y<22Iu5q$G?xRY&| zBHSE~KIqMt*v-3MCb!<3r1Z|1LkH(C7kd6#DqO$4MCjf&llKQsW&Mt6q(2M!k9F{= z-oOkt5%tGAPxI>??cyWzrN8=cdw&u&qZZ7^RmdqK&>)k&-pL>C68nG^a1Ys+h>XYypNjH1;1P%{AZ)!agA7_-24r7 zsArY0_u3}8|KF%VVKZ_per%8}eaPo{u#H$2o+S3K&noU+@U+ftqqcPHGplO+`o=21 z&QRgk#Z>tX<(0Czr$jNvW+~F6@F}O%QvG!J1Y7Hfrnj7^FXSp__`u8;N(N1rbN$w{ z&^q6p!(5}wsQZC(7SMNRSDhxHueDQ>J1qk4Nq|`XE2{HDTzH0y7oEtH^!a8O`l6S=9K$VbSg=p}Jb|YCk{D^;PX) z^Mk2+0sA%VK%DTcxvZxo@0bo%AZ}BELG5DDA+XDmk@K^mS@O2mi=N_2Zg=NV^L}VU z#X`jzS*$o`l`D=VxIdf*^JR9r?CHyvJ-<{D&zX9`y}VI!xxnVAJ|j9mN1Y38lPo7% zC9@eC;!l>;3dO-LhVA{M!m66+1>X8_+*CR}k-;ZOr{9G?Ym#H>s{R38rrG79(6@(CAN{cxy*p?uI~^RossMf)bu5>=z3`RkR{Q#PZB_cGCHh2F znNN4C!fzN>F6)<65q&gz_NQMZ&IivER~~$=E8w>%tW$JhnBUThWo>s2Uf;*5jY4y3 zuaFJbv6pVGCPsfPHCZvot?g7SnDOisb6(;J+4dz~qtLB*uH)YG1hg&XxRuNSS2QA3 zu>Z47ZGV2PO8@RY(HtLz+eZZM_eo5@BZWv0>|q-12IQHqQoBa2QR&J0WZ2b2Y<(7f_+bApT=`UvG2vsY;!*J zM%aJ6oXO4qO#vG&g;_s3D%j@j72RcEo6K9PwrpIlGKOPU{R+4Nd>3?}VcdCbkKk(E z&m5QcQ3GZ}%ceNN>cmcG{V`?}%BjAfnrSb>L%tdN(>=($xDRt`61b~bc+VlX+56&2 zDKNHE490dn|*sznajXW(|TQ17xU)q>uc@}lIq)Vz(z=xwR!f5NH$)Gn$G ziX)oKsfzJtHgSB3O!8r!)cemPq`PjiMF%cL%WFc z8QhjU}30lkZtJq56)M7 zi)Siw`5Z;AnruludCo27$C$if%^g^*Oi#L;AWZml~Dz{gGiL`2!&`Z;#fw8$_ zUt>8_zKHgzf0`$gzA4J!*%``!cQ)-Qz-(5Fxv2!c+UiB>zxYz|>iwZ|pY5>UfA2bf z-^^L$=8v=4)n3e1)1l+uiJzYd-`F?MdP6UL!+|<>cnpm2&r!tCU-sb^hLIq4I#~s9(%0^=su~S(j2G zYoEf7XI>387I#wH(`TvcQ|NMpR&F*_678rozkXVtteaX*4ACcu?KZqN#^dlm99Jxr z4b=P`cHEuNFg^<1d+Y0p`v5Y_K0T{AziU?P<4dW%8QEJo#e#J%*s2?_OMC;lotyTH z4t+S+PflURbLre}iQ}$IVCCIOB*C}ZUX%CO;lG` zub8IgFn3jyFkIY&-Qzl@`vkuEf47Q`X{$x62`uW13Ct+qv%5Qu*?BVewkNBC-7%`5 zAJZ7_Jj5JB!WW`nu@$APd=utgV@-TVa?V)4&GUwgar^4U+!( z~1#)YO_Hm?lOE&S!RuuB%{5-nBt%x|kw)Cbfvcv9Hs6hQ2P`wX{WR z(_p{i$YSnSbD7f)X2zs4Y8wG&?ABuL4$R@s(xYGsB~$&zNTN&6R&cYVmYJ1A|77&C?(X69|+FePr1oTI^q%U!0nk6xzMS1l9_ zZ*P>Wvv-S51Gv`qd}40MqWVvY!F4*$^f#~{i^4n>2;S;@;2qwCr=8;4pYL!FaW97^ zI%q3)#K}#PHw+rYh1g}!1fMnx`svaVqU$Um#+${&zN#L4qz2~tx>T{(Vviq`M(jV8 zQ%6D#cTI*Kz>IxU3%v0^cSx3fO)5iqt*GyX@36Chn?A;l=nF9OO=ZH6Lh#zu*aaIg z8-9bFkN5Ri(m=R43<27(jdtX2nnP$qgF zE#Q_fE15pzq--v@;4=<6@7MmXoah5HaaYYFwo8RXKQx``w!{hgm-aK$AA6{AAcpF< zBr)?F$PzDZM9+JgdapyLI22m8*(aF`S|s-;$ECpOtzu9E_SFfe1=l0!m-3c-rkIWWtyf;U<_82lj-d@LhK0l!NF2oJ`?>^-B$F5X- zN9HSvxIk7W&%f_O7^BJF9L!h1aH`R%>w+McEP zn(pdqb>FusgoP~f)02~k@4{4NVD}8t7dMyRG|m^Vj$W+#WBy9@)nQxN?dpTF|Jg_) zPhQTtCn2}23oO`OU>#NCF&T-_srfQcgyH`;=q$l2M44V ze%GVmZvO(m*#_tsqu~2E2#wJbE6L3T;4t>!P8PM%*I&6+8Jrra2>sZziTScFzEIZu zRG{c$%N3K@Ky6Pz)0GIXwimS}qDa=Ciudd9%kt}J+@dd{%U7^)c>nfG{Gf`Ar0E%^d!msMU3Ps z%O>W2{Mea1yqB_=X+j1V`1mZXM3%br8h34hCuuA^0bf@Mc0)e=80p+d5{SLvFqQoK z_~55;#Jm@N$+xqJHZ7a0pNSW&m5I#tCNgU0M+v(1X`=g)YL)9}e1;yv{pV-!zf!YV zz*AW~Xe%;Qo50;a08gkr6IrEcJaBa}v6Zz_lL2{;_vI7yh8jg1fW6!^)l_f5-}Nc* z7PAtF*&mI+>l)-luGDJk;<#yXir;hzI;{ubNqKCO%Kgh~m1Ey3wdvDUD#Mvol4-;u z!GYe*=|3d6*5*-Tb1XF?qe)j&!*t!y6;B1vXbX1p3(iQc3OrVWRhtM7nF-p(J2mKo z7SX%3UUV~LRHk+ylMZ^$_nR@7L3ed;88NKIu6j2Z<6qY^cXcUoA}7_-g|Ddv8FOHg zxTfQ7@ILa$uAsg&W54_3anW$FPB0{6Pxc)i8OU}lN2X^a2)4z}}@B1tSOq>x>LDxS+3}LI74)-PY?{h z9%Wkio^=9PevjtK(#=R}_zM1(>F5aypu4)1z;u^mnPEC|JX`iMdto>?mLC!H!?U^V zqh`Ss{3>&wgMM)oG-==#Sye6E6?$9@JbGLV{u#ROA^3bs2TxPPY=1B6&rxKZH#Lj; zE8ro#gZ?%mgB$b_f_QD8KvyLRY<#((`Uu%{EqG0JzX>fAzCDTjr4b#(ex}`TUDf0> ze+>ShCY%aOR`Y?dWy0<8*yXrZ2tB4PbRa8UR<`H(*pVzBUmqb0L)OW{+GRdkwP4VH zZuWq$WcuLXwO7FIcwW3w@vQpaQ!hw2mt#&_KLeRX>l9jiOf_(@Len?3R+5*eGbSzd z^Gm2hC6md(WbBv9=JH#%h0?XCP5k<b1mVZT#^ zp6H(i;y(;K>1Egloq$K~@@_J){;)jwevCZ$^E%SKXBPkW%3OZ!<+<$IH}IJ~H=ka6 z27dhI;4e-@M$m%QO83dt%8iiq{#(Tx{R4l3tJ@hyl%_<#CL_zQH)QxVhcjgD3uS)O zKpn9bA`fOuy=cD=y61)*MSJL&U$-~cuPJHr>y$3pxV}|3e$pyi)*a`@Vld3C*wr;0 zWma(1>}74T?a{M-M>F^wX7sI{9o#VP1UylV(5u#qPH(-+CDuuI?ijX=T)*jG@Ly`N zAAh<^G3OR5nzxQo>4nWq7ciIWS1%Ct6BbFPwB>4}Y85poTdBQpzwC|-WuBr?A?Tl# zV!)M^%uu)uS+P5*HFiCGY+x{+38nBk$m*4Ke$A)~zbY-Y`){?P znO#Oy@nv{!rOZ4DH@paF3KQ@eoO+f!UWX3pNAO0L<39Uo4YU3NP1S*XzwV!6g+G`_ z^~IvTBWc!F*pt0+^dzu^uz0ZE31)Brea0^MlDf4SRrc`mn!<|6mBZQeeAhp zV*KtH(X2>dLKyhUv`jXSuk`DN;W>N~Avn@DsR9DmNbY;_{4egnEf90spPRYu&CP;y zF>*^v)4={pB&Mzu%p`eC_h*fuALtOw-(YV6FR63bNy#a}GkOtP;Za@KF}I5WlQA7+^nVMOw`Y!65@`iAOZcQ#QhTZmE+Jmex63%TXE}r z9K8XL`KoX^56=OCvP!4=08o`1m2@@ zEo!K?POwB~bMwoGMfJr40)OJLBs6D;s$a_m{lyN!b_?F;18*~T=|$>vVt@Te3pM>( zr5MM8qjDyK>L!Fy7QBtpfVD(9znsZ$?IOaQbicTwcu>_{=+ju^Wz{p=i7;)Aj~!Yx zpe&r*@9RQlTKr_?_V{Oo8#kVnZeE=r+#Z3M?1S0L;F-0u)O3tUchzw*w}Gppk148- zg$kL2I<#vN8H~ohx^*7wd2}_qf!%t4cPu4yVu>&sKX1W7BKFM@x&x;Pe|LfL`Ymqn ze%<=S9_AYxsrXN%K))GE`u9P5ISRX@U*@oD5984R2GJ03 z(B8++scIR&p+^7Y4)gV#j~eX%eYek7yhrwZeNgtzO!JB1nSR|*M-}aCun=cf`wjj& z#o{ZcmXC4IJeb0fH$&CuvJ}IjGR4qR!wmC}Q_YK&el_ZbzOP7B%_&#d-MCwJ?-jH! zAHv-a#&a|>E489uC1NAzQyOudi1(uS|r!}8gBioN-=qx;43`~ z<_|t2UMwaicOqAHu2ZQm&yh57izRK!BFVCJt=1^*^;r(bE0*#574Q3-;q6)>I;Je) zrrw22-Ljn;XNMD8!FIu_-5^>#hZMYTWy7P;JN*-hug88_4Sk~S+e3opjXXvDdX=nR zRjH_E*Hc|M-eX_krur5%JZ0#~qoJD##LeVeWQzWS8{MG}<}!3pPycbnwxdNh-@?uN zD&E7Nw{!g?oy;h8QpelSH*Lbc+R{XA8a(HRYgG>T5bQ5vmv9@|L^avmN#OZE4Mx&) z@chh_8zV(DuEqoRrE1~7Lm+9L*D-XsOZZx&q7?+_dl_b_MCUg{XPm%H4d+%*sb zZ)zHHaO0V24mj3PX`=Rcg{afFi)Lgr+rywEz%1(g7CUQJ1=0PAIt0&^uBDuM{^_KnX*~;Aki`uH(4udu!skhY z;OT1+hAgQQL#CFCp1V zMcuQjHQMJ^O8VRDndu=gh2P!*&kwldZ4tzhRUm7p*2uc>Z1k6LlCCt9tD>8kX$?FZ z(_a%kV9o|+V=v!OEtqD5A@Mk7cKA-b&ozqy;9v)^Rvvh*g$Jw#E4c>q^>^@P|C0?y zawT^l@7ppXm+JRtG0ndjg1)OzFeFxRV=sJ=3|g^=Kc$ZD%h<18pbif*HZSEf?fEFd z=-G{56RD{4!)4XgwXz8RIx9P%XzTKQ+RNpGnnNXi&0ooi>R=cVlGgcH&B8%hnA7h& zIdgEZ5p`(OGs5-Q=Y*cj$!s74e8(YkN&onDL>PyfYs8JoSRxzVk5N<+3yFWmBs%cl ziL_ssMhBoz>TTXF_peM+{M(N(GWRg!>yIg#xrY_$hk3N8YZ|*21(ts_JZ_(29vhDt zZVq~(mzN6v{)-t<(}Im!-J{FY>bmt@y9hJJ*f4Hd5ijcw7Woa0sj~B#?f9IZFPhmr_%ar7&9p7V zc564Whlg@oGv0@*Vuc*`KeP=u>yx;P{{v>kC1~a5C-Q&;yQRS5U6TEg?UM7@ zPL=CwsK)tZB=+;sBxr9687gK`*Zu-Q^IQTqs$*sEr|CXR%03lcwnr@wg^v1UB{hEC z$jrrE#Jr|cG5v!_cR4k_pUmAeBZMJc`;d_uin(RC;7I@rR|)6dV+WXb7#ITMGpYTF zBxcQy1}895G&&MRQ(v)QdJb%rAo$ZSAP=$N|1~~>I|x1qc-EZ{pr=`#!G_wB;KNAc zmaV1Gnx0_RP0+>ch4&BldUsJ4^+uI}{Z>!C|CAB0u8_EXt)Q+as|1$@K8$L3j{SAa zu@afv1&v&v0qwwM^poStxXF;i%!Bz#(0^;>F?SUxLC!c)TaqYA_d-K9q*e%+(kuo& zj2+edI>~vUTICpBrLrUM*6KS9z1U{K_|EGD{l(dWJU26KG@kAEn% z3T?69C9qjOz&z2>B-y`i7VS5Yp^|{z_xSg@)A=#{w9x#&j^3{ob*Md3(B9m~G_47W zZp{%{SFlM@4XmZY;YgwZ2V3_8*re}PDXLzu{0~Ae`N$5xIAeiNUX8gda?YUdEV5U# zC-NJ2zl50#^Vo{ntWTUr`_^w@!isc7(~X-pbXQv4VJh6h4D{y$I{48f*7xQN*3Vaw zf%ZePe`yL)?AU90_j5KFLq+`_!ZPQO-V;-m?)2Hpt$XLw?hN?mBTtC2=c7<_bdt> z7zjN&z|#*7vbVPSiRqwUtSRwR};o7#_-@x?yqL zI@P;ax6xKi-JUuY7;}QT|A3a_WpMW%KF%E+d+~x()ZPi!zByV9+__WZ zI32FCeGE2PcR0~$!DuZ^Wagi;6#Z@VvCzudUffUJ)pG^Y#wDWa%Qf5-77DH7PQPW{ zE^e+46RamAsWCN?s8aI%E^iUjy|;~M6Za9-nj=j2e4e8Fyjs!DZDfYeF~3Xr z``~`>Njf9AP1u>dfu87T)TZ@ks9SwT@r*d5cyF8~p2y&U{1v{1woY#O2^kuDPjcHf z_(_m&Yy0DbV15@|+;lvzd*HA94Ec!2Vz$i3-e9njSvs&+-M&`{Shh*!RFDffWUa>b zza1(|f4JnGwVw~2vX2joN@ebEbE$nZJhMM$`Axkww+*L`Fya2aTHkZ=%rFU>=6RR{ z+ghn%?vJ3X-QD-=G zK9A%m_R(p?H7c10ypqdYo*L|Ez*c-8evWW>-d@HHZ7%c-|9}IuvP$rt$YO!-9ObSF zF@miDS#+n$nQ?wAv#RQty)KuynzM=X60|qJchG>Xtt_Cdp1Z!NVUD$B%zhcS{@g0& z3VtgHXs_Cbh{T=MIwq*;! zV@nmMP}6T3m*=xGJYQRBm|ZoR34-e-gX6YsmjAZjwwJDv^Dh%5@1xnEq{K z7)2wmCTAZ}C+(q9SS-`b#p^VoT-HW45bYq?rQ?w=p^5Mt-Pkwx&mELQ=lXnafh%@! z0l)d;Oz~Fh0^(b-koCo^pabGQs@a`Rb)}_56Ozo-Q}8+(mr8{f_7S#sF7?ep9co-c z`>yR%{I`=(QzAM0DUNgKpoB%c;K!Xs`geEpBC z?ejglYk+;V!zZ3tL*+|b6?sy;EUeD(8*B6YrU~^#(|m@QzlYypE3&x`VSatAlo}_e zG3Tua&8TaOT#air~XuG#UFc0acu1(u6IunPfr(j-UmM|vW!h%V}BIdsxlSg z`#tctZGs-h*s8KbBCqBmG&-$knW5($DoyJEKemvVpU9xD_(&G84H|$Sz)o4dh1WZ9BxmFzn61b_F1JrgI}Uti-_k3mmVJkK|vUp>%g4wDB`LYe=O z?Ub$BtoYZ4%d{h%m^f|$fw&o7OeGpH!BrRXamG7JR1d78v=nt{)I8Q#v6Btz;;1q{ zhV!jiOpFIZ{cdslx zwobfl*yF!-HA?PPXUGO~F)_?IO@i*HYOehh*(8rwsEpfNMO{#rVo`8oRADbv zR4ZzVE19(C6gBk!eMPxouC9hJ3LkOlX~398=!W^8So z=)ApO44fPz4E-`*7#f;7bnPSy z1m7|6znC*dVE6IDamjsGi`w<+akaZ0{If%i$Pz-v_9^`P*FbOZ;+4L${i+}yK*D+Bt$9x%_U;CI=9 z+kIsrwU=d43yET;ZQ+7pLn5~f7E#ZO$OJL9a7W1r?wSQ&|My^y9I9ZhvG91Lq;VTB zV#edG%pBgytf#ASb4=sLSuuifI~aXEMbuV_Ojr^tdiSEXlqPb+KQYYk7V;dFD((oa z$6N(XR1NN*4>SngTUFehfV*c~g5VfT5L~WmaA?jju#-o4y@nu9%h*5+myw_I zRhwdVx60OzD%r6-jX1-%`Hl6cLzm|d_9@$Y`wl1h{6D4;dMk>O{)2vbLxx}YutGL> zf%z6vOAHZtsB76&h4-9vDwAp^ZKTp-Xsd(g@t(KC>EOm=RGARR*@8?at}Z3&kMb4u zN#E%zOt$8H^2Ap9$DReGC}xSUu@*>9*pm$1A1x{Rn}vzT;o zgFx0pC;93Ea&zoba^rqzr++{l3SQ;!-@VyC5PI0BSkni^TS{g8`-zSaU zu}6K^%5Z7Sefy;c@%;xdPmS0YsvdT8n`Xp4TQ#F9cdEvG6)KH<4EfKeH)$O88+6v# zP?e)US+Lw+Kuow%TLOyV6+<2RvyQu_w-UGVs_a?#D)GF4eQ`B7W8KK284b_a#AeZT zXIDWAxcd?^Y-)B$rVGo};ybHUG;%lg-TAUHx_&@+{Oo|au36UmGb!!^M4wqJ>qpgr zV*;Mgv$%_o&iARM45G<{Ucr~IXj)1X?G(&@`U{2hPUEM~^x{=_tu?o>sjoh{6xX(Fb zsd-HTZVlM=e^AMwdF1XourvPvPazET2%b9k%5r4Gzw3LpVLKKe~Mt=0=7#yX2A=nn{QW(o{?b2rPO1- zuHv4R@TJg1!4)4Pco&v%*K^n-?L{5h1fN_k=CaFRm?U+Hfn(v9y%$>99j8S5c+{Z} z;cwXn_TLVC_Azk2pUI=Pmt&zNh)_%yqh(D&iL836Qqlgej_TD-%=j&C1eeNXV_OO_ zI%1fkb(?HDvc#v@7WsO&MD!1?${Cc;XDf7X43Y0o^!q=o@=F)M1JfbjGai120o0Zq zB~ms$VXbs)oc?#}MLCVE#VT;D zADpe+4w~y9+%{hsNW!lvTrKoKC*>>1<0Q5e`Oif}{V{IOxw%v&ZWX9{7Q4M1Gg%gV z9Pch8J?iC3@6qL?Z`mgNSy7lN)BP%j_n?%esGrZitqUyc(Jv~ZIuUz@@Y9NICEkn2 zPceHFv?BL6G1FV{W)5%g8yuQmO2iC{c^NRaVFw7>NWu76Yn5Vy;|9>{oUFbEGD&*JXD?_$}S=fJo z>$AeSSiGO{Kk=EhE8nkAXc{!m>hf7WK2Efgvxw&JXkvM_P&SQdlg*!=kd3R`2Mtf1 z7&L6ICpsTACO;JV&8@k9$J}zid1nJY_rQa^|8*LW{04K(JS!M`&WL79tIBGH2cra> ziPU3K@OP;~@Zw{FS+hfBeQ=%Ly%q0!J$CqqvIOI-eCAk`#=WC(4|_jMw9ekE4$j!E z4%xY0?d-?h>Pj5<+?`I{jk(k_Dw}xwps%jTAVK$K(|{!W*oZOVe@Na#JI#h!k z=w+o+V04D)PJs8OH(#{ATq4@$Ak$-Hgw|<}Qd{1^-Ql|`W)6m*KQ)7!$CfbjnI_SG z4mTz2l!ALth#{UPG2mlpi(Fvct;!|Fr;&Y)+$ZzBQONv`(8`*pmB@5S$D z#QyyX*fVEQhiY26P&7OU&w9g(1rij#x0BS-Y$oq zV{|;Rrx(ihN85;JLksopg$C|6p3hWxW`8RaT*x7CfGOwjSBTDK)sm|S*#RqR!~p2a zJTIg6s8YCFjuJfAa=9(9Np!S=6Oszu*gLqp=}?P4$Jg=vY0(n|eZ)s#K70Xv)Ni;? zu7L)r6xnKGi{RP_md)gRV*4{&F)`?8wiYO=PYV_G$`Yc9EoVAy0n_^6g{xd6O0!mq z+Q*kE(r=4ppJ}OlYsrxTUrn)(zL~A?aj}Fxo#Usq;Aao%AeIBix#>T(T$fZ$HK7$m zYC|3RC6gLzR|=}Trm(?n3wZC8NG8V=5MfmTYE&+h+A&)N$5Zi}-BkHuHtih=PweBU zLoIXt1CJm_I(mU}yJ)#^`=R|r@n%!{NF@;p;pZwx2AHLQ=of}@;mRD=TZ?|^>>}k> z`$F1NIiKBrWj^nDXqnXWZy50v$H^l40K-=7kyCO9jV~1V^v5ch;XUMIy@DIa5!_|B zw{m?1H0c~{)L-hjrU7|P!;54Ce3RBoc;7A9s~$coirw?S!+hkc2Zdqn2gR|M4oVNr z3)POg5~}qk?9mK)D_puKW|wwk@E+tU?^O+R?okgl?NJTcxkoeNScH1ysbkdnEb1q? zhnnBeD{D%*c>;Ds*UnLABK#!a|2j&~G0&H0Sl}7tB}{0POfg`)-XAB9%#Y;Gal5$j zm%~gwEl&|UkrTV7NY+}*{f1k$(C$}J$%FfVv=1I5$e?gk;u8NmK+)0XMXA0MsR$*@nU-x+IpnqwXw4Eo_)_cH7)a477m^jHLB#Qwf zQv~;dXwm-iQmvzHfzJHVdW~f`cEM}nxcgKVG9+_|<)LuF*g8wpH80m#yVqzO%4W59 zeWc*EU{<=BN`jtAXT!H-@DM{T^DIbV!));^_*OIz{3V_T{}+4x71_-DFgOC=wL#wi zufvs7La-VPo|)M99>}DYwapO4L2BC)zjO`GH zD6Qg33iC#VS?kALxQC>Q82e+-y8v^Q8@OHzhWNy zs|mZdW^UO7FX@ZW5KV`-V(MwZQ-xj8x6tg|u9pJbxkAwK1kpK|!HsVf5#uRvZohA# z&Zsu#1qad_2L4=Jt7zI-C+I7&)345lrUKb;b2GSQWdWWuKKH(Yw{K+wv)sbx>qpSU zhL_6r-L2GH*vf*FT3C>;lLvZQMeq0J&<-H8p*~x%-HU&hEx6lls8e|~cq~Ufnu@vd zku2^U8!tF_L7OoI`=eXfQCFiMLiTll4K=8zQ}Vh_N}gAs5nT-pWC=7{!?1t*8lE-y z?3`s_{(lO_+lSbjBQwN+y}G_JS5Y5AEn1EFdutxie4mPW2m7D;8N5GX8uOi2aycV`{6ql@YVU41RnJc1hZa}iT*N<;S3 zN-ibMpgzkY)^FcW6(x&^>uZ>1WEEFGkjGR*vZ(OU5lTCj(1EkF$-s!Yq|br-*`)d8 z))Qbvo>@iuzuxWVU73WxU8xAyE2*jmbtDnHE^>gY$UHho!DQSFKmK3y=P%dVU|fm4u^tSfM(FKF)>Ca_rL1|khUgRU zcWZ#o=GA;fyAayl9m&+-{LJIe2Mp^f$NBH+@f*y(pkoYh9e+{sP(*C!YTzu)(f$3Bmj{4`E<3 zFUsPc&4-}R+b2t55wh{2?TY#RB)@KJiBI=YuFsejJ?J_TN$l^y@A1J7YP21urt~=G z8kxWXf7xfV+_l%H{{{Whm?P9wpUCV7vWe@{8tR&doQ*!zq3g|R*P9KJ$x_9&1MtK` z>!WKy=Jz_}tE>mV?%g+uX2ySH)yB6J!yaf{#~`<(1wJ_FuI<_mA#hz69~#yw4Es-$ z5bUS{TdjmS_hNt4oJZ~HN0~is4|n%$ksRyy3-+c2!SY=SbG(p2?L)yL+fvWWPr*C# z7;4cSrqCta1b#owJwL%~a-jpa3+M^M@OtRW#1A3=m zWTcHO6N19P<6oUCSXLp&HWqW1kj-qibZ(shFV8{bTt3puZO}hkzQ`liCF#We4|4eK z$KL+01~&B76Kv@9lPn|>xg+ltLxY(j*+Mc!)7}cnRsbH#7-YOXjEu>T!2bUN{n64Q zFb%SqeH3;E@u-7--1zM9rkL=!2cILiP>0YTc^(08TZOtBj#?Cmo@fJlBILYz{%n(i zzlGK+4_R6)nHg3^Gvpf)T}hduHiKLOa*}1E$0*+)U0ih`Os)T=QWmQ$JtGR0mSH`0EBj z4lPs$>eecQ7sC{}H-m_OfIF&b;F?E~Avv>@ii?g>`r8KL|6>mEy+4NxUIMqM0yp_6 z+~zi{#C*2P$L~iS`mkEzgVor9S2Eq}V6;CS#U*%geY4>$v!N#npHF)hPZw^RC#!Dz z7Kk?;N93NILRnUWO_`5<21CzN4n2pbQ8c}X8v|}2w%kf$JplhIm^L~*f8!PW_wp*g z`Yr6xkwvDuUZ`jcC9=s-E*pm6d4CCQLTs^Y|4)%(_aqR@xFlvg3SE&WQ5CrIfHwHa z{hEN}y}FU%hlMduhN}XSBhX!upfLi2lH%- zV*fUi2fhUF`6rQrDQq*hez;ZTshF>|-akj9xwKNHsSRbij(Dasr%}uAV8mOBh-G*_ zHC;#&O+W5YS>so0j0-oZ%%2<)15HPvM@W|q{jJC%eV1w;?*bDqRWbBL%C-j+{H71% zs97H=m~TW$=F9MLnc&qJ5vj7i2QShN_#)t6bX@{RXjd{dqvi%Y8p8uz2btwsJU2cQ zM}}0Tk>PJ;5${##q`%vtvR$7l8ItBnl5vm5Xax`ZYMBr?xtO~@sNkM&^V!f}bLp_F zseHuWDSYVZBpv{LWxyZdlDlM!#_`5s!E+8C&OchXHwm7C`N;h-gQe8as0$dCCXToi zDq4en2#JqbaF(<|n>2j{DYR~i=HYE%?e$19FGfEWwPGlHdK8QRD_;ky0KYR9& z&UAT)-tbsBF`PdHW=8^ddU5Z%A9KjLb|Ih?xvSG!Rn9{#k} z_BXrF67%xU6hqlpih0z#)UmQjuxvy95b~(03>iIBcX@PxgHo+!3i2>Q2e^mNZPB&d@-lj%3(#SG0{v(L z{9P@W&;7WWf!pNy4LYKfN+EDT77J=fM|M;NHE%f1tUtFf$Bj1bI)Gi*V(5PIGWgKn z6T~6$@D460;uh2^n;W(0dM>vgDPVSOHM4cWk3SSy9PdCQk%d3ME`tUPgngug@j@@+XwBs0X@+|XtP6`#gMs$bZBTS zI1KAm+Mk!Jw3ng#UW-2e4|wuJYKXQF`J?0FWO{Uszt1sO=}np^_dl~p_IoxC`ic+w z{PqH$xUW>!4hIixe-%@G-6HAcc1osE?UL!CMwNa}1=oCAz$ARF#DH{0zuiY<&o=+y zjxfJ}?rxv*O%4%e*D&>aRkHLzC48s2gFTx^c`9<}JK?WQ2D7MVn$ovypXTxPzyCvaUcE>S%D9r zCe1~U7+63g(Wg~6`;2ci`VAjIf3y+oMOH-&nYf9Xvbm)p zS!KFAMl*C#3cc%GJn{NtB?EdW>+{93*<9i`LjPoG#k`8oX#ID{$q76lx`#w_>(&Tx z@ONkeEL*j%&sVE0$CioK#n4sfE>vqQt2DZby@Hw@R@ApMi8dyc*~T0d+<{f#;3F@u z5Wb3BX#Y=#3$}G(qOB!E@`yV{)5}MYr%~?Lo6q`Hx=KaNOW=m6gNkcSs$aJYyX&Pn zOuD~-t2u7Fr?G2}KE@3TQ@J)8Jx&#v-6Qj4<9m6EF%b3Ukpki(S;TZ9gX$a8NzjU7 zC3tip@xGGJtPw{<*Q@ZJst;;R&m7X~-bRMe7VI@w)rt;+$2Ra%zb|D$o#}kU+o?iG zS+Woqi(c#CAt~UMEn3f;yU>>;^N^i|Ea>lQ9&!RZ`w17Ab85Zbo0u$)Jdd7f>Kf6s zaJ67Mwp!E%Z51W_d8)QZroE8rH@;dc8>XK??+0&4N0egwErNQ(`a3O+oj;xc9mOqLbATsA{yUl5)4HR-1tvDH~xk@dQca$ zG`zv=U%kaWTi^x0!wnhH%mJ^5w9P zO2u4%e2-v%5gx!Vk=67?ED!i84q4dftGjclO{m12hdi`i=p@b1sMnmKp2wl-65+pX zKpri!%Up|VByVy)3v9wp?G~6GKjAKxiXGxZct2ZkE4!zV2KFQO1if>>kGS)i!P6az zoXBW+iXXpgKaHYgO|59&jh&7UJyLApom@;lBA`eJDmX5B#-8MXXTX6B zL!Z)C#@tUrNAXnwcl-fe_rW^kh19Eq^6FIquhxm4C%`VL2cLg!9d}%B6r3@rhwCxZ z-9o1AlrAA;Y?m191os~qNFEg!c)y%N{$rQq1mDD=X_IV^*GR4xjtL>pg^CvKJdyt} zLtqsfWzAJ&Ad7{v`Wkwohii!X#SB@D+2|v>*?!-qIkFG;D0$&Z|G@AAKHoX$kx$nu zy1BJf_gkHybu_8~%H@G>-6Pi|qt=(+UPEwJ*>%%`_&rwe^orb+!XH_<^)jLiQ? z^=Ug&WMez-l`j?GepEyZA45|%1YVgx!6*A1Ud35QaBJGbR1M+Wd}crLxZqo#gt~OS zfT)I+E7GlES?bCI%cR_|`mWKhx!5wO>#O%^i@{3U*x=Vb)af_;dd6?r0``6)X7tWt z!R099UK4J(V@jzeB8M27^JINunrw-RC9a2|&l`0>FdWz`+ARl}s|oK9+@D>8o7J`- zH;Se+Yq|C5m7?{+GC}vmMoGK<5O=J}V#eYe{JVn@Fgu-@)w-o(i`c$_g)EI1QoAfN3o))R{<+12+5iGHs2pvu66HPAvrncnWyai+ZGnO&g4eM zRDzs~(dbpCw-RSuEp9aR)R11zdIsu=@9}!#cET@v3eQkC?$X6sg6%ByDT8G+@C5GQ z$T_n3knwl5m3QrFAN44$V$*F4k(j+Axi4_DZei*f!BynM_7i zw-9>>{76Ex#{LnQUVp8Sywg_-wkzSRd2kJ{U9m>etzDwkrK}fpDd~(hzgp4!0KLM$ z*vGddvzCll)|HgSIP;4b`Q^Q=H8)W;ThmnQ-?(M}QB57l^>LmA-{%^3%;}gVY|#Ej z!*2?Io#}66T$s=+$9EB1CvM$OAltg?4NevgagORfYQKS+VFGV;GPnbii>P@#^wX(b z*x_Q|WI`Qs!`~s5u}&Sd-2P;0zP6pSKevIiSAzk59Xqx2Db%?F`~wG=Q4=az$Fw%i znt*@D4h@?hy^!ND^;93`o%;`p@;AtB-Hc45#WkFJO9}DX>Zqrtk9xo`2{_kJz2&%- zJ%nuPn<;!?0&QH37_!f(lMPS;d*bMlz186^*YH38t7b)_8w~ z-)=0SK~g2}zNZCRcibzt^>N;#9fBvkf%gQI5ckG?)RS8VO%ifx`x`~y6tF7I(7-*8 zudAp zzlvS>JMk)gJ|6cq)TDC-3ctKY6&|c-#Fy$>X=*v)C+`*1y~w70V3U7@gpS-eIhXm{ z3+?uG)S>Z<6gn!xuluElk=h%H^kx&)&w~zPYzt?0)Zj)N&1!egWo``3WdC8nfL!x3 zck}*NCI6}QoZpn_7e>-YjP-?n=adT7`bUXkco!^$VB8JPb`yCBTC~Ab^tx*q?WOg| zLfOHZo{MI!cg7*#AdS<^DrclA_&GmCKPB&FBsoXbZ?06u@pXRQk>}Sum7yB#d5XTH z-LJhdIAXF6DVEh8$e@D;Yy`Tt@pyKA+0Gi#3+YNT6>ZEe)x2;MYa6$NSaXqsb6+%T zJcK#t$7WElFbnjpmI+xD6+wlK4U9f3s;Uk zLoVi#c4Q64qvyGV=b*Dg^o|8HXF>z=6B{_^&y9T02=>3RV1GRh4R$DIht&4R><%(& zL73ovbQfa@O(8nVE@VohhTGSQ`j59!?O+rYKHNdM66_k@gxBHEWY)B@P_>~-`x5N+2H}h9g61W*jdveK|8lgO^96PD9tZS`?{xB(hv4&!gpTLkPHO)Xw{UpN ztolQ&<>V>G6mguf%;+Jew~#5_m<^uzF42;m!`YLNWpJ*G^DMz^(%gd2CswBSPgx1@k#a^p#kdBgixPU_Z9V!Z`=j>iFz@El?9x5e{ z_wnzqt0As{2I~EynRE9w^0xO{c%!zHH+@tmn68%a#>0ig;4e{ae)K*@_{t($@JxfB zbzU#;c>{WA5qXdglu++Vc$Jr-9z9ft+gH8lCUt_dsFriCsH09la<67L^KvQf3&m}M z!-2WXIlu>B=of?zSn>=)=P~ z81>1>nkYYOF7Wf&C4Q-qJ zIn9hp@V(pMdC6s^jLpP68vdO|)Y!MORdIBrstwr4njeT~4Wt%(%yz#nv{^NWV>fUQ zd;`IFp56mK^?YzwR#y9UPs3OHQH-MdWs9QQ8VApDE~B53p@<*nQ867ph_u7R{v^EF zFJk6>IoWSH9_yFmqg8Q7wyJ}d$vzP~%q_?ZybGQuPZp>DBA!}SZxZCkkrf?UAlNq| zpKvs?No`=0F0A03LLwKGyGj$(x>)NmFV$G#OS28UMl70{MEeCiN|ErPKMu{tBe-{V z;fDMVZqoPSZ6Tl9M2C%xqzXohd=4{*O`x6q&^Idt5vL_T0~ zEblqD19fvJb!ZZZ*_qAi9@t1M%ja?S`C(v1q$%Emt*ku%fa?0GLJ2TtLYJNmpJt7s z3*Dz!|3-dBU^F#OTP5nASSU#U%%Ym~rJ^Bi4R1J?%<3!3!SQWo?3bz;c?q;j&>Fb* zW}`+zBcQ;$ITK&!+d1D3=ow}BOOJu|F};m9Ob37WZ}@=Ia8JJ!mz38{iHEncu?<`L(A>WSy)F6teFUgF0dXtA*~*;~N6Hm0-A&?MIW6~0ol z7^?@|vG^Rub0(Q}*JQv?QO-D5K-awi*(B>w?~qMrwZrp11KEt9!UO;D5pWw1^7gsV zyMNWo$??eP@Ktm6>D9Zpf9cA9NCx+qw1*d z7ibW3k#+U~GR)rWp_XgtPc;p^>9$7B_(%tDX$5z&yNB~E$3Fa1^sMh=zVdYNp6o_0 z;CeCjmgN!m73hV2f^QD9lw(7^fSgdykp+Ei6P`I|n+3TBcdu5kP96q7?$1FX=y<;v zIHgDQ9q1Fh{(ixI9((FLp@mux|4a<_)b?({?nTDPnL=WHc`q?;iKfEYEez9`G(xRK z3ZDmNL@#u!-+&ph9Wzr>q@NbdQ%BA$RFvFp!}>cD{hELzKPQ(e{6@@fb30hwEU-#1 zKm*4^i@OMV|A#w8^TVxzIkkqGt>!IHfsNrMh1X`%{4%DGS zR-=P=xoex?TAsk!Id}^%#4uXNM#g+GnlXjut5RQqs@V=ERzf-n{5+p^wIMHaZVz#k zHZ$@MxYy6x#|YCh{JcGFgf8AUqB&mSmmVrp=*u;V5CvZUMDR2(?`N%{xaWS8s9K(Y zSFs{j(Ht#PbWzZ|MMLZ3g1@;Q8eefAYdu=Pn2yI2%Y;nUzAIC3rq%N9ZSW1)+C9KiGYXou+fxsUhIA!50Ikmx6Oh}LH?za-bP!GEUncT}McZHyA!BKo;0v5aeS zHe-qggFPn+Jha#0=UoD?c@*OTr_OE|P~E;k)opGhEm1wPZs*xc-A;= z9dBw}$(v8F!_PmD7`36QVdh>&S6QQq0odbbVGmQ3h5Y2bocot--r0e>^ObhNodu@F zWq9NNMeU~81-qbkENmpYZSCNMfzheyV=ZrC&VH;7JXLt@XTj_AMG-6Bhv!p93TONb zthqn2M+t7_JVFok#b7r}!MeV$S#TzUy|e{<|2g19)<+Rp56{o|8b&t5pWmBGJVVLE zy}j_(dxVoOgQxgCYQ(+Bh6_hN!1M4!-#kj)cfCP93*Zwy3bx}?uv#O!ILF7Bu`cBZ z?lGz2m=_a-F)LEIfI;X=(=&PZ${gM&l;Yk6?crhU1fr{{=W%HAy~sQJ9sDu_>fN8+ zygaIhm%l(gIE^{!zCqse9`q4^cXHkfjo>`Or@b0=wY`jbUqJ5nYB0-d%2>xdc;-H> z<7}(Jr@Fre{$R}DKY}yr!8~^c@3|kbGui=$V?VOk_VkOMZD4JWMjiSHe7T?YQ(p?Q z0zSjPnFlT70_0VlZsKg$N{DSqCbfj@=5%AXvHaEqg_~8V3MX<^%}dFOcKkj?I$xp) zMbQd>XthFK3{wOn=Ic*NRU@CLXeQ$BexjJr_mS`WSs&Fs3pRfS{EnYu{`?zUj5 zSXN5KNojq>QK~cm<6}q)Ph|m-rcU~9DS;BxRdDq>Y%z8dx_CsO{66e zRC#kMdu=;dB=JuR|LmM9{QJL9PAMqz>n|Y7Q! zE$)jK44&cw4I*m6;HHaZQg_xK=o70M^TJjt-v#|%$v#d4-a2`*)K5=W z3=0oe4{Ps*A0wk?L^_NacN92l`=O^)k@0f`T-TpssB>}*HSdEzV^XnVLI%0{HTXxT zR`a&TWc1;gtnJPkMSlcL8zD!v{g%%LGVm)#Ad6~zmtdb*D|&)T_yEjqAzN2z1NJY` zd1frtI+raLwBKxyOyFQ!UjZk!sRaxH5XIR$k3;LuCvo`iON!a zV>4Non8iB2N1pT(WxVf|a^6>7OkFWxI?itwoM6NILfb@-4c^91=x2W(5G+T)eE4vP zu`N1@e8~@30w%N}12e?&DsD_is(6POF9yF9EBK7?rRK*I&-7f@hU_cTCq;hqLmOCm z)>6)PG@RH@r!w~K6{@QVS?sT({*)h3ov(vQT3MzTzfDkek4Hj}96>FbIMucpySLn} zBXV`ju<6Y#MSCxL)NNqPu7nq~7IVo0P=qe##<-FDe#lRq(pyMsU`n_=-UGT0QbHKCh;(kF%jez`hzhGuL}noc&B2 zZ*9fS=@9N^VSQBo7PHw`*b)75jC!vegP!vcW*h8T&LU5#p`LfNJz>B?hMHS~R#g`6R=mp?k4RvzzC+JBtdj%)kFE}gl{XPVn z8+sp4GWxjX=v8~+9eW#DNE^U3ekzmqJ(P<3S0;5@p+(w*y}@YA?K999Pr=SR3Uz2} z4FyY>^WFj9+1FLb8-gF^7(8WrdnDKY;FDQ&>wY#s113P1^nO2eU56$!3VQ0v*vocd zuhdpbleduZK z1Osvde7(WhtT-!$CksnB;%VfBk{-@*2Ku3Uakr1I7mSZraQfS`Dfi(nX1FGKWW=}U zx~@6;roKK`;l9oDb2Qg4{V$W%?oT58`x)@T=Z~-%l_SCz_)I>E`uElzhTXkFy%9W1 zz4rL4>a~a&>dl|SMkFaq5woxtzT64jcQY0K(?o@528iLAHmc?Ju*~PLve&0S&s~js zPWWfyG+|`^T9zBh_Uq!13p5{0rZ@X}OGF26`lXQ=z!5SWLJsu%(DK!+7xlB3O8le< zUK{|Q^=agGUdrQaW^i2tkWU%eqZ+1yksl7e;n-M>mPF|M;}W4usPgOI*rymiUk7b8 zb}zp!;t)8(>y_7eV|o~NyP2vX1KRbZJ=D_?&1u2HA*(7B?m7IP{<>j(G;%m98gR8Yc7b7$t{T9}CcDrD{ z2fK^?U^Og9_Gb}x2zqGLzNnUDU6H`=KE$en=TzoqE2DW5nzx=J)qWpXZ>w{NN0&)M zTA*d=%i}NIX0{v_sxj(*$<5H(L021 zL2vwUHRm&A@niE7g%EopKdL3356Dd9d{tnh?XG1k!>x*OYqsC|$~M)p0L-u2oy0b{ zi}T!*My-3QRm;0AEbcVKnN_SfzQqmf#xiPDSMbKQyBPD59KYqy%n|z-^i=ofDn_|V zxs|Ng*fcBwZakudB^9o)M zJQ!ENjtz${8a;u#3jNviPF_YPqKteJ`9QxYKQth^&I}2jB?G)W5G6U$A0D#IT-LD}TBBg>3DGlJ=?T(jzU z8GKtu1Nyh>PSIHcCSE4y)HTRNKh?pzYM@`_z`Z%zMXhnDL+2}4$6v^ZI|onBC(unt zrK@~Ojw=2L_T58ZdhwfB^MhMi>qqde=9H`2*gBmvNStU5#Ouu z>$OcpYpJFD4-Kra0xXCtUZh2ZOuYV3&B5?KeyP(B+bRjPsnJ-Yf6+KH5Zqj9p`8M1dUc?>X-!9ht?to$n zZ&Z!oN0_I96Eix@aP0%Ilfo-U44aA-?S@d!z|Z9LPtGBF%L1akJew6K&0)lgD;dMh z9mIKRGoC~11%nHD>?4@pW`G0a#J;FIQ!##@&RE*uH+vPC>tEvj`A8G*yqd>(GIsF3 zhd1%g<0-0bUZY~#)TNpx;&Z$?m2>{SN%U+8Ih<(Hx5Saxf_bQ1Z4 zZ$q~-z&ZBS^738f$Sy5nE#t6@`5E`Az`dMq6#V>E1&rhT0C9qE<%z`mBp&b2OHGpP zv1-9g%cvIjYhElM(zQa?8U=5;h#bD9d93&4eQeNeC9JC-o@?ZU*_ul@`|dQ!RhuW+ zC$tLo@35CluMs`yhYd9iis(40T5o_&U(~`&;n>Sx&fz?T*);G%5$p98vc6H-Y{0P; zGAb@w2)1F@vKoJ@b#01yOga1~NyKz-6mg8K);V{s(K(9K*pSjJb@aozO5nsy)l;&T zSe~3u&Ce|5jmsj4eff6A@o9qUoR+S*y}62QZjoX-Q^;E0#jdyrnr!4aTaa()xVK60 z{M;gX#M7HkYpJ#I(ZMrwT!-q^2GSfd&V-BBEI$3ssP zi~a7ECeBX5M1hV(egXZ_G3353Ltbjm0pgg58uTw}&@+cQ_xWSId(Z!87znP(_-r>} z_tw+FJNg@_V`eUKPXKr0;WQesvz&EYfnRYka;zJ1Gg#8XdnVR!&KuR7qZd7jyH$`k z;8s2~DA<<4`~D|xa3Ad#o#FxBGX{Q)hp?yr3VUf?5q0;aa+bRjsp+m*YQ2ce-sN$e z{5$q)$8%ZFgUCfYQb_FKS-gFBDR;{Q;>w0MW(qV%_drwqO1oe;BA;&K2`VF_792I~ z2RcPL0rl}exZ8p8=xhO->X%+BuWF}`{yNsNpcp#^{EeExDka=Q4p zs5Z1;FdQ%8wV!8jdUFEUg)zhQ(`3K$Z0?A-s%%8ER}4!}H;icR?PT<4T3Fo-XmgS( z74CYqB2-r^;RIgrWmNcY zB_jscGP<{O7-2)?2s>`p@UZdVo<;^(+Oe*iDu)K1aPbV_Ea zOE4~L2>kjOZ~NiN2la%Vtw)z@;GA!w4n0{!WfyvYGn?V(&QxvX z9g6h|=C%i+!EM+HzvC9(@mx4^Uc;y(KZ0|<8p(S;f}ipE2Ep{n8=T#FlJ(3xPV7eH zEJi^C^F73;xGQJ{n7}tYE@D){Yfx1rK@%&-V+*>PINEL4jDyNoT zOQ>l!xLl(OiTrI5>(*f3^(}PK;|iI;>E*2R4Sa?^%4a?O@IWaQG3*E8}*m5OUosSuTb>g}5CifEBhDJN!eP&>1xma}<2dx;V!DCG<>N(};UKUblBz zINKiF91~kP`E(cO?1iuX#I2k!Fj(^8EsHq9xksTF+Js)n2v*|C!-9L!0l~edM{wc( zB|ln0>8ZtCbnZ$qG$NJNcTfZp#D%w+d=2sVG8XjwTZ zTKfkiTjqddUyM40{CPLQ-l-n*^!x_FSCq~9TC<4xUA(6sg?7#xPxQaW@aA`-dHFlM zAGP>fJOW+)xG2H#C3@jMa3}i<+J_ZAyl*aUZRdM=*UCe@oNn_)*(!ba#zE!K} z&@1RJW-8pKt%~2W*njQpLiOqwE7*Vjh+);0rK$*znC6ozcrXg6l#@fmui+p6DTC4c zcf&BdYxYg$3baliy~JNX@v{CpGskdkTP%3D$dq3W&S_znAkV?ffSaNP%oy91PVmhO z8N=)-R*c$C`2QqQ?YMNRPX>b+K1f$ZBXyr{r}9PIy!yeAszA=r))ZC0E?Uuh^G3|S z_l}tBv9FTz80qu|Wi|eT64EV}rZ?Q(`L-eoi zqqd<~A+R-;_c@`F8V={35j(_y4PcIjHHp$~xsr*m=1mW^@h0RD%1g?T^#}b(domw1 zHzI(H1b+0X645lLRSS-UCP>=~hg$D6IXzbaGQQ;U_r#|s!=MHUy3k{|_@$7x2tyPLax zN2Tgn+d^#VHMn!;3f?uz`q9@5G`^eZQqZdjQt+M8n&3BfNWp`VQoyrG@H{1oL7%1I zZk<4M-=z3Wms42tIo#F~^NFPZ`=M*dAc3aAF#|Vg=%)i#Av3Y2Q}irqlbp{sYaEVR z!IfXYc%t$Yd3%Lodo{c@bry=jy|Z5HBW~RH4ki#PX{=81RU;Hp(VW0 zF1T>N_Eh3)FK$g{u!tV(=A5&^ZtSf?CU-U?M@JFYsvVq@Vh81ecJVvt0#bXa^G>iJ zccb>m$ipbb_m0GV?ajlyqYznQ14p4HIKsRBIm&x`4hsR#bi->1w*L3%iHqZSPv~Ci z=?9}Wr=7@^Jp}n+oD8q9Tm@FaF7#+|=<`I}<9+CbLi#1kbMOw{iy9QwFUc{eLmT0_ zco2-Ee&ie}^}O%#bS@D2-6o}35w2mk_HR5f<|cB+&{WQFK8c!5@arDkjQ7`O(faW| zPT$uqSZCr!h8wBxN9=;`!j69VpdcSTDA>LoLXIBt+k(19`^orI8GkM)T;Gng|A%kK&YrnFVHz!6@C%iJ2aR-V0SKJZb zN3Ovcc-1EKV6HL|e(u8W*vFabS~;T?9{tuvMwgCUus`acOUm|(XCnOS+@-_A zZ>$;_p0bfqn)fhdSt;~{;COYGv6`@As+pfpw4)0cZ9uvzeGJy(`MD$h$qUrs;MeHQ zQ!i<+KR4TOBWWkY-h-UzZ~Is$^s}yZ@LZrfcR*|9o{Hzk!7A2RlFsOd6X6j>uEC-V zX!~)y{sdaL6ZO=q!CikWo?GR(!M|9|7^|`W+*mkln9?+CiSGB?U+eQ*L+ky*9%fD{9rG64mk}@}2V%7>f~J^A{qh>lO4& zXE)m4%iM;u+NYV4gR>{+ph@KgoqtVbveF&ycZ!d3s8o5Ou?T~^ltk*cl%;UAQ z!Q@YyEt`;lEvVbNR2BQI|EwDf(@-2|ExuS zhWhYjDeK%+?sxuxe4e+kPrIi;GSND%aUVR$`%yDJ5k{wXh0ZD=1MTn<-gfMD_(x{( z`nXk+C3Pno5L%#kZp&3eo~&Y=`f6%huupLOcb61=cBe4j7hpdWoki{U*YS=VHl!)xav5ma1yNUM&Km+i7 zFXtJLS~RwUI=tXDO(=yf1lpnZBRG3JbWgKOSgX*AeNH#$dKcae%>dZg_-thl;(Ouy zv&j7R9OLDIV}hgLsNj0$2oEkM@9XOnyr&C^r+NqP-4zaA(r(@beT=*tJ<=PP*S^AT zaS8a~?l#eJ3G?P{4P3w|+y;7aCwF5WyEv$^z0j|*ukMo^R|iE`-hk*l0Y2HzZqa$6 zjt`oW#)Wi%`IMhVbjeA?`0*BMJD*CeAA-R<6P)(Tm~B&b;l39q7@sNTw4>UPi-gzn zI5M%9^oZ^+kwtws=En~Y3UUavi!0#cSkxogNTX!&=3aV4s(vF$VvYKdZa_B zpLNIx{`{-6;divq`Fre69?H#c`T;bP-Ml)h)7_0jx zoH3kSLZmll5h~4N)UY+YCVmeS_!vIB52JV5R-tk~Ba?a@G&{TV7{jN~WhM`yCL!~A zF>*6@Lr45}vtI`fkpAiItR*gtlWSsGM@)`tgeKegRQ8B5Bvv(F+ySj2m{{Wzh`wqA zk;9jA?q+C?+m~xBF*^jWRLr^-U=RH(c=!|1&;D7?**bO!9(BHCTksO6ne;N%y+4E3 zFblLgZ@kW1QXx1hns^(Jn+;}o*Q3?cdm>%%Op6uFF)5txs~T3T?E^oqSvCGx&zhl0 zG(U}9+A_@7iDkt6ez~Zt=%&)q^M8-{4*jFMR7fp1;{=&*VeKoIQTzMw%Z=MW4Y$Qn z^V(SATb;-RP28sj++L}=?m|Y0zg94x%i&%5DO_N45*>Z8nmYINQOCYI-nT0gt!k!W z+#NZ_JNO*K?Oc|2?1n7O*hez;WBPJTA)gj&M?s_Oc)pIBzk%;=b&?>@i;>(5;jjNK zmfEKzQhB(P%9Yp)xjH59_72JS89cOCv8Nt_kFywcXgPNMPs8iLR)VF4theRx$3M4+ zv(78U=dc?(kprCVb3Bj#+Aqr4s6+Rows&+2))%n{E^OcfzX03b03E~L9^N||JD0nV z$#WE+p^3Pgj>}VR=4j|B(T4_Sv9{~AM4r{gxh7#Z_js@1vf$Zx6F$(Q{ha&+W|{|( zwYw8@+598C8`^5u8*4q8EF{# zHSO@Z{sh+6T-?#QR$?-PxA8VI(`{{x?K7}1Yr_2eghhT;Sg!E7%URyKlW_U)t36uF z2n;;y``cJkDEj1 z{EOIE&jgeBPVg>wBG)?-8vd4Y*7#knszt`4)>Am5nbU+^Kj;c?ga7*oI8j6OtUbJ& zk?-tNt#^Uxb7(*@e>SArg&{^322}a4e#O#RuSm5CjF7aB)&Cqy3}aRby6P2F^n~7^ z7q_wG*<8l)Dr$cQe)i%Ks{QWsT~`gp$$5nt_^xB z%`hq-1)wb3z(5Pm3bfEpgO#GM~yR^@4je_S1_y zBro!}y(Q3m2jTbKjG3(z{K47C!G5%av1X>SmPYJquRy!84;eU{dU)fP`vu#F`$hY% zLCOAHpJ;b=36_a1;Ilx_C)RU8mzwxd>A0EILX*6qkMs0{>5QGU{8=N5TwO)J172qX zyd9?#scqRlu-V}KN`e-YMUJ8aHQ`A-<4+B8&dbP8{R|q$sn`)kAcyd``g`B4eQ)E!&PxgW($b|-G>S*Yd6eYK74 z&{#j{(>P}C7u~-Ofgy>Uq*~mz7Nb`>gS&fPF&XVo;>U!>V7}TY*dy10c^}C-Ipm0s zYv=4u*aJR=-NuAEPQFmW+h^iFDuP%0aGT`wKr0l98Wb}qI>QbM&btrsjz7Speg}8C z*Ly|T1D@o*a?aCKLVX#STaEDJ2jH$Z7G9N;n3vBYv)I3v+SWu+`^(EY{W}||{_`lR z`6-$=)+dRwtsdMY^h19f;T($w1$!K7TO#&OZ-Wn4&?va(SM&C+2Hvs&`-Ej}yl!%< zpdH&T8d9Mjf32G6e}?8}B6hoXS17vCxvU_kQhqR>3h*UK?=@1*qxMjV&n*Zf#KtaCN1`tQL~S^;mer-8NYYi6v2eTrjkpCWI;b0Kg) zi~K7VT6Na(5OOMa^|IC>c<9e25Uo9on4-gpb!nvF@WfK{)tC|O-V8gji?jQ zElun48}95^aJNwHpQ7&8qu$K~YknPeoA4>S?O={iZ1J1s?NxO}J5@*9YU+G+p{;VUqy9qZ&9V_ZGPiKlu>t#}rBUdj>DPzRqB&hR^>D_9auBL|;~;=*{oqkpoN} z$HDfSiaVCBNVQC9VT}>c^=aBzBQnhNnlhrxs3WHH-~c{?I!fDl)0Jb4Q}gkkWAcvP z@VX8tw)cyuqkA_Eij0BZH->S)6{C8Z6If>k^2a935iFBeNzVJW)7vKR7J{Hp_FT&p zJVy(tPlk^BLOBySIggB~&f;#{k;RXCKZ6hYHj5A5ktPP;VjFy)C z7O{^YbM-gq8^^}-0mbm?wJ*_H^k8(1k0It4qKJ7olUk9Z>KfCE-wzDNZSA7354-A? zPVh_Hc^P|V`%CrI)&QPjJ#>`^vRRV|hBLD6jS~iVQ~#i7IWr(xC-h5Jdyfcao?r>B z=ggy^>r1HT0@pS3!9nPUT5y9}dXRYjL=Mqg;HsW$r1BVKo(*L%-qmr)7>c9TuM;`L zG3@0&1UKlh1H9{F%w<9NyCc`y{WchA&G5P|#lAWmyP+?Rg9~?<_q~W2=$#tgdprZI z?<8azW>HH5>d-qWd=Qf$hGZlOqkn;RRji_J2Y8+y+|u)*dw#Hqm($xtWEe`0b3=kF z2Hw-lc%S_bb?EkP!TMk$vXcsE(2J=wiDsNF@YbHiYjei|;(B0^ zx^vO{*^7}yUM4!H;(e0dCI(mc2!Zv`A+5m9=|2ZW$5PB?cY_CeTQBzEy`mGh08e2x z7dXCx2CRj~Aslnuo<_;LArIW5c;4uZphgZN7K6Hz()N|ScHJnyk!y8|$ zpoYwRstL+rrQ{k#J6yqN-%R7gM%?l@fPI$NO10h=P9KWhb`tJZZ#59@q$);Jhn(jp zz_EKf7W4En!oN9-`2U{8sGDXpTy3amjNU_~U4@J^2bvBWG77`MeR>D)k1Q~})GFkf z?-q0m7K-AQY0xx5H+*^r(LPt`H{R9G+APRvjT_)?N5DM&q*Jhjp~mmS`!1y#`8MF| zad_T8ou=v|ixquDGi$W8amGnaoaq&KoQhB@miH*~p*FDNTTx5;sl5!@4qFd_9gW=1 zX~-XLD`%_^K;v>}0<`Mss^jS#HlQvaIqKjER+cE%udxG}Qp?&tN4C(z$h7R;uR5j- zF%Clqk#Dad4zLGp`ex47*QSwQfVXodm?G*H)iQM*Yrkz3wQpQQ&9SRFV>fEePz0-~ z+&p4QUkV1;D%Sc_7&Yx*E^00>;e|URRBrobqB)Lz?Hx6&z8CXrUn8eKbBfVs{-j7X z#~J%o?2n#!RjXrPkp#yKUJP5OmnUXWUk})Xds@Z7_nIWniAK?uP)=+u$X|6P(ZIj4 zpL!OV6A56+e$mTX1aNxN3y5wWZrOO|n-(`xeFV6-u3A2Dc>@zt+QxVvYnG5@PF%*l zbaX=k8{8ek`bMv(u8kYv>swE)T{EcRfw`P@*>cfW6(;y5Zh@91p7)J`K6o0mY7KqJ z&Mjer>e9J!ALY=XZ?cIeI+u8$aS1@4O301f{HQmh#h}rfrGT$uiM6&smFAQTYa?S> z?{#Fo9bdp%3JQq@cOTP~NUGnTOfALb#MOZtb!vwga4&X5)4N3vG`_BXdN}9kHe|#? zt8uZBnoohvl@HcSFZ?a=p<9jcx*izft;cY4TH7yL>$)WCTdktyOUwix;y&JuJ@3V4 zAz&^z9ZRr#JdHY}U{92b8~YM?PEQrFt_vBAXA)}gor%Q!GxYi=ayj#t-GXD;an5ar zr>tQ==XUR>o`(-mPcgEXKSvL==!oEvj*8xQ4hp`*s6&&Xb$Sc>p9ci zqWS%1!Rjq0u77ig%Y>Ox!Os`7oiz@jem_(|980=5J9cBvFAh-GQ_!vER`AY66};=N zM#0z9CIol%@WH3RK#SNfIsy-f_9FC*3wkA6C+g7HKEXY)QwUh!$OmHg=$#53`=4N- zO{^3>CsMEn*n$knl~m)0m*Kt5jQ-|oqS>^Fr|+)g>4fc^>91t?=aBigXo%WQ4GEUl zdnHp#k7(Z9En3?`jQos4ZOyh@wVAEiJe?}T6GAlQkA@V9xoo3pngGiH1(@&~IJ z%Y_Ekx(6KJ72rixfzRniuYUp=#HWz`(_62a@9tHtNA@$0?+!4I$J#jii)E}GTz>n- zT28h%X`Ij0a2|YSY||F7rqb70bNM>Pk`$xZ8e$nacm+J(%Q$`EI>!9sQt|)?M1FS~$P5~_iwo+G;GIj>a)uVLiEbqOtq0>7-{Kg*Z)6E| z{DeC64&FCs;njIClbCtrOY~wl5efg>0_=z=?mabN(C$F*GT6qu^zGDM*~MBG)T$;; zg<`%7oU&(;-S*gFYQG&B+*c3q_MO=I26ky|Pq#_7H1I4RE9YJ5wSrgIBzit*5!{F2 z)w+rO5O&s{v?|samd`p~N6zBDy{xMmJm!OGtZ8iqF@+R?-GV(w_M5nwz}LDTKCBqr z&nAM!Sb{nB|x_yrr zG%uS5rBo9Sa>U#r_<63hNbV`a*?Vm_C*;V!okyPJoxYyYcHv{5jCe?^YSPUy_0 zAe*ug+`@ZO!DZOP>W@Hs*#a%E2c9?UK_V-7?z-{w<-*ew1wVdi6CZG)l@CUp2~6(i zJ-ZI@&d7s;{T0;eHGLW@+b7wp`$V?{ow){?Y+Ipg>g*KUUqD;%CGJ2^r1I9mZJfC_ zoS6QKVfCw#smzl>juGETF;yAE#vfeAU|L&vJ&d|BDX4un9t;^#y^@FtrMD~pQ}03Nodr5 z=w;3C_Y+GbJa%oZym>KdQ4xClYq)`ZQ^siTD2A77AH&VhRk`trs(3r@;2Recp!uyj0{z76OZ+j7Xgb>v0KWnHd1<&I^yyupML)*B4HGGb@|179uHBFVQ{CX|xNNQB= zz7E!02OZIm$X<;_zQP0GIIaR8=f)A%a}C+xmGE*-1b0{8OYPBMILv}K{U*Fu<@JJR zN+t3Ppc{RzTygf7DE4oW(O~RnEeS)cT!h)~F5GV~BfH_=Y-)eDm^$8X5Ix;xB)E46 z^_`y2*nVH87)=p3%@Qu;K|N_|NK7K@3)x2P+3Pq*aRf4jR;zbiS0822aj5Nj{xG<;}4|$hmOMn2$G#zEJE%9!JJadoCAXPNyMZ zSxiV@G7Y|r+{8x~iH4+Eg7MwulD%mY?!nO`0Z*(U?j-P6BeT#yg9rC}A!7xH$6e4Y z_@2gWR*1Q52DC5pka^R9|K5(zt`{t)7Ch(fu24-s6f)*~Jd@OJ&h!v+mEJv!^#J@{ zr*RWH-KCND;x09Giz8o7eUH}gzD|7ZOR*#Ff+zilIv%Vr&LKjlHy7IV^fcC)6b~Lz zJZpJoFJt-@jE9Ht415c{_2oCIBNTV~lepjS01Nh4Xwttv$UE2ID-hWozaJ7@g#)7R z?G7<;DfTL_AhQym8+T6;I7C^Tb7?Z~y)9l0{BgG!xH^jlPDekK2d{HgtK=PS)p)LU zYV0pz{)$8$N{3cy9%|_mxT|l1{$PBMAZJ5=x}}_0!?KCF6yE3I6ru@FCi>ES#PV$e zZ(V_WO)yVng0HQ}5jlh1S6&=*3NKE_@= z9zKsQcwR!fxxhE!E1m-GZz1N-aq#LtgP!gYJpan!>p8ZUwXeY}Q60@W{~J%OKg2Wo z-*WwhXkt?&w%BA;}P3OUWIER}0F*wYtu#bJJhqrci2v!+vCS-Y=reV(z zQj2V=T2@yM9!_E%BmIFH?Wx_2l#_d*+#~OtZ3W$8fyO{ zpIF_{7@tgF#oq1_diCQGa`BL=J6_A!_cyW5vQ`!~owZ~RK|_R}eIfMs@NL>7P=B@^ zA)X2_1Ea80OgPBf-ao+G@}QM`5zn6u(8tY#&nFWz`Y*U|kI7d&Z)GcTel5|z)6ZD& zIyjabWL**1F9l*hWCO#q6nf>QjntP_#01?JNqmPFvvPdcuzB*f8#-l|U!w)Du3$T{ zkyX4o`gJ@%pLohT_@EVyQc!Y(;J#eL+gE{= zvL4*H3a|;np@W4+!}1)w+Z#}q@2EpIMxko(?qf_DyQurIt-RM3O>A!$sQOi<#L9J2 z$1r*c9rCrmhu8dpRCUx7d)c7G1me0DPkoi(v-(n1>y9|y{Ah&W>{>5*CoR-^HZ0V- z{$8eacP`fkUfL)I>{=}aB&-v>!VWIz>r`&kTj@0D7-q9Cp~ddcCIO4^y`qw{+IBZl7YlQKFd7qc0V}dDlWueGzxF50KxEzQr4jT>Dmh zmM+7OVQi!(^m_WU#jNpV;d!5!2A#$hFK zex5}=LkU8_Y1E;yn9c4653R6*xb6dA>Gn2__q#T&ZyLN_vCz&CHCS?lyH#&k1{F&<23jG^$e+{9! zwA&~5V#YbZ zSmVHg-+!2OjYT%-Z$rcp3eDCk__FiBB)AEF#}?dmz*Mp>Z3Q=~ofs=SiK!KHQ8%*B zBfyS1l}qHOkfZ%Nvcli(M&L!X!Z+HrxkI2A8W*W z(c0I=$dB~m^NGB`TQ_whX4EwYi2ZTwhIStT`x7E$Pv)#&c%*Vx1Vv{+e9o6f?r!Y2ltsd#1}PCHIZx8c%kp6rc;|yxr^JXWvKyW<|5X%ah2c z`ZzA|fdt;;+sg)|rxNd`LV<3s}C~H>H*enK=0m$_t1q7$(am~ zR#1oF-r6a8)7r$qF|9)2UEn4SbraVJ7~2c>5%0VtE)X2?psSdPKTQRHw}d#Sf^DDL zE_n~PX@ZuwOMynz3KidLF7kUGKppz_fMBnN#%5a&Z;5Q?Ek8rkkGxy^hHU26e$zTL zOEsUvorCR%?hJP-9(~R?s6!Q~N!7@qw)XJuNo~Am2wI>QkXiB`w2&`A2UU)q=ws9& zAG85HeD`<4b3YmV(EGhK=)drPdKZ{VY8I)ofz)aF*)uEL>< zOzYy!w}IO?A9d+j>~C)EaEh`2ZLSCN6kbsWUZ+X$DJ<-y#lV$w zV)yKT*Z)uadzH|=K##(?PatOww-I+Be)f}i zznm^$Ex)DV2A;{7#}~53RhYSJiaEOtnxS$ z>MlG+J%8Z7`tu>)J`wknN_c8IzzL0jN2B7F1`D2me7;$p2b?_WAAM$sxV{n8ju|W=K`;E}5=kzkRN0BplES>j+tkwE-t0c$7Wg6e+ z4N|bOne#okg?hGcApvDk#QRee_5Qj^DoN4NAULUzn(D5yz0iRim8Fk2ZkdxnnF6AG*CiAf4Jb^y`fADt&)NsB5 z^eXlE>>FA{7l+Tp%m2sGc}7QBb#3_RGn2{m-qNUohzj=JE20QkKtX!%Eg?W4ArKNm zNCF9@_g+b%gQ$q0qS#PTQS24<1r#CkUHN`wxt0rt$;@;1+56si!ACz>Dm#~C$?mdv z#gjNwvrL<-Se{)fI*ls?kA0QoF>I9GcjZgIxm9v#Ts=FfI@!LBUH@Lr=@4G$EblXt zdig3obC1!p%>ZY)g$&5zb}?kbyJ*^W*+bTC=nc5-cs<8DljRuhY+oUML6`Y^Uo&Ix zt<}Z6U8}`;@kKZYR=pliqovGd9X!*++cX>A9X5TTXrG%UxUb?rcMJ^czI4I#cbRBx z0i*wDzwD_6tNMmH@keH^67mtnHuV4WW%bmf4){+>yXJnIonIgFIh@R`A9d)w+t`cs z&TI2p)li>SB|4HGY9_tcGi91}bdl`aNp(clGnbagt{re@pJ-HV+u>V3g$~XN=ldCW-u?08+(LiD`?cQ< z*7GO(>kqht->OCrwL`H#n}y#f9w_+#*&oCI@jdoqFEZCV(G*K7b!o;2W^}5a0Ppu;r6EVS87S z)Q?Pt=&5Bq`jKg3CHaFeX8$O zI9Bn@Wuy5%ye+EN(Wts_{Jrps>2lK*^E2%1sXyM&55TKoz6+sK zI>MgjbP<^|*>ddAm1^AQQ&e}|6xmiVU9?9m5$q2o^q3Z=_n3Awr(TZ^A3m-9_&m)v zc^3P>xe<=gLcQq5It|9TZlJd+u-wFi_hUwdWKchpG!QT=7w0|Ri zzxO-D&%tpTT`j%LtEEu!b!=8WqcT+6%o4>M)2vya2dA%V z(A)bp=xjq9b=J-Jlz-E%I+wvSY-v&LXB*VW$?S?3tkYb^RMpdf?{_nG$WI;WpbmZ4 zuGriyn&ma}vPP!b_M3`QNFP@2~@+5d5HS{YZ z8ub3r+*^8lc-tEB`DqlL|0@^VGqXj{7b%J(HdD2j*-g6YbLh| zLz^RM#gyL7Iv_NkuOL*&KKBf;bJWil&&1~24#`nOehJ>0)VbnHy) zu}?*xKdaQ;wT=$L8Ob^deBG=&cHj3Pk`==Q!?wJP1j%9k^ zmBohWj07#JJ416C@$Pf%k!^agi(Tvn^TFBD;1~Q^ELsk%R-KO3TGTyBJpXGI*9(hv zp1Ij_XhF3cmxOP%il5tq2V~Q$CE)Mtl+ZA=uFs~b{+H5~kR?MMP`*v=pTy1;?+M2n zU>BdzLw1uxxTs1sEzi>VG;|xsmz(;YTx^WGv_K#I#v+}sbDqvMcB1Be_9eWQmWDn5 z;@SG@hm|0qo)w{@8m{oPV2^wliQdpu2bI1@EXW|Cz6o7t>^>}~c&z$=GZCNny5obn-n1CwhY8C=p(%5kC1CHmOkyS^-|#NBsq{9 zFM9Sen^mro99t5=vscOfk;!5J&r&z}FpfK_WoIUQ;c4_kKQcGwQY&-o^bYbnO!MJ* zm-%So?3E8?&;=817lRUrC?_Fdr zqNnp5-G{Cp9I6nW{XY%7Mv?3$Kil86RCSgtmdvv@3i?;T5G>pg{=KsMRx&rJTi#cw zIoZ^oN62giyNbe3EwmTz(7j;m2dF_Ox;6XwF3o<9xnTy}p*PvlZ{wVuVTU-HdwB?+ zyk6#5eXeZBJRoh-;nokL%!^|7QXEh8KUDsvSPl6yqt* zVnwUoy1yyH@?ML<@;_?QF?j88t9_TLLnZ9+E|IsEK~`>Cz7#p~+wyLmg^w)Y-gv)>>LL$2WMjt*AjOTcBu}Ytebl z(A!#8Yi{cfvev5=$4&Gn3(z}{AtU!byo4X&zQzN|{s6PvwaNH!r)us;+0{1hkgY>j z>n+d4YsOuxl?Z31Y#o%RIDX*{In}814TDpA2OPE=!OqH<7l$^HCkkG17aZhUnFW7j z-w;u+b6jdyt$*&}yQBxKY|^>Dg}?SE+>ul8e7t5bATL9{6FvAt2lR|amlPx z?O$$FJZl$g{zc)oGvqE@9;LwXD_;hOv2IR3f)E4b|PA~v-BksYBhg3^HXEJ-Y@Zb!{P3H zOnrN&QgN>=6r4X6^tk2}_IT@Z1dlVN_jwg9?3FD8)=G}!>!?xdMDL_j*_WIvy8a@U zM}YUUv_bE!=FY??DRe`X&h;JK?kcjfpHI?)pC;)%Q}F~|$R2UXdO2p_dL`scQvB*- zc5aI_`&Y@Dr87%$Y$Mk-2aTJ#RPx}T2}Y}U5~xK5Xh{aPsE#@4(|+702OeIbg{Dr| zIkwG{A`fSV<=^VUh8x(8)}jlV#Jr_||Bw&ne~8*+?ogw^yrY+p%Q2)|4P1ksBMQuE zG&QIae-S%4)USB1b%NbA_G(w~-Hir+Yav_d_I4=>A2sU@<%%O7&eel;T1g^f%vVAH%vyv{!O%1}^ zM7DnprhOOqc5JU6mwW3sbfX14JH_mZ5||S=Y*y`OGGqgOIfhvHwzc_^$Bl*_?p2_x zQ6HLv=K3XciL1$AnZ_>keYpLNyth=|LoPmj4QK{GVutkali4lXFTE<;9EW7nyw?irprf?_w9~+}2|nT_3gxO+A*& z@Nb@CS9bBB?0=j(G#)SX;q6*vM}t1XQY72&;ePyXx@t{aAlVDzMT={_U_sw&Bk#!h zMV=HaPFMQhxKY>du?$`GnN)qizXd&HmKOVH33Z@QwEWT(wp?oO@%*+o>@8ydaxb|0 zoP+4};iv@Q);co8;0IF`*XFsh_5OLX<>1SD^O+a*R(ZV6;$E+q&FJ*!WXf^p$Urm{ z;oFIZITO5j#Sz)MflT1YS~N}Udg@jP{=TVK`g}1*jJp6{bA5v1JGor-ez;2WK28qW z`X!oc|59D(<>iqv@hc)jOV$|UEGdTAvpITCSD_(lURgw7cBw9KoW0_G;^?6CQHJl zQyRXRxw50B8ozmZm$$*IU*&hoV*j=TF2Fx%%O3*Io>m%m|5Fk6go=9Hja$Qxg&Csx z=X7+PX`=mKe5gG1LdERAvbM{ve0I6acHR%j=^0w94<4s?8&;-yGx1FLE1sG^SMOa- zUp#M(5*)u)iTNN|iSAgh1j(&-y}4Sku1i#m&yZsuC=^|U(znrHF^q;0V%HoBHrX4*>WJ^!PW9|4J z+ps)>K9%fbMf=QjsmRtPiDlS zU1%fe!?*6kWBi0{9C1`O9X%nN>&Zy{6>d3M3Z6fio42vMzlGnai=Oo_@>9y_LGgI9 z&Dttj_Mqo)AY1ifQ`p?m8n&G25bQVZ#fOA`=nQq}TXqmNwn9MNTL0k)&5y&y13A0eJFE_!Q;%)x$XsjyOk`yVY|Y% z`i`*U-d$nmVz6l+`^m4_#k3w$y!TOuCXxS}pD%jI_jZ4hDBEmPHR~%Qbmlvr)>&U2 zr?YHjGQEu34jsqgquGQM{}a+3i6Qut5=AFfdRU!XBPj~7TD=VSsi_gkf+ z`^Hki12@T=lNa_(Wfy#TEB$wt?C|G^whMU8%|gez0!>&KnLI<-BlV!2y}3q<+Eb+q zykDw$XKqzIL*vz8{X*4uaSfT$8^owp^g>^ypzWX^x@(i{zLPm|L#Aj-E)3hRX^=d9 z+a#Y*FMAaBCK(MnM|y|Oah6)N0B$e-q@G*K!T2{S(NE3Nd1lQ~ZQpMc^ZWg0rj~EjPNhlm_N8>d+n4TIh}nE&A&UHR?P% zlixV6H#EzxL2zxd;MfgfKbyWwvaab+Y|Y*1_Su{L*r9Xx;TegmhX2JmzO7CdDu**g zetO7XBS$aCvvx|0>Un@#JAwV-5blW#d?)?r{_^2Qz5)Na4BTrDUUQ@N=$$`x8XQl+ z(Vht&(*w5l2i&am>6*FfVNJL6A)R)5j$*!4B08hHB=4aPGSjIMm0-x;a7WpWOtTTt@92cH|`SHJ-OjFa)a#~;FDGykRAUVQEa*F4#8eMv)DPzqz-)v zzhee@DBWN^4SY5lJ`PvfaJ_R3_hs^_X#JD2(`-75n z{`6FXdth3G`_v}A>&*huG!<`(V4Y|ggVs}k&n4im6uV#aSPn|Q4-d)S1-wsu#Dml_ zS4=h>sP*`!P1o#=kLk?|pVC==86RQ#lYG}4@Zxv!@!PVA z{ta%F<|Y%uTHB$KSHO2u=I`|_uqN_74XWze!JB`~c)axLR| z>;W$tzEg3ek|o%@QRlgghUNh8$;vscV<-L`Jz!;#=>LxloR>=_M*z>nHuk?imx;b> z>4);Sil*N-i>4LXqM1Ar>vVqq_t4Enf#+0%EBF5V3!mj?vZbG^Rw7b&il$pr72B*N z)v;o`V*daPXf@R zZ%3EG`phnawiItxd$HiU6VLH__Rp2zI`7t#2ZEpVIDFGQaN(b){;X&-xO2dTzQ*rz zF&qXzb?77Zm0zKeHg>7rV_;@E%zcCK6eq{eG<0LwQolZIJh??sudOB@twS))*(=&_ z2A{qSPS_;+s7K-2pJiwE%5KrSfZs2R&u9KF)o#qtT%)E$I_*fF z{9&p-@a#g>x+_6v$zG{?o%ybgRKCT~|P7c({IduV6> zfdlaTrpST5Z1c7E=2&ldAlE+hwe8ly9~K*fQN?=iD^(GplRNd%fo#>f8O`-M_|j7| z)wtL3jNMy&#d8Dtn88Jg=L|dW8{t~a-6ngoiX=}d&#<0n_?lWZkVrkU!s~snhJ97F z<~d0(0mmb-8!v2Yfn+(GE16@+>8&8!{@NVLIjTao_eZ-)ZUpzG7B#V17aWdXPhpkn zSPyS;6nN4MyiRUdr2`L89PgmLDB%0A>e4%o?9y9C!xNF=-&PjMmhaZ8*1og#HVvP` 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 literal 0 HcmV?d00001 diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 00000000..97af3c92 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# set -x + +# a bunch of cleaning up ... make certain everything will be regenerated +rm -f Makefile Makefile.in aclocal.m4 +rm -rf autom4te.cache +rm -f config.* configure depcomp +rm -f install-sh intltool-* libtool ltmain.sh missing mkinstalldirs +rm -f stamp-* vipsCC-7.12.pc vips-7.12.spec vips-7.12.pc +rm -f python/vipsCC/*.cxx +rm -f python/vipsCC/VImage.h +rm -f python/vipsCC/VImage.py python/vipsCC/VError.py python/vipsCC/VMask.py python/vipsCC/Display.py +rm -f benchmark/temp* + +# some systems need libtoolize, some glibtoolize ... how annoying +echo testing for glibtoolize ... +if glibtoolize --version >/dev/null 2>&1; then + LIBTOOLIZE=glibtoolize + echo using glibtoolize +else + LIBTOOLIZE=libtoolize + echo using libtoolize +fi + +aclocal +glib-gettextize --force --copy +test -r aclocal.m4 && chmod u+w aclocal.m4 +intltoolize --copy --force --automake +autoconf +autoheader +$LIBTOOLIZE --copy --force --automake +automake --add-missing --copy + diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..149ae783 --- /dev/null +++ b/configure.in @@ -0,0 +1,382 @@ +# Process this file with autoconf to produce a configure script. +AC_INIT(include/vips/colour.h) +AM_CONFIG_HEADER(config.h) + +# user-visible library versioning +IM_MAJOR_VERSION=7 +IM_MINOR_VERSION=12 +IM_MICRO_VERSION=5 +IM_VERSION=$IM_MAJOR_VERSION.$IM_MINOR_VERSION.$IM_MICRO_VERSION +IM_VERSION_STRING=$IM_VERSION-`date` + +VERSION=$IM_VERSION +PACKAGE=vips + +# libtool library versioning ... not user-visible (except as part of the +# library file name) and does not correspond to major/minor/micro above + +# rules: +# sources changed: increment revision +# interface changed: increment current, reset revision to 0 +# interface changes backwards compatible?: increment age +# interface changes not backwards compatible?: reset age to 0 + +LIBRARY_CURRENT=14 +LIBRARY_REVISION=2 +LIBRARY_AGE=2 + +AM_INIT_AUTOMAKE($PACKAGE,$VERSION) + +# patched into include/vips/version.h +AC_SUBST(IM_VERSION) +AC_SUBST(IM_VERSION_STRING) +AC_SUBST(IM_MAJOR_VERSION) +AC_SUBST(IM_MINOR_VERSION) +AC_SUBST(IM_MICRO_VERSION) + +# put into library name by libsrc/Makefile.am and libsrcCC/Makefile.am +AC_SUBST(LIBRARY_CURRENT) +AC_SUBST(LIBRARY_REVISION) +AC_SUBST(LIBRARY_AGE) + +AC_CANONICAL_HOST + +AC_MSG_CHECKING([for native Win32]) +case "$host" in + *-*-mingw*) + vips_os_win32=yes + ;; + *) + vips_os_win32=no + ;; +esac +AC_MSG_RESULT([$vips_os_win32]) + +if test x"$vips_os_win32" = "xyes"; then + AC_DEFINE(OS_WIN32,1,[native win32]) + + # makes gcc use win native alignment + VIPS_CFLAGS="-mms-bitfields $VIPS_CFLAGS" +fi + +# Cygwin/mingw need binary open to avoid CR/LF madness +# ... should be a better way to test for this +AC_MSG_CHECKING([for binary open needed]) +case "$host_os" in + cygwin* | mingw*) + vips_binary_open=yes + ;; + *) + vips_binary_open=no + ;; +esac +AC_MSG_RESULT([$vips_binary_open]) +if test x"$vips_binary_open" = "xyes"; then + AC_DEFINE(BINARY_OPEN,1,[define to open non-text files in binary mode]) +fi + +# we want largefile support, if possible +AC_SYS_LARGEFILE + +# Checks for programs. +AC_PROG_AWK +AC_PROG_CC +AC_PROG_CC_STDC +AC_C_CONST +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_CXX +AM_WITH_DMALLOC + +# vips.c/im_guess_prefix.c need to know the exe suffix and (as a fallback) +# the install prefix +AC_DEFINE_UNQUOTED(IM_EXEEXT,"$EXEEXT",[extension for executable files]) +AC_DEFINE_UNQUOTED(IM_PREFIX,"$prefix",[configure-time install prefix]) + +# i18n +GETTEXT_PACKAGE=vips7 +AC_SUBST(GETTEXT_PACKAGE) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", + [The prefix for our gettext translation domains.]) +ALL_LINGUAS="en_GB malkovich" +AC_PROG_INTLTOOL +AM_GLIB_GNU_GETTEXT + +# Checks for libraries. + +# build list of pkg-config packages we used here +PACKAGES_USED="" + +# Checks for header files. +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_CHECK_HEADERS([errno.h math.h fcntl.h limits.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/time.h sys/mman.h sys/types.h sys/stat.h unistd.h io.h direct.h windows.h]) + +# uncomment to change which libs we build +# AC_DISABLE_SHARED +# AC_DISABLE_STATIC +# couldn't get this working :-( maybe try again with the next libtool +AC_LIBTOOL_WIN32_DLL +AC_CHECK_TOOL(DLLWRAP, dllwrap) +AC_CHECK_TOOL(DLLTOOL, dlltool) +AC_CHECK_TOOL(OBJDUMP, objdump) +AC_CHECK_TOOL(RANLIB, ranlib) +AC_CHECK_TOOL(STRIP, strip) +AC_CHECK_TOOL(AR, ar) +AC_CHECK_TOOL(AS, as) +AC_CHECK_TOOL(LD, ld) +AC_PROVIDE(AC_LIBTOOL_WIN32_DLL) +AC_PROG_LIBTOOL + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MEMCMP +AC_FUNC_MMAP +AC_FUNC_VPRINTF +AC_CHECK_FUNCS([getcwd gettimeofday getwd memset munmap putenv realpath strcasecmp strchr strcspn strdup strerror strrchr strspn vsnprintf realpath mkstemp mktemp random rand]) +AC_CHECK_LIB(m,cbrt,[AC_DEFINE(HAVE_CBRT,1,[have cbrt() in libm.])]) + +# have to have these +PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.6 gmodule-2.0 >= 2.4 libxml-2.0 gobject-2.0) +PACKAGES_USED="$PACKAGES_USED glib-2.0 gmodule-2.0 libxml-2.0 gobject-2.0" + +# option to eval without threads +AC_ARG_ENABLE(threads, AS_HELP_STRING([--enable-threads], [evaluate with threads (default: yes)])) + +if test "x$enable_threads" != "xno"; then + AC_DEFINE(HAVE_THREADS,1,[threaded evaluation]) + PKG_CHECK_MODULES(GTHREAD, gthread-2.0) + PACKAGES_USED="$PACKAGES_USED gthread-2.0" +fi + +# optional supporting libraries + +# we can wrap fftw3 and fftw2 ... but just look for fftw3, since we can do +# that with pkg-config +AC_ARG_WITH([fftw3], AS_HELP_STRING([--without-fftw3], [build without fftw3 (default: test)])) + +if test "x$with_fftw3" != "xno"; then + PKG_CHECK_MODULES(FFTW3, fftw3, + [AC_DEFINE(HAVE_FFTW3,1,[define if you have fftw3 installed.]) + PACKAGES_USED="$PACKAGES_USED fftw3"], + [AC_MSG_WARN([fftw3 not found; disabling fftw support])]) +fi + +# ImageMagic ... detect attribute iteration too +AC_ARG_WITH([magick], AS_HELP_STRING([--without-magick], [build without libMagic (default: test)])) + +if test "x$with_magick" != "xno"; then + PKG_CHECK_MODULES(MAGICK, ImageMagick, + [AC_DEFINE(HAVE_MAGICK,1,[define if you have libMagick installed.]) + PACKAGES_USED="$PACKAGES_USED ImageMagick"], + [AC_MSG_WARN([libMagick not found; disabling Magick support])]) +fi + +if test "x$with_magick" != "xno"; then + # we need ResetImageAttributeIterator() / GetNextImageAttribute() to get + # attrs, but that's 6.2+ I think ... test for them + save_LIBS=$LIBS + LIBS="$LIBS $MAGICK_LIBS" + AC_CHECK_FUNCS(GetNextImageAttribute, + AC_DEFINE(HAVE_MAGICK_ATTR,1,[define if your imagemagick has attribute support.])) + LIBS=$save_LIBS +fi + +# liboil +AC_ARG_WITH([liboil], AS_HELP_STRING([--without-liboil], [build without liboil +(default: test)])) + +if test "x$with_liboil" != "xno"; then + PKG_CHECK_MODULES(LIBOIL, liboil-0.3, + [AC_DEFINE(HAVE_LIBOIL,1,[define if you have liboil-0.3 installed.]) + PACKAGES_USED="$PACKAGES_USED liboil-0.3"], + [AC_MSG_WARN([liboil not found; disabling liboil support])]) +fi + +# lcms +AC_ARG_WITH([lcms], AS_HELP_STRING([--without-lcms], [build without lcms (default: test)])) + +if test "x$with_lcms" != "xno"; then + PKG_CHECK_MODULES(LCMS, lcms, + [AC_DEFINE(HAVE_LCMS,1,[define if you have lcms installed.]) + PACKAGES_USED="$PACKAGES_USED lcms"], + [AC_MSG_WARN([lcms not found; disabling lcms support])]) +fi + +# OpenEXR +AC_ARG_WITH([OpenEXR], AS_HELP_STRING([--without-OpenEXR], [build without OpenEXR (default: test)])) + +# require 1.2.2 since 1.2.1 has a broken ImfCloseTiledInputFile() +if test "x$with_OpenEXR" != "xno"; then + PKG_CHECK_MODULES(OPENEXR, OpenEXR >= 1.2.2, + [AC_DEFINE(HAVE_OPENEXR,1,[define if you have OpenEXR >=1.2.2 installed.]) + PACKAGES_USED="$PACKAGES_USED OpenEXR"], + [AC_MSG_WARN([OpenEXR not found; disabling OpenEXR support])]) +fi + +# pangoft2 +AC_ARG_WITH([pangoft2], AS_HELP_STRING([--without-pangoft2], [build without pangoft2 (default: test)])) + +if test "x$with_pangoft2" != "xno"; then + PKG_CHECK_MODULES(PANGOFT2, pangoft2, + [AC_DEFINE(HAVE_PANGOFT2,1,[define if you have pangoft2 installed.]) + PACKAGES_USED="$PACKAGES_USED pangoft2"], + [AC_MSG_WARN([pangoft2 not found; disabling pangoft2 support])]) +fi + +# hmm, these don't have .pc files on ubuntu 5.10, how odd +FIND_TIFF(,[AC_MSG_WARN([libtiff not found; disabling TIFF support])]) +FIND_ZIP(,[AC_MSG_WARN([libz not found; disabling ZIP support])]) +FIND_JPEG(,[AC_MSG_WARN([libjpeg not found; disabling JPEG support])]) + +# look for PNG with pkg-config ... fall back to our tester +PKG_CHECK_MODULES(PNG, libpng, + [AC_DEFINE(HAVE_PNG,1,[define if you have libpng installed.]) + PACKAGES_USED="$PACKAGES_USED libpng"], + [FIND_PNG(,[AC_MSG_WARN([libpng not found; disabling PNG support])])]) + +# libexif +AC_ARG_WITH([libexif], AS_HELP_STRING([--without-libexif], [build without libexif (default: test)])) + +if test "x$with_libexif" != "xno"; then + PKG_CHECK_MODULES(EXIF, libexif >= 0.6, + [AC_DEFINE(HAVE_EXIF,1,[define if you have libexif >= 0.6 installed.]) + PACKAGES_USED="$PACKAGES_USED libexif"], + [AC_MSG_WARN([libexif >= 0.6 not found; disabling exif support]) + with_libexif=no]) +fi + +# some libexif packages need include , some just +# how annoying +if test "x$with_libexif" != "xno"; then + # cppflags not cflags because we want the preproc to see the -I as well + save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$EXIF_CFLAGS $CPPFLAGS" + AC_CHECK_HEADER(exif-data.h, + AC_DEFINE(UNTAGGED_EXIF,1,[libexif includes don't need libexif prefix])) + CPPFLAGS=$save_CPPFLAGS +fi + +# Look for linux video +AC_CHECK_HEADER(linux/videodev.h, + AC_DEFINE(HAVE_VIDEODEV,1,[have video4linux 1]),[ + AC_MSG_WARN([linux/videodev.h not found; disabling Linux video support]) +]) + +# make python binding? +AC_ARG_WITH([python], AS_HELP_STRING([--without-python], [build without Python bindings (default: test)])) + +if test "x$with_python" != "xno"; then + AM_PATH_PYTHON(2.2,, + [with_python=no + AC_MSG_WARN([Python not found; disabling Python binding])]) +fi + +if test "x$with_python" != "xno"; then + AM_CHECK_PYTHON_HEADERS(, + [with_python=no + AC_MSG_WARN([Python headers not found])]) +fi + +# need SWIG too +if test "x$with_python" != "xno"; then + AC_CHECK_PROG(HAVE_SWIG, swig, [yes]) + + if test "x$HAVE_SWIG" != "xyes"; then + with_python=no + AC_MSG_WARN([SWIG not found; disabling Python binding]) + else + with_python=yes + fi +fi + +if test "x$with_python" = "xyes"; then + AM_CONDITIONAL(HAVE_PYTHON, true) +else + AM_CONDITIONAL(HAVE_PYTHON, false) +fi + +# Gather all up for VIPS_CFLAGS, VIPS_INCLUDES and VIPS_LIBS +VIPS_CFLAGS="$VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $PANGOFT2_CFLAGS $FFTW3_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $OPENEXR_CFLAGS $LIBOIL_CFLAGS" +VIPS_INCLUDES="$PNG_INCLUDES $TIFF_INCLUDES $ZIP_INCLUDES $JPEG_INCLUDES $FFTW_INCLUDES $LCMS_INCLUDES" +VIPS_LIBS="$MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $ZIP_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $FFTW3_LIBS $FFTW_LIBS $LCMS_LIBS $LIBOIL_LIBS $OPENEXR_LIBS $EXIF_LIBS -lm" + +AC_SUBST(VIPS_CFLAGS) +AC_SUBST(VIPS_INCLUDES) +AC_SUBST(VIPS_LIBS) +AC_SUBST(PACKAGES_USED) + +AC_OUTPUT([ + vips-7.12.pc + vipsCC-7.12.pc + vips-7.12.spec + Makefile + include/vips/version.h + include/Makefile + include/vips/Makefile + libsrc/Makefile + libsrc/acquire/Makefile + libsrc/arithmetic/Makefile + libsrc/boolean/Makefile + libsrc/colour/Makefile + libsrc/conversion/Makefile + libsrc/convolution/Makefile + libsrc/freq_filt/Makefile + libsrc/histograms_lut/Makefile + libsrc/inplace/Makefile + libsrc/iofuncs/Makefile + libsrc/matrix/Makefile + libsrc/morphology/Makefile + libsrc/mosaicing/Makefile + libsrc/other/Makefile + libsrc/relational/Makefile + libsrc/video/Makefile + libsrc/acquire/man3/Makefile + libsrc/arithmetic/man3/Makefile + libsrc/boolean/man3/Makefile + libsrc/colour/man3/Makefile + libsrc/conversion/man3/Makefile + libsrc/convolution/man3/Makefile + libsrc/freq_filt/man3/Makefile + libsrc/histograms_lut/man3/Makefile + libsrc/inplace/man3/Makefile + libsrc/iofuncs/man3/Makefile + libsrc/matrix/man3/Makefile + libsrc/morphology/man3/Makefile + libsrc/mosaicing/man3/Makefile + libsrc/other/man3/Makefile + libsrc/relational/man3/Makefile + libsrc/video/man3/Makefile + libsrcCC/Makefile + src/Makefile + src/iofuncs/Makefile + src/mosaicing/Makefile + src/other/Makefile + src/scripts/Makefile + src/scripts/batch_crop + src/scripts/batch_image_convert + src/scripts/batch_rubber_sheet + src/scripts/light_correct + src/scripts/shrink_width + src/iofuncs/man1/Makefile + src/other/man1/Makefile + src/scripts/man1/Makefile + contrib/Makefile + contrib/vips2dj/Makefile + contrib/vips2dj/share/Makefile + contrib/vips2dj/share/vips2dj/Makefile + contrib/vips2dj/share/vips2dj/lab/Makefile + contrib/vips2dj/share/vips2dj/cmyk/Makefile + contrib/vips2dj/share/vips2dj/mono/Makefile + contrib/vdump/Makefile + contrib/mitsub/Makefile + python/Makefile + python/vipsCC/Makefile + po/Makefile.in +]) diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 00000000..288e4702 --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = \ + vips2dj \ + mitsub \ + vdump diff --git a/contrib/mitsub/Makefile.am b/contrib/mitsub/Makefile.am new file mode 100644 index 00000000..79ccae56 --- /dev/null +++ b/contrib/mitsub/Makefile.am @@ -0,0 +1,7 @@ +bin_PROGRAMS = mitsub + +mitsub_SOURCES = mitsub.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ diff --git a/contrib/mitsub/mitsub.c b/contrib/mitsub/mitsub.c new file mode 100644 index 00000000..8e3cb2ed --- /dev/null +++ b/contrib/mitsub/mitsub.c @@ -0,0 +1,491 @@ +/* + * Version which sents raw data to the printer, which enlarges and centers. + * Works for monochrome and four band IM_TYPE_CMYK images. Uses putc instead of + * fprintf. Sents data straight to the printer. If enlargement, it is full and + * x and y ratios are the same (aspect ratio is not changed) + * + * Helene Chahine, July 95 + * + * JC 4/8/95 + * - small tidies and bug fixes + * - now does 1, 3 and 4 band + * - resets printer after use + * JC 1/9/95 + * - colour reverse mode added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#define NBPRINT 1 /* Maximun 15 copies */ +#define VMAX 2904 /* Number of horizontal pixels */ +#define HMAX 2368 /* Number of vertical pixels */ + +int +main( int argc, char *argv[] ) +{ + int enlar = 0; + int center = 0; + int rev = 0; + + IMAGE *vips; + FILE *out; + int n1, n2; + int x, y; + int xsize, ysize; + int c; + PEL *p; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + while( --argc > 0 && (*++argv)[0] == '-' ) + while( (c = *++argv[0]) ) + switch( c ) { + case 'e': + enlar = 1; + break; + + case 'c': + center = 1; + break; + + case 'r': + rev = 1; + break; + + default: + error_exit( "mitsub: illegal option %c", c ); + } + + if( argc != 2 ) + error_exit( "usage: mitsub [-ecr] vipsfile mitfile\n" + "where:\n" + "\tvipsfile may be 1, 3 or 4 bands for mono, IM_TYPE_RGB or " + "IM_TYPE_CMYK printing\n" + "\tmitfile may be '-', meaning send to stdout\n" + "\t-e means enlarge to fill page\n" + "\t-c means centre within page\n" + "\t-r means reverse black/white\n" + "\tNOTE: data is sent raw, with 0 == no ink - all correction is up to " + "you\n" + "example:\n" + "\t%% mitsub -ec fred.v - > /dev/bpp0" ); + + if( !(vips = im_open( argv[0], "r" )) ) + error_exit( "mitsub: unable to open \"%s\" for input", + argv[0] ); + + if( strcmp( argv[1], "-" ) == 0 ) + out = stdout; + else if( !(out = fopen( argv[1], "w" )) ) + error_exit( "mitsub: unable to open \"%s\" for output", + argv[1] ); + + if( vips->Coding != IM_CODING_NONE || vips->BandFmt != IM_BANDFMT_UCHAR ) + error_exit( "mitsub: uncoded uchar only" ); + if( vips->Bands != 1 && vips->Bands != 3 && vips->Bands != 4 ) + error_exit( "mitsub: 1,3 and 4 band images only" ); + + /* Set xsize and ysize. + */ + if( vips->Xsize <= vips->Ysize ) { + xsize = vips->Xsize; + ysize = vips->Ysize; + } + else { + im_diagnostics( "mitsub: rotating ..." ); + xsize = vips->Ysize; + ysize = vips->Xsize; + } + + /* Shrink if image is too big. + */ + if( xsize > HMAX || ysize > VMAX ) { + double x_factor = HMAX/xsize; + double y_factor = VMAX/ysize; + double factor = IM_MAX( x_factor, y_factor ); + IMAGE *sh = im_open( "shrink", "t" ); + + im_diagnostics( "mitsub: shrinking by %g ...", factor ); + if( !sh || im_shrink( vips, sh, factor, factor ) ) + error_exit( "mitsub: shrink failed" ); + + vips = sh; + enlar = 0; + } + + /* On line command and buffer clear. + */ + putc( 0x11, out ); + putc( 0x1b, out ); + putc( 'Z', out ); + + /* Memory clear. + */ + putc( 0x1b, out ); + putc( 'Z', out ); + + /* Media size. (Size A4) + */ + putc( 0x1b, out ); + putc( '#', out ); + putc( 'P', out ); + putc( '0', out ); + + /* Enlargement. + */ + if( enlar ) { + double rh, rv; + int n, m; + + /* Enlarge method: ('0'=simple enlargement, + * '1'=linear enlargement) + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'O', out ); + putc( '1', out ); + + rh = HMAX/(double) xsize; + rv = VMAX/(double) ysize; + if( rh > 8 || rv > 8 ) { + n = 8; + m = 1; + } + else if( rh > rv ) { + double fact = VMAX/255; + + n = 255; + m = (int) ysize/fact + 1; + } + else { + double fact = HMAX/255; + + n = 255; + m = (int) xsize/fact + 1; + } + im_diagnostics( "mitsub: enlarging by %g ...", (double) n/m ); + + /* Horizontal enlarge. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'P', out ); + putc( n, out ); + putc( m, out ); + + /* Vertical enlarge. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'Q', out ); + putc( n, out ); + putc( m, out ); + + } + else { + /* No enlargement. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'O', out ); + putc( '1', out ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'P', out ); + putc( 1, out ); + putc( 1, out ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'Q', out ); + putc( 1, out ); + putc( 1, out ); + } + + if( rev ) { + /* Colour reversing. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'W', out ); + putc( '2', out ); + } + else { + /* No reverse. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'W', out ); + putc( '0', out ); + } + + /* Number of copies. + */ + putc( 0x1b, out ); + putc( '#', out ); + putc( 'C', out ); + putc( NBPRINT, out ); + + /* Left margin. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'S', out ); + putc( 0, out ); + + /* Top margin. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'T', out ); + putc( 0, out ); + + /* Centering. ('1' = centering available, '0'= no centering). + */ + if( center ) { + im_diagnostics( "mitsub: centering ..." ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'C', out ); + putc( '1', out ); + } + else { + /* No centering. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'C', out ); + putc( '0', out ); + } + + /* Transfer format = pixel order method for colour, = frame order + * method for monochrome. + */ + switch( vips->Bands ) { + case 3: + case 4: + putc( 0x1b, out ); + putc( '&', out ); + putc( 'A', out ); + putc( '2', out ); + break; + + case 1: + putc( 0x1b, out ); + putc( '&', out ); + putc( 'A', out ); + putc( '0', out ); + break; + + default: + error_exit( "internal error" ); + /*NOTREACHED*/ + } + + /* Colour specification. + */ + switch( vips->Bands ) { + case 4: + case 1: + /* IM_TYPE_CMYK. For mono, send just K. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'I', out ); + putc( '2', out ); + break; + + case 3: + /* IM_TYPE_RGB. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'I', out ); + putc( '0', out ); + break; + + default: + error_exit( "internal error" ); + /*NOTREACHED*/ + } + + /* Gray scale level. + */ + putc( 0x1b, out ); + putc( '#', out ); + putc( 'L', out ); + putc( 8, out ); + + /* Rotation. + */ + if( vips->Xsize <= vips->Ysize ) { + putc( 0x1b, out ); + putc( '#', out ); + putc( 'R', out ); + putc( '0', out ); + } + else { + putc( 0x1b, out ); + putc( '#', out ); + putc( 'R', out ); + putc( '1', out ); + } + + /* Horizontal shift. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'J', out ); + putc( 0, out ); + putc( 0, out ); + + /* Vertical shift. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'K', out ); + putc( 0, out ); + putc( 0, out ); + + /* Number of horizontal pixels. + */ + n1 = vips->Xsize >> 8; + n2 = vips->Xsize & 0xff; + putc( 0x1b, out ); + putc( '&', out ); + putc( 'H', out ); + putc( n1, out ); + putc( n2, out ); + + /* Number of vertical pixels. + */ + n1 = vips->Ysize >> 8; + n2 = vips->Ysize & 0xff; + putc( 0x1b, out ); + putc( '&', out ); + putc( 'V', out ); + putc( n1, out ); + putc( n2, out ); + + /* Transfer colour (for monochrome image only). + */ + if( vips->Bands == 1 ) { + putc( 0x1b, out ); + putc( 'C', out ); + putc( '4', out ); + } + + /* Image data transfer. Image must be sent as YMCK. + */ + putc( 0x1b, out ); + putc( 'O', out ); + if( im_incheck( vips ) ) + error_exit( "mitsub: unable to read image data" ); + p = (PEL *) vips->data; + switch( vips->Bands ) { + case 4: + im_diagnostics( "mitsub: sending IM_TYPE_CMYK ..." ); + for( y = 0; y < vips->Ysize; y++ ) + for( x = 0; x < vips->Xsize; x++ ) { + putc( p[2], out ); + putc( p[1], out ); + putc( p[0], out ); + putc( p[3], out ); + p += 4; + } + break; + + case 3: + im_diagnostics( "mitsub: sending IM_TYPE_RGB ..." ); + for( y = 0; y < vips->Ysize; y++ ) + for( x = 0; x < vips->Xsize; x++ ) { + putc( p[0], out ); + putc( p[1], out ); + putc( p[2], out ); + p += 3; + } + break; + + case 1: + im_diagnostics( "mitsub: sending K ..." ); + for( y = 0; y < vips->Ysize; y++ ) + for( x = 0; x < vips->Xsize; x++ ) + putc( *p++, out ); + break; + } + + /* Form feed. Page end. + */ + putc( 0x0c, out ); + + /* Now try to reset printer to default settings. + * + * No enlargement. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'O', out ); + putc( '1', out ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'P', out ); + putc( 1, out ); + putc( 1, out ); + putc( 0x1b, out ); + putc( '&', out ); + putc( 'Q', out ); + putc( 1, out ); + putc( 1, out ); + + /* No centering. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'C', out ); + putc( '0', out ); + + /* No colour reverse. + */ + putc( 0x1b, out ); + putc( '&', out ); + putc( 'W', out ); + putc( '0', out ); + + return( 0 ); +} + diff --git a/contrib/vdump/Makefile.am b/contrib/vdump/Makefile.am new file mode 100644 index 00000000..49113dd0 --- /dev/null +++ b/contrib/vdump/Makefile.am @@ -0,0 +1,15 @@ +bin_PROGRAMS = vdump + +vdump_SOURCES = vdump.c + +man_MANS = \ + vdump.1 + +pkgdata_DATA = \ + vdump.pro + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ + +EXTRA_DIST = $(pkgdata_DATA) $(man_MANS) diff --git a/contrib/vdump/vdump.1 b/contrib/vdump/vdump.1 new file mode 100644 index 00000000..2c84cd82 --- /dev/null +++ b/contrib/vdump/vdump.1 @@ -0,0 +1,69 @@ +.TH VDUMP 1 "July 1990" +.SH NAME +vdump \- convert VIPS image files to Postscript +.SH SYNOPSIS +.B vdump +[ +.B \-slpDa +] +.IR filename +.SH DESCRIPTION +.B vdump +turns the vasari format file in its argument to encapsulated PostScript on its +stadard output. The result can be +either sent directly to a printer or included in any document processor which +supports encapsulated PostScript diagrams - eg. WordPerfect, Tex etc. For +example: +.IP +.B +example% vdump campin.v | lpr -Plaser +.LP +or +.IP +.B +example% vdump -l -s4 -D campin.v > campin.PS +.LP +.br +.B vdump +normally outputs portrait, you can force output to portrait or landscape with the +.BR \-p +or +.BR \-l +flags. + +.br +The +.BR \-a +flag makes vdump select +either portrait or landscape orientation so as to get +the largest possible image onto an A4 sheet. + +.br +.B vdump +will dump one or three band unsigned char images only. +.SH OPTIONS +.TP +.B \-p +Force portrait output. +.TP +.B \-l +Force landscape output. +.TP +.B \-a +Automatically select portrait/landscape. +.TP +.B \-s +Set a sub-sampling factor (default 1). +.BR \-s1 +will not sub-sample at all, +.BR \-s4 +will reduce by a factor of 4. +.TP +.B \-D +Produce output suitable for including in documents. This option +simply supresses the generation of a showpage command. +.SH SEE\ ALSO +ip(1), vips2dj(1) +.SH COPYRIGHT +.br +1990: J. Cupitt, National Gallery diff --git a/contrib/vdump/vdump.c b/contrib/vdump/vdump.c new file mode 100644 index 00000000..e79ea385 --- /dev/null +++ b/contrib/vdump/vdump.c @@ -0,0 +1,399 @@ +/* This is incredibly primitive and annoying. + * + * Turn a VASARI format file into PostScript. Do simple subsampling of + * images to get the size down .. no point in sending anything much larger + * than 100x100 to the laserwriter if it's going in a document. The output + * conforms to PS-Adobe-2.0 EPSF-2.0, I think. + * + * Options: + * -s Average an nxn area in the image for each pixel in the output. + * This reduces the size of the files significantly (obviously). + * Default 1. + * -l Force landscape output + * -p Force portrait output (default) + * -a Automatic choice of portrait/landscape + * Nasty: as we have to include a %%BoundingBox: line, we can't + * size the image to fit comfortably in whatever size paper this + * PostScript printer takes. + * -D Supress generation of showpage. Sometimes necessary if you + * want to include the PS file in a document. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#define USAGE "usage: [-s -alpD] vasari_format_file" +#define PROLOGUE "vdump.pro" +#define PAPER_WIDTH (8.25*72.0) /* Paper size .. A4 */ +#define PAPER_HEIGHT (11.75*72.0) +#define PAPER_MARGIN (1.0*72.0) /* Margin we leave around the edge */ +#define PRINT_WIDTH (PAPER_WIDTH - 2.0*PAPER_MARGIN) +#define PRINT_HEIGHT (PAPER_HEIGHT - 2.0*PAPER_MARGIN) +#define PRINT_RATIO (PRINT_WIDTH / PRINT_HEIGHT) + +/* Useful: a pixel. We mmap the file, then cast the pointer to the image to + * a pointer to one of these things. + */ +struct pixel { + unsigned char p_red; + unsigned char p_green; + unsigned char p_blue; +}; + +/* A monochrome pixel. + */ +struct mpixel { + unsigned char p_val; +}; + +enum output_format { + LANDSCAPE, /* Rotated by 90 degrees */ + PORTRAIT, /* Vertical */ + AUTOMATIC /* Whichever fits best */ +}; + +static const char *our_name; /* Name of this prog */ +static char *file_name; /* Name of file we dump */ +static int print_on = 1; /* Generate showpage */ + +/* Copy between two fds + */ +static void +copy_file( from, to ) +FILE *from, *to; +{ int ch; + + while( (ch = getc( from )) != EOF ) + putc( ch, to ); +} + +/* Send a file to stdout. Used to transmit the prelude. + */ +static int +transmit_file( name ) +char *name; +{ const char *prefix; + char buf[PATH_MAX]; + FILE *fp; + + if( !(prefix = im_guess_prefix( our_name, "VIPSHOME" )) ) + error_exit( "VIPSHOME not defined" ); + im_snprintf( buf, PATH_MAX, "%s/share/%s/%s", prefix, PACKAGE, name ); + + /* Send it! + */ + if( !(fp = fopen( buf, "r" )) ) + error_exit( "can't find %s", name ); + copy_file( fp, stdout ); + fclose( fp ); + + return( -1 ); +} + +/* Encode a colour VASARI file as mono hex bytes. Scale down by a factor of + * s. We scale by averaging regions of sxs pixels .. is this the best way? + * works ok for our laserwriter anyway. We lose incomplete regions down the RH + * side and across the bottom. + */ +static void +encode_colour( im, s, data ) +IMAGE *im; +int s; +struct pixel *data; +{ int p = 35; + int x, y; + int i, j; + + /* Scan across and down. Make sure we chop off those incomplete + * regions on the RH side and across the bottom. + */ + for( y = 0; y <= im->Ysize - s; y += s ) + for( x = 0; x <= im->Xsize - s; x += s ) { + int col = 0; + struct pixel *rs = &data[y * im->Xsize + x]; + + /* Now average the region. We monochromise each pixel + * and add it to the running total. + */ + for( i = 0; i < s; i++ ) { + struct pixel *d = &rs[i * im->Xsize]; + + for( j = 0; j < s; j++ ) { + col += (int) (d->p_red + d->p_green + + d->p_blue) / 3; + d++; + } + } + col /= s*s; + + /* Output the averaged pixel. + */ + printf( "%02x", col ); + if( !p-- ) { + printf( "\n" ); + p = 35; + } + } + printf( "\n" ); +} + +/* Encode a mono VASARI file as hex bytes. Scale down by a factor of + * s. We scale by averaging regions of sxs pixels .. is this the best way? + * works ok for our laserwriter anyway. We lose incomplete regions down the RH + * side and across the bottom. + */ +static void +encode_mono( im, s, data ) +IMAGE *im; +int s; +struct mpixel *data; +{ int p = 35; + int x, y; + int i, j; + + /* Scan across and down. Make sure we chop off those incomplete + * regions on the RH side and across the bottom. + */ + for( y = 0; y <= im->Ysize - s; y += s ) + for( x = 0; x <= im->Xsize - s; x += s ) { + int col = 0; + struct mpixel *rs = &data[y * im->Xsize + x]; + + /* Now average the region. + */ + for( i = 0; i < s; i++ ) { + struct mpixel *d = &rs[i * im->Xsize]; + + for( j = 0; j < s; j++ ) + col += d++->p_val; + } + col /= s*s; + + /* Output the averaged pixel. + */ + printf( "%02x", col ); + if( !p-- ) { printf( "\n" ); p = 35; } + } + printf( "\n" ); +} + +/* Print the image. Work out the orientation, print the prologue, then call + * one of the dumps above to do the image. + */ +static void +dump( im, format, scale ) +IMAGE *im; +enum output_format format; +int scale; +{ float r, width, height, xstart, ystart; + + /* Fix orientation, then set our origin and output size. Four cases .. + * can any of these be combined? Perhaps not. + */ + r = (float) im->Xsize / im->Ysize; + if( format == AUTOMATIC ) { + if( im->Xsize > im->Ysize ) + format = LANDSCAPE; + else + format = PORTRAIT; + } + + if( format == PORTRAIT ) { + /* Is it going to be smaller than the paper vertically or + * horizontally? + */ + if( r > PRINT_RATIO ) { + /* It's too wide. We make it as large as possible + * horizontally, then center it vertically. + */ + width = PRINT_WIDTH; + height = PRINT_WIDTH / r; + xstart = PAPER_MARGIN; + ystart = (PRINT_HEIGHT - height) / 2.0 + PAPER_MARGIN; + } + else { + /* Too high. Make as large as possible vertically, + * then center it horizontally. + */ + height = PRINT_HEIGHT; + width = PRINT_HEIGHT * r; + ystart = PAPER_MARGIN; + xstart = (PRINT_WIDTH - width) / 2.0 + PAPER_MARGIN; + } + } + else { + /* Do a landscape picture. Will we run out of space + * horizontally or vertically? + */ + if( 1.0 / r < PRINT_RATIO ) { + /* Very wide indeed! Fit it horizontally, then center + * it vertically. + */ + height = PRINT_HEIGHT; + width = PRINT_HEIGHT / r; + ystart = PAPER_MARGIN; + xstart = (PRINT_WIDTH - width) / 2.0 + PAPER_MARGIN; + } + else { + /* Too tall. Make as large as possible vertically, + * then center it horizontally. + */ + width = PRINT_WIDTH; + height = PRINT_WIDTH * r; + xstart = PAPER_MARGIN; + ystart = (PRINT_HEIGHT - height) / 2.0 + PAPER_MARGIN; + } + } + + /* Print header. + */ + printf( "%%!PS-Adobe-2.0 EPSF-2.0\n" ); + printf( "%%%%BoundingBox: %d %d %d %d\n", (int) xstart, (int) ystart, + (int) (width + xstart), (int) (height + ystart) ); + printf( "%%%%Title: %s\n", file_name ); + printf( "%%%%Creator: %s\n", our_name ); + + /* Print prologue. + */ + transmit_file( PROLOGUE ); + + /* Print position, scale and rotation. Print size in pixels and call + * doimage. + */ + if( format == LANDSCAPE ) + printf( "%d %d translate\n", + (int) (xstart + width), (int) ystart ); + else + printf( "%d %d translate\n", (int) xstart, (int) ystart ); + printf( "%d %d scale\n", (int) width, (int) height ); + if( format == LANDSCAPE ) + printf( "90 rotate\n" ); + printf( "%d %d 8 doimage\n", + (int) (im->Xsize / scale), (int) (im->Ysize / scale) ); + + /* Print body of file. + */ + if( im->Bands == 3 ) + encode_colour( im, scale, (struct pixel *) im->data ); + else + encode_mono( im, scale, (struct mpixel *) im->data ); + + /* Print trailer. + */ + if( print_on ) + printf( "showpage\n" ); + printf( "%%%%EndDocument\n" ); +} + +/* Start here! + */ +int +main( argc, argv ) +int argc; +char **argv; +{ int scale = 1; + enum output_format format = PORTRAIT; + IMAGE *im = NULL; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + our_name = *argv; + + /* Decode args .. just look for file names and our three options. + */ + while( --argc ) + if( *argv[argc] == '-' ) + switch( argv[argc][1] ) { + case 's': + if( sscanf( argv[argc] + 2, + "%d", &scale ) != 1 ) + error_exit( USAGE ); + break; + + case 'l': + format = LANDSCAPE; + break; + + case 'p': + format = PORTRAIT; + break; + + case 'a': + format = AUTOMATIC; + break; + + case 'D': + print_on = 0; + break; + + default: + error_exit( USAGE ); + break; + } + else { + /* Try to open the file. If we have previously opened, + * then flag an error. + */ + if( im != NULL ) + error_exit( USAGE ); + file_name = argv[argc]; + if( !(im = im_open( file_name, "r" )) ) + error_exit( "unable to open %s", file_name ); + } + if( im == NULL ) error_exit( USAGE ); + + /* Check it for suitability. We can print colour + * or monochrome pictures. + */ + if( im->Coding != IM_CODING_NONE ) + error_exit( "cannot print compressed pictures" ); + if( !( + (im->Bands == 3 && im->Bbits == 8 && + im->BandFmt == IM_BANDFMT_UCHAR) || + (im->Bands == 1 && im->Bbits == 8 && + im->BandFmt == IM_BANDFMT_UCHAR) + ) ) + error_exit( "can only print mono or colour images" ); + if( im_incheck( im ) ) + error_exit( "unable to get pixels" ); + + dump( im, format, scale); + + return( 0 ); +} diff --git a/contrib/vdump/vdump.pro b/contrib/vdump/vdump.pro new file mode 100644 index 00000000..16e0a771 --- /dev/null +++ b/contrib/vdump/vdump.pro @@ -0,0 +1,25 @@ +%%Pages: 1 +%%Creator: vdump +%%EndComments +%%BeginDocument: vdump +/doimage { + /b exch def /m exch def /n exch def + /pix n string def + n m b [n 0 0 m neg 0 m] + { currentfile pix readhexstring pop } + image +} def +/spotsize { + /perinch exch def + currentscreen 3 -1 roll + pop perinch + 3 1 roll setscreen +} def +/invert { + /curtran currenttransfer cvlit def + /newtran curtran length 3 add array def + newtran 0 {1 exch sub} putinterval + newtran 3 curtran putinterval + newtran cvx settransfer +} def +80 spotsize diff --git a/contrib/vips2dj/Makefile.am b/contrib/vips2dj/Makefile.am new file mode 100644 index 00000000..7c982e3c --- /dev/null +++ b/contrib/vips2dj/Makefile.am @@ -0,0 +1,17 @@ +SUBDIRS = share + +bin_PROGRAMS = \ + vips2dj + +vips2dj_DEPENDENCIES = vips2dj.h + +vips2dj_SOURCES = \ + vips2ah.c \ + vips2dj.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ + +EXTRA_DIST = ${vips2dj_DEPENDENCIES} + diff --git a/contrib/vips2dj/share/Makefile.am b/contrib/vips2dj/share/Makefile.am new file mode 100644 index 00000000..23636af7 --- /dev/null +++ b/contrib/vips2dj/share/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = vips2dj + diff --git a/contrib/vips2dj/share/vips2dj/Makefile.am b/contrib/vips2dj/share/vips2dj/Makefile.am new file mode 100644 index 00000000..e492c393 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = lab cmyk mono + diff --git a/contrib/vips2dj/share/vips2dj/cmyk/Makefile.am b/contrib/vips2dj/share/vips2dj/cmyk/Makefile.am new file mode 100644 index 00000000..22acd499 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/Makefile.am @@ -0,0 +1,14 @@ +vips2djcmykpsbitsdir = $(datadir)/vips/vips2dj/cmyk + +vips2djcmykpsbits_DATA = \ + head1 \ + head2 \ + head3 \ + head4 \ + head5 \ + head6 + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(vips2djcmykpsbitsdir) + +EXTRA_DIST = $(vips2djcmykpsbits_DATA) diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head1 b/contrib/vips2dj/share/vips2dj/cmyk/head1 new file mode 100644 index 00000000..f77a2336 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head1 @@ -0,0 +1,686 @@ +%!PS-Adobe-3.0 +%%Title: (micro_65_macbeth.tif) +%%Creator: (Adobe\250 Photoshop\250 6.0: AdobePS 8.7.0) +%%CreationDate: (2:17 pm Monday, April 29, 2002) +%%For: (FOTOG4) +%%Routing: (mailto:Colin.White@ng-london.org.uk) +%%Pages: 1 +%%DocumentFonts: +%%DocumentNeededResources: +%%DocumentSuppliedResources: +%%DocumentData: Clean7Bit +%%PageOrder: Ascend +%%Orientation: Portrait +%%DocumentMedia: (Default) 612 792 0 () () +%RBINumCopies: 1 +%RBINupNess: 1 1 +%ADO_ImageableArea: 30 33 582 761 +%RBIDocumentSuppliedFonts: +[{ +%%BeginPluginPS: SetTime +/HPDict /ProcSet findresource /SetTime get (20020429140835) exch exec +%%EndPluginPS +}stopped cleartomark +[{ +%%BeginPluginPS: HPJobname +/HPDict /ProcSet findresource /SetJobName get (micro_65_macbeth_tif) exch exec +%%EndPluginPS +}stopped cleartomark +%%EndComments +%%BeginDefaults +%%ViewingOrientation: 1 0 0 1 +%%EndDefaults +userdict/dscInfo 5 dict dup begin +/Title(micro_65_macbeth.tif)def +/Creator(Adobe\250 Photoshop\250 6.0: AdobePS 8.7.0)def +/CreationDate(2:17 pm Monday, April 29, 2002)def +/For(FOTOG4)def +/Pages 1 def +end put +%%BeginProlog +/md 178 dict def md begin/currentpacking where {pop /sc_oldpacking currentpacking def true setpacking}if +%%BeginFile: lw8_feature-1.01 +%%Copyright: Copyright 1990-1999 Adobe Systems Incorporated and Apple Computer Incorporated. All Rights Reserved. +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/Z{0 def}bd +/T true def +/F false def +/level2 +/languagelevel where +{ +pop languagelevel 2 ge +}{ +F +}ifelse +def +/odictstk Z +/oopstk Z +/fcl +{ +count oopstk sub dup 0 gt +{ +{pop}repeat +}{ +pop +}ifelse +countdictstack odictstk sub dup 0 gt +{ +{end}repeat +}{ +pop +}ifelse +}bd +/sfcl2 +{ +/odictstk countdictstack store +count/oopstk xs +}bd +/efcl2 +{ +stopped{$error/newerror F put}if +fcl +}bd +/noload Z +/startnoload +{ +{/noload save store}if +}bd +/endnoload +{ +{noload restore}if +}bd +/setcopies{ +level2 +{ +1 dict begin/NumCopies exch def currentdict end setpagedevice +}{ +userdict/#copies 3 -1 roll put +}ifelse +}def +level2 startnoload +/ststpgdev{}def +/dopgdev{}def +/stpgdev{}def +/buf Z +/didstop T def +/sfcl +{ +/didstop T store +/odictstk countdictstack store +count/oopstk xs +currentfile cvx stopped +{ +$error/newerror F put +didstop +{ +save/didstop xs +/buf vmstatus exch sub exch pop dup 0 lt{pop 0}if +dup 64000 gt{pop 64000}if string store +{ +currentfile buf readline +{ +(}efcl)eq{exit}if +}{ +/UnexpectedEOF errordict/rangecheck get exec +}ifelse +}loop +didstop restore +}if +}if +fcl +}bd +/efcl +{ +/didstop F store +exec +stop +}bd +level2 endnoload level2 not startnoload +/setpagedevice where{pop/realstpgdev/setpagedevice ld}if +/SC_topddict Z +/SC_spdict Z +/$spusrdict F def +/dopgdev +{ +userdict/setpagedevice undef +$spusrdict +{ +userdict/setpagedevice/realstpgdev load put +/$spusrdict F store +}if +SC_topddict realstpgdev +}bd +/stpgdev +{ +SC_topddict dup 3 -1 roll +{ +SC_spdict 2 index known +{ +SC_spdict 2 index get +dup 3 -1 roll +{ +put dup +}forall +pop put dup +}{ +put dup +}ifelse +}forall +pop pop +}bd +/ststpgdev +{ +/setpagedevice where +{ +userdict eq +{ +/$spusrdict T store +}if +}if +userdict/setpagedevice/stpgdev load put +/SC_topddict 0 dict store +/SC_spdict 3 dict begin +/InputAttributes 0 dict def +/Policies 0 dict def +/OutputAttributes 0 dict def +currentdict +end +store +}def +/sfcl/sfcl2 ld +/efcl/efcl2 ld +level2 not endnoload +%%EndFile +%%BeginFile: lw8_basic-4.0 +/xdf{exch def}bd +/:L/lineto +/lw/setlinewidth +/:M/moveto +/rl/rlineto +/rm/rmoveto +/:C/curveto +/:T/translate +/:K/closepath +/:mf/makefont +/gS/gsave +/gR/grestore +/np/newpath +12{ld}repeat +/framewidth -1 def +/QDframwid -1 def +/numframes Z +/mTS matrix def +/$m matrix def +/av 87 def +/por T def +/normland F def +/psb-nosave{}def +/pse-nosave{}def +/us Z +/psb{/us save store}bd +/pse{us restore}bd +/level3 +/languagelevel where +{ +pop languagelevel 3 ge +}{ +F +}ifelse +def +level2 startnoload +/setjob +{ +statusdict/jobname 3 -1 roll put +}bd +/devg/DeviceGray def +/devr/DeviceRGB def +/devc/DeviceCMYK def +level2 endnoload level2 not startnoload +/setjob +{ +1 dict begin/JobName xdf currentdict end setuserparams +}bd +/devg[/DeviceGray]def +/devr[/DeviceRGB]def +/devc[/DeviceCMYK]def +level2 not endnoload +/pm Z +/mT Z +/sD Z +/mTSsetup{ +mT $m currentmatrix mTS concatmatrix pop +}bd +/pmSVsetup{ +/pm save store +}bd +/initializepage +{ +mT concat +}bd +/endp +{ +pm restore +}bd +/adjRect +{ +dup 2 mul 6 2 roll +4 index sub exch 5 -1 roll sub exch +4 2 roll +4 index add exch 5 -1 roll add exch +4 2 roll +}bd +/frame1up +{ +gS +mTS setmatrix +QDframwid lw +/setstrokeadjust where{pop T setstrokeadjust}if +clippath pathbbox +2 index sub exch +3 index sub exch +currentlinewidth framewidth mul +adjRect +numframes dup 0 lt{pop 0}if +{ +4 copy +rS +currentlinewidth framewidth +mul 4 mul +adjRect +}repeat +pop pop pop pop +gR +}bd +/$c devr def +/rectclip where +{ +pop/rC/rectclip ld +}{ +/rC +{ +np 4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +clip np +}bd +}ifelse +/rectfill where +{ +pop/rF/rectfill ld +}{ +/rF +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +fill +gR +}bd +}ifelse +/rectstroke where +{ +pop/rS/rectstroke ld +}{ +/rS +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +stroke +gR +}bd +}ifelse +%%EndFile +%%BeginFile: lw8_level1_colorspace-2.0 +/G/setgray ld +/:F1/setgray ld +/:F/setrgbcolor ld +/:F4/setcmykcolor where +{ +pop +/setcmykcolor ld +}{ +{ +3 +{ +dup +3 -1 roll add +dup 1 gt{pop 1}if +1 exch sub +4 1 roll +}repeat +pop +setrgbcolor +}bd +}ifelse +/:Fx +{ +counttomark +{0{G}0{:F}{:F4}} +exch get +exec +pop +}bd +/$cs Z +/:rg{devr :ss}bd +/:sc{$cs :ss}bd +/:dc +{ +dup type/arraytype eq{0 get}if +dup/DeviceCMYK eq +{ +pop devc +}{ +/DeviceGray eq +{ +devg +}{ +devr +}ifelse +}ifelse +/$cs xdf +}bd +/:sgl{}def +/:dr{}bd +/:fCRD{pop}bd +/:ckcs{}bd +/:ss{/$c xdf}bd +%%EndFile +%%BeginFile: lw8_uniform_graphics-2.0 +/@a +{ +np :M 0 rl :L 0 exch rl 0 rl :L fill +}bd +/@b +{ +np :M 0 rl 0 exch rl :L 0 rl 0 exch rl fill +}bd +/@c +{ +moveto 0 rlineto stroke +}bd +/@w +{ +moveto 0 exch rlineto stroke +}bd +/arct where +{ +pop +}{ +/arct +{ +arcto pop pop pop pop +}bd +}ifelse +/x1 Z +/x2 Z +/y1 Z +/y2 Z +/rad Z +/@q +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +fill +}bd +/@s +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +:K +stroke +}bd +/@i +{ +np 0 360 arc fill +}bd +/@j +{ +gS +np +:T +scale +0 0 .5 0 360 arc +fill +gR +}bd +/@e +{ +np +0 360 arc +:K +stroke +}bd +/@f +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 0 360 arc +:K +$m setmatrix +stroke +}bd +/@k +{ +gS +np +:T +0 0 :M +0 0 5 2 roll +arc fill +gR +}bd +/@l +{ +gS +np +:T +0 0 :M +scale +0 0 .5 5 -2 roll arc +fill +gR +}bd +/@m +{ +np +arc +stroke +}bd +/@n +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 5 -2 roll arc +$m setmatrix +stroke +}bd +%%EndFile +%%BeginFile: lw8_bubn-2.1 +/$t Z +/$p Z +/$s Z +/$o 1. def +/2state? F def +/ps Z +level2 startnoload +/pushcolor/currentrgbcolor ld +/popcolor/setrgbcolor ld +/setcmykcolor where +{ +pop/currentcmykcolor where +{ +pop/pushcolor/currentcmykcolor ld +/popcolor/setcmykcolor ld +}if +}if +level2 endnoload level2 not startnoload +/pushcolor +{ +currentcolorspace $c eq +{ +currentcolor currentcolorspace T +}{ +currentcmykcolor F +}ifelse +}bd +/popcolor +{ +{ +setcolorspace setcolor +}{ +setcmykcolor +}ifelse +}bd +level2 not endnoload +/pushstatic +{ +2state? +$o +$t +$p +$s +$cs +ps +}bd +/popstatic +{ +/ps xs +/$cs xs +/$s xs +/$p xs +/$t xs +/$o xs +/2state? xs +}bd +/pushgstate +{ +currentpoint +pushcolor +currentlinewidth +currentlinecap +currentlinejoin +currentdash exch aload length +np clippath pathbbox +$m currentmatrix aload pop +}bd +/popgstate +{ +$m astore setmatrix +2 index sub exch +3 index sub exch +rC +array astore exch setdash +setlinejoin +setlinecap +lw +popcolor +np :M +}bd +/bu +{ +errordict/nocurrentpoint{pop 0 0}put +2state? +{ +pushgstate +gR +}if +pushgstate +gR +pushgstate +pushstatic +pm restore +mTS setmatrix +}bd +/bn +{ +/pm save store +popstatic +popgstate +gS +popgstate +2state? +{ +gS +popgstate +}if +}bd +/cpat{pop 64 div setgray 8{pop}repeat}bd +%%EndFile +/currentpacking where {pop sc_oldpacking setpacking}if end +%%EndProlog +%%BeginSetup +md begin +%RBIIncludeNonPPDFeature: NumCopies 1 +%RBIBeginNonPPDFeature: WaitTimeout 600 + 600/languagelevel where{pop languagelevel 2 ge}{false}ifelse{1 dict dup/WaitTimeout 4 -1 roll put setuserparams}{statusdict/waittimeout 3 -1 roll put}ifelse +%RBIEndNonPPDFeature +sfcl{ +%%BeginFeature: *HPColorAsGray No +<< /ProcessColorModel /DeviceCMYK >> setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPIntent Colorimetric + + + userdict /UserRenderIntent (RelativeColorimetric) put + + <<>> setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPAutoScaling Off + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *PageSize Letter +/HPDict /ProcSet findresource /SetMargins get [ 0 0 0 0 ] exch exec diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head2 b/contrib/vips2dj/share/vips2dj/cmyk/head2 new file mode 100644 index 00000000..49615f82 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head2 @@ -0,0 +1,160 @@ +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *OutputMode Best + + + << /PostRenderingEnhance true + + /PostRenderingEnhanceDetails + + << /PrintQuality 3 + + /Type 36 >> + + >> systemdict /setpagedevice get exec +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPMaxDetail False + + + << /PostRenderingEnhance true + + /PostRenderingEnhanceDetails + + << /MaxQualityResolution false + + /Type 36 >> + + >> systemdict /setpagedevice get exec +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *MirrorPrint False +<>setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPTransverse False + + +userdict /HPCustTrans known + + { + + (<<) cvx exec + + /Orientation + + userdict /HPCustTrans get + + (>>) cvx exec setpagedevice + + } + + { + + <> setpagedevice + + } ifelse +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPPantone False + + +/SpotColorMatching where { + +pop + +false SpotColorMatching + +} if +%%EndFeature + + +}efcl + +sfcl{ + +%%BeginFeature: *HPColorMan NoEmulation + + + /CMYKColorManagement where { + + pop + + /None CMYKColorManagement + + /None RGBColorManagement + + } if +%%EndFeature + +}efcl + +sfcl{ +%%BeginFeature: *HPCMYKEmulation None + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPRGBEmulation None + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPCyanBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPMagentaBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPYellowBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPBlackBrightness leveleven + +%%EndFeature + + +}efcl + +(FOTOG4)setjob diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head3 b/contrib/vips2dj/share/vips2dj/cmyk/head3 new file mode 100644 index 00000000..685562e1 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head3 @@ -0,0 +1,12 @@ +%RBIIncludeStartNup +/sD 16 dict def +{/Courier findfont[10 0 0 -10 0 0]:mf setfont}stopped{$error/newerror F put}if +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%RBIIncludePageSlotInvocation +mTSsetup +pmSVsetup +initializepage +(FOTOG4; page: 1 of 1)setjob +%%EndPageSetup diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head4 b/contrib/vips2dj/share/vips2dj/cmyk/head4 new file mode 100644 index 00000000..68581c6c --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head4 @@ -0,0 +1,76 @@ +11 -30000 -29999.5 @c +-29990 -30000 :M +psb +gsave %% Print PostScript gsave +/hascolor +/deviceinfo where +{pop deviceinfo /Colors known +{deviceinfo /Colors get exec 1 gt} +{false} ifelse} +{/statusdict where +{pop statusdict /processcolors known +{statusdict /processcolors get exec 1 gt} +{false} ifelse} +{false} ifelse} +ifelse +def +40 dict begin +/_image systemdict /image get def +/_setgray systemdict /setgray get def +/_currentgray systemdict /currentgray get def +/_settransfer systemdict /settransfer get def +/_currenttransfer systemdict /currenttransfer get def +/blank 0 _currenttransfer exec +1 _currenttransfer exec eq def +/negative blank +{0 _currenttransfer exec 0.5 lt} +{0 _currenttransfer exec 1 _currenttransfer exec gt} +ifelse def +/inverted? negative def +/level2 systemdict /languagelevel known +{languagelevel 2 ge} {false} ifelse def +/level3 systemdict /languagelevel known +{languagelevel 3 ge} {false} ifelse def +/foureq {4 index eq 8 1 roll +4 index eq 8 1 roll +4 index eq 8 1 roll +4 index eq 8 1 roll +pop pop pop pop and and and} def +hascolor {/band 0 def} {/band 5 def} ifelse +/setcmykcolor where {pop +1 0 0 0 setcmykcolor _currentgray 1 exch sub +0 1 0 0 setcmykcolor _currentgray 1 exch sub +0 0 1 0 setcmykcolor _currentgray 1 exch sub +0 0 0 1 setcmykcolor _currentgray 1 exch sub +4 {4 copy} repeat +1 0 0 0 foureq {/band 1 store} if +0 1 0 0 foureq {/band 2 store} if +0 0 1 0 foureq {/band 3 store} if +0 0 0 1 foureq {/band 4 store} if +0 0 0 0 foureq {/band 6 store} if} if +blank {/band 6 store} if +blank not { +{} bind +{} bind +{} bind +{} bind +/__settransfer {{dummy1 exec dummy2 exec} +dup 0 4 -1 roll put dup 2 _currenttransfer put +_settransfer} def +band 0 eq { +systemdict /currentcolortransfer get exec +{dummy1 exec dummy2 exec} +dup 0 11 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 10 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 9 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 8 -1 roll put dup 2 7 -1 roll put +systemdict /setcolortransfer get exec} if +band 1 eq {pop pop pop __settransfer} if +band 2 eq {pop pop __settransfer pop} if +band 3 eq {pop __settransfer pop pop} if +band 4 ge {__settransfer pop pop pop} if +} if +gsave % Image Header gsave diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head5 b/contrib/vips2dj/share/vips2dj/cmyk/head5 new file mode 100644 index 00000000..cbade898 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head5 @@ -0,0 +1,101 @@ +level2 { +band 0 eq { +/DeviceCMYK +} {/DeviceGray} ifelse +setcolorspace currentdict /PhotoshopDuotoneColorSpace undef currentdict /PhotoshopDuotoneAltColorSpace undef } if +/picstr1 cols string def +/picstr2 cols string def +/picstr3 cols string def +/picstr4 cols string def +/picstr5 cols string def +/_rowpadstr cols string def +/rawreaddata {currentfile exch readhexstring pop} def +/padreaddata { _topPad 0 gt { /_topPad _topPad 1 sub def pop _rowpadstr } + { _subImageRows 0 gt { /_subImageRows _subImageRows 1 sub def + dup _leftPad _picsubstr rawreaddata putinterval } + { pop _rowpadstr } ifelse } ifelse } def +/image2 level2 {/image load def} {{begin +Width Height BitsPerComponent ImageMatrix +Decode length 2 eq +{/DataSource load image} if +Decode length 6 eq +{DataSource 0 get DataSource 1 get DataSource 2 get +true 3 colorimage} if +Decode length 8 eq +{DataSource 0 get DataSource 1 get +DataSource 2 get DataSource 3 get +true 4 colorimage} if +end} def} ifelse +/_image2 level2 {/_image load def} {{begin +Width Height BitsPerComponent ImageMatrix +/DataSource load _image end} def} ifelse +/beginimage { +band 0 eq band 5 eq or +{image2} if +band 1 ge band 4 le and +{{1 exch sub dummy exec} dup 3 _currenttransfer put +_settransfer _image2} if +band 6 eq +{negative {{pop 0}} {{pop 1}} ifelse +_settransfer _image2} if +} def +/readdata /rawreaddata load bind def +12 dict begin +/ImageType 1 def +/Width cols def +/Height rows def +/ImageMatrix [cols 0 0 rows 0 0] def +/BitsPerComponent 8 def +band 0 eq +{/Decode [0 1 0 1 0 1 0 1] def} +{/Decode [0 1] def} ifelse +band 0 eq { +/MultipleDataSources true def +/DataSource [ +{picstr1 readdata} +{picstr2 readdata} +{picstr3 readdata} +{picstr4 readdata picstr5 readdata pop} +] def} if +band 1 eq { +/DataSource { +picstr1 readdata +picstr2 readdata pop +picstr3 readdata pop +picstr4 readdata pop +picstr5 readdata pop +} def} if +band 2 eq { +/DataSource { +picstr1 readdata pop +picstr2 readdata +picstr3 readdata pop +picstr4 readdata pop +picstr5 readdata pop +} def} if +band 3 eq { +/DataSource { +picstr1 readdata pop +picstr2 readdata pop +picstr3 readdata +picstr4 readdata pop +picstr5 readdata pop +} def} if +band 4 eq { +/DataSource { +picstr1 readdata pop +picstr2 readdata pop +picstr3 readdata pop +picstr4 readdata +picstr5 readdata pop +} def} if +band 5 ge { +/DataSource { +picstr1 readdata pop +picstr2 readdata pop +picstr3 readdata pop +picstr4 readdata pop +picstr5 readdata +} def} if +currentdict end +beginimage diff --git a/contrib/vips2dj/share/vips2dj/cmyk/head6 b/contrib/vips2dj/share/vips2dj/cmyk/head6 new file mode 100644 index 00000000..7bea01fb --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/cmyk/head6 @@ -0,0 +1,10 @@ +grestore end % Image Trailer grestore +grestore % Print PostScript grestore +pse +endp +showpage +%%PageTrailer +%%Trailer +end +%%EOF + diff --git a/contrib/vips2dj/share/vips2dj/lab/Makefile.am b/contrib/vips2dj/share/vips2dj/lab/Makefile.am new file mode 100644 index 00000000..a8aaa0b8 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/Makefile.am @@ -0,0 +1,14 @@ +vips2djlabpsbitsdir = $(datadir)/vips/vips2dj/lab + +vips2djlabpsbits_DATA = \ + head1 \ + head2 \ + head3 \ + head4 \ + head5 \ + head6 + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(vips2djlabpsbitsdir) + +EXTRA_DIST = $(vips2djlabpsbits_DATA) diff --git a/contrib/vips2dj/share/vips2dj/lab/head1 b/contrib/vips2dj/share/vips2dj/lab/head1 new file mode 100644 index 00000000..78210203 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head1 @@ -0,0 +1,600 @@ +%!PS-Adobe-3.0 +%%Title: (Lab_example) +%%Creator: (Adobe Photoshop\250 4.0: PSPrinter 8.3.1) +%%CreationDate: (12:54 martes, 5 agosto 1997) +%%For: (Johan Lammens) +%%Pages: 1 +%%DocumentFonts: +%%DocumentNeededFonts: +%%DocumentSuppliedFonts: +%%DocumentData: Clean7Bit +%%PageOrder: Ascend +%%Orientation: Portrait +%%DocumentMedia: Default 612 792 0 () () +%ADO_ImageableArea: 51 77 561 715 +%%EndComments +%%BeginDefaults +%%ViewingOrientation: 1 0 0 1 +%%EndDefaults +userdict begin/dscInfo 5 dict dup begin +/Title(Lab_example)def +/Creator(Adobe Photoshop\250 4.0: PSPrinter 8.3.1)def +/CreationDate(12:54 martes, 5 agosto 1997)def +/For(Johan Lammens)def +/Pages 1 def +end def end +/md 163 dict def md begin/currentpacking where {pop /sc_oldpacking currentpacking def true setpacking}if +%%BeginFile: adobe_psp_basic +%%Copyright: Copyright 1990-1996 Adobe Systems Incorporated. All Rights Reserved. +/bd{bind def}bind def +/xdf{exch def}bd +/xs{exch store}bd +/ld{load def}bd +/Z{0 def}bd +/T/true +/F/false +/:L/lineto +/lw/setlinewidth +/:M/moveto +/rl/rlineto +/rm/rmoveto +/:C/curveto +/:T/translate +/:K/closepath +/:mf/makefont +/gS/gsave +/gR/grestore +/np/newpath +14{ld}repeat +/$m matrix def +/av 83 def +/por true def +/normland false def +/psb-nosave{}bd +/pse-nosave{}bd +/us Z +/psb{/us save store}bd +/pse{us restore}bd +/level2 +/languagelevel where +{ +pop languagelevel 2 ge +}{ +false +}ifelse +def +/featurecleanup +{ +stopped +cleartomark +countdictstack exch sub dup 0 gt +{ +{end}repeat +}{ +pop +}ifelse +}bd +/noload Z +/startnoload +{ +{/noload save store}if +}bd +/endnoload +{ +{noload restore}if +}bd +level2 startnoload +/setjob +{ +statusdict/jobname 3 -1 roll put +}bd +/setcopies +{ +userdict/#copies 3 -1 roll put +}bd +level2 endnoload level2 not startnoload +/setjob +{ +1 dict begin/JobName xdf currentdict end setuserparams +}bd +/setcopies +{ +1 dict begin/NumCopies xdf currentdict end setpagedevice +}bd +level2 not endnoload +/pm Z +/mT Z +/sD Z +/realshowpage Z +/initializepage +{ +/pm save store mT concat +}bd +/endp +{ +pm restore showpage +}def +/endp1 +{ +pm restore +}def +/endp2 +{ +showpage +}def +/$c/DeviceRGB def +/rectclip where +{ +pop/rC/rectclip ld +}{ +/rC +{ +np 4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +clip np +}bd +}ifelse +/rectfill where +{ +pop/rF/rectfill ld +}{ +/rF +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +fill +gR +}bd +}ifelse +/rectstroke where +{ +pop/rS/rectstroke ld +}{ +/rS +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +stroke +gR +}bd +}ifelse +%%EndFile +%%BeginFile: adobe_psp_colorspace_level1 +%%Copyright: Copyright 1991-1996 Adobe Systems Incorporated. All Rights Reserved. +/G/setgray ld +/:F1/setgray ld +/:F/setrgbcolor ld +/:F4/setcmykcolor where +{ +pop +/setcmykcolor ld +}{ +{ +3 +{ +dup +3 -1 roll add +dup 1 gt{pop 1}if +1 exch sub +4 1 roll +}repeat +pop +setrgbcolor +}bd +}ifelse +/:Fx +{ +counttomark +{0{G}0{:F}{:F4}} +exch get +exec +pop +}bd +/:rg{/DeviceRGB :ss}bd +/:sc{$cs :ss}bd +/:dc{/$cs xdf}bd +/:sgl{}def +/:dr{}bd +/:nmCRD{pop}bd +/:fCRD{pop}bd +/:ckcs{}bd +/:ss{/$c xdf}bd +/$cs Z +%%EndFile +%%BeginFile: adobe_psp_uniform_graphics +%%Copyright: Copyright 1990-1996 Adobe Systems Incorporated. All Rights Reserved. +/@a +{ +np :M 0 rl :L 0 exch rl 0 rl :L fill +}bd +/@b +{ +np :M 0 rl 0 exch rl :L 0 rl 0 exch rl fill +}bd +/@c +{ +moveto lineto stroke +}bd +/arct where +{ +pop +}{ +/arct +{ +arcto pop pop pop pop +}bd +}ifelse +/x1 Z +/x2 Z +/y1 Z +/y2 Z +/rad Z +/@q +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +fill +}bd +/@s +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +:K +stroke +}bd +/@i +{ +np 0 360 arc fill +}bd +/@j +{ +gS +np +:T +scale +0 0 .5 0 360 arc +fill +gR +}bd +/@e +{ +np +0 360 arc +:K +stroke +}bd +/@f +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 0 360 arc +:K +$m setmatrix +stroke +}bd +/@k +{ +gS +np +:T +0 0 :M +0 0 5 2 roll +arc fill +gR +}bd +/@l +{ +gS +np +:T +0 0 :M +scale +0 0 .5 5 -2 roll arc +fill +gR +}bd +/@m +{ +np +arc +stroke +}bd +/@n +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 5 -2 roll arc +$m setmatrix +stroke +}bd +%%EndFile +%%BeginFile: adobe_psp_customps +%%Copyright: Copyright 1990-1996 Adobe Systems Incorporated. All Rights Reserved. +/$t Z +/$p Z +/$s Z +/$o 1. def +/2state? false def +/ps Z +level2 startnoload +/pushcolor/currentrgbcolor ld +/popcolor/setrgbcolor ld +/setcmykcolor where +{ +pop/currentcmykcolor where +{ +pop/pushcolor/currentcmykcolor ld +/popcolor/setcmykcolor ld +}if +}if +level2 endnoload level2 not startnoload +/pushcolor +{ +currentcolorspace $c eq +{ +currentcolor currentcolorspace true +}{ +currentcmykcolor false +}ifelse +}bd +/popcolor +{ +{ +setcolorspace setcolor +}{ +setcmykcolor +}ifelse +}bd +level2 not endnoload +/pushstatic +{ +ps +2state? +$o +$t +$p +$s +$cs +}bd +/popstatic +{ +/$cs xs +/$s xs +/$p xs +/$t xs +/$o xs +/2state? xs +/ps xs +}bd +/pushgstate +{ +save errordict/nocurrentpoint{pop 0 0}put +currentpoint +3 -1 roll restore +pushcolor +currentlinewidth +currentlinecap +currentlinejoin +currentdash exch aload length +np clippath pathbbox +$m currentmatrix aload pop +}bd +/popgstate +{ +$m astore setmatrix +2 index sub exch +3 index sub exch +rC +array astore exch setdash +setlinejoin +setlinecap +lw +popcolor +np :M +}bd +/bu +{ +pushgstate +gR +pushgstate +2state? +{ +gR +pushgstate +}if +pushstatic +pm restore +mT concat +}bd +/bn +{ +/pm save store +popstatic +popgstate +gS +popgstate +2state? +{ +gS +popgstate +}if +}bd +/cpat{pop 64 div setgray 8{pop}repeat}bd +%%EndFile +%%BeginFile: adobe_psp_basic_text +%%Copyright: Copyright 1990-1996 Adobe Systems Incorporated. All Rights Reserved. +/S/show ld +/A{ +0.0 exch ashow +}bd +/R{ +0.0 exch 32 exch widthshow +}bd +/W{ +0.0 3 1 roll widthshow +}bd +/J{ +0.0 32 4 2 roll 0.0 exch awidthshow +}bd +/V{ +0.0 4 1 roll 0.0 exch awidthshow +}bd +/fcflg true def +/fc{ +fcflg{ +vmstatus exch sub 50000 lt{ +(%%[ Warning: Running out of memory ]%%\r)print flush/fcflg false store +}if pop +}if +}bd +/$f[1 0 0 -1 0 0]def +/:ff{$f :mf}bd +/MacEncoding StandardEncoding 256 array copy def +MacEncoding 39/quotesingle put +MacEncoding 96/grave put +/Adieresis/Aring/Ccedilla/Eacute/Ntilde/Odieresis/Udieresis/aacute +/agrave/acircumflex/adieresis/atilde/aring/ccedilla/eacute/egrave +/ecircumflex/edieresis/iacute/igrave/icircumflex/idieresis/ntilde/oacute +/ograve/ocircumflex/odieresis/otilde/uacute/ugrave/ucircumflex/udieresis +/dagger/degree/cent/sterling/section/bullet/paragraph/germandbls +/registered/copyright/trademark/acute/dieresis/notequal/AE/Oslash +/infinity/plusminus/lessequal/greaterequal/yen/mu/partialdiff/summation +/product/pi/integral/ordfeminine/ordmasculine/Omega/ae/oslash +/questiondown/exclamdown/logicalnot/radical/florin/approxequal/Delta/guillemotleft +/guillemotright/ellipsis/space/Agrave/Atilde/Otilde/OE/oe +/endash/emdash/quotedblleft/quotedblright/quoteleft/quoteright/divide/lozenge +/ydieresis/Ydieresis/fraction/currency/guilsinglleft/guilsinglright/fi/fl +/daggerdbl/periodcentered/quotesinglbase/quotedblbase/perthousand +/Acircumflex/Ecircumflex/Aacute/Edieresis/Egrave/Iacute/Icircumflex/Idieresis/Igrave +/Oacute/Ocircumflex/apple/Ograve/Uacute/Ucircumflex/Ugrave/dotlessi/circumflex/tilde +/macron/breve/dotaccent/ring/cedilla/hungarumlaut/ogonek/caron +MacEncoding 128 128 getinterval astore pop +level2 startnoload +/copyfontdict +{ +findfont dup length dict +begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +}bd +level2 endnoload level2 not startnoload +/copyfontdict +{ +findfont dup length dict +copy +begin +}bd +level2 not endnoload +md/fontname known not{ +/fontname/customfont def +}if +/Encoding Z +/:mre +{ +copyfontdict +/Encoding MacEncoding def +fontname currentdict +end +definefont :ff def +}bd +/:bsr +{ +copyfontdict +/Encoding Encoding 256 array copy def +Encoding dup +}bd +/pd{put dup}bd +/:esr +{ +pop pop +fontname currentdict +end +definefont :ff def +}bd +/scf +{ +scalefont def +}bd +/scf-non +{ +$m scale :mf setfont +}bd +/ps Z +/fz{/ps xs}bd +/sf/setfont ld +/cF/currentfont ld +/mbf +{ +/makeblendedfont where +{ +pop +makeblendedfont +/ABlend exch definefont +}{ +pop +}ifelse +def +}def +%%EndFile +/currentpacking where {pop sc_oldpacking setpacking}if end +%%EndProlog +%%BeginSetup +md begin +countdictstack[{ +%%BeginFeature: *InputSlot OnlyOne + +%%EndFeature +}featurecleanup +countdictstack[{ +%%BeginFeature: *InstalledMemory standard + +%%EndFeature +}featurecleanup +countdictstack[{ +%%BeginFeature: *HPIntent Colorimetric + userdict /UserRenderIntent (Colorimetric) put + <<>> setpagedevice +%%EndFeature +}featurecleanup +countdictstack[{ +%%BeginFeature: *PageRegion Letter diff --git a/contrib/vips2dj/share/vips2dj/lab/head2 b/contrib/vips2dj/share/vips2dj/lab/head2 new file mode 100644 index 00000000..f972de6c --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head2 @@ -0,0 +1,3 @@ +%%EndFeature +}featurecleanup +(Johan Lammens; document: Lab_example)setjob diff --git a/contrib/vips2dj/share/vips2dj/lab/head3 b/contrib/vips2dj/share/vips2dj/lab/head3 new file mode 100644 index 00000000..d7a83ad4 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head3 @@ -0,0 +1,9 @@ +/sD 16 dict def +300 level2{1 dict dup/WaitTimeout 4 -1 roll put setuserparams}{statusdict/waittimeout 3 -1 roll put}ifelse +/Courier findfont[10 0 0 -10 0 0]:mf setfont +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +initializepage +(Johan Lammens; document: Lab_example; page: 1 of 1)setjob +%%EndPageSetup diff --git a/contrib/vips2dj/share/vips2dj/lab/head4 b/contrib/vips2dj/share/vips2dj/lab/head4 new file mode 100644 index 00000000..e64faa14 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head4 @@ -0,0 +1,71 @@ +-30000 -29999 -1 1 -29989 -30000 1 -30000 -30000 @a +-30000 -30000 :M +psb +gsave +/hascolor +/deviceinfo where +{pop deviceinfo /Colors known +{deviceinfo /Colors get exec 1 gt} +{false} ifelse} +{/statusdict where +{pop statusdict /processcolors known +{statusdict /processcolors get exec 1 gt} +{false} ifelse} +{false} ifelse} +ifelse +def +40 dict begin +/_image systemdict /image get def +/_setgray systemdict /setgray get def +/_currentgray systemdict /currentgray get def +/_settransfer systemdict /settransfer get def +/_currenttransfer systemdict /currenttransfer get def +/blank 0 _currenttransfer exec +1 _currenttransfer exec eq def +/negative blank +{0 _currenttransfer exec 0.5 lt} +{0 _currenttransfer exec 1 _currenttransfer exec gt} +ifelse def +/inverted? negative def +/level2 systemdict /languagelevel known +{languagelevel 2 ge} {false} ifelse def +/foureq {4 index eq 8 1 roll +4 index eq 8 1 roll +4 index eq 8 1 roll +4 index eq 8 1 roll +pop pop pop pop and and and} def +hascolor {/band 0 def} {/band 5 def} ifelse +/setcmykcolor where {pop +1 0 0 0 setcmykcolor _currentgray 1 exch sub +0 1 0 0 setcmykcolor _currentgray 1 exch sub +0 0 1 0 setcmykcolor _currentgray 1 exch sub +0 0 0 1 setcmykcolor _currentgray 1 exch sub +4 {4 copy} repeat +1 0 0 0 foureq {/band 1 store} if +0 1 0 0 foureq {/band 2 store} if +0 0 1 0 foureq {/band 3 store} if +0 0 0 1 foureq {/band 4 store} if +0 0 0 0 foureq {/band 6 store} if} if +blank {/band 6 store} if +blank not { +{} bind +{} bind +{} bind +{} bind +/__settransfer {{dummy1 exec dummy2 exec} +dup 0 4 -1 roll put dup 2 _currenttransfer put +_settransfer} def +band 0 eq { +systemdict /currentcolortransfer get exec +{dummy1 exec dummy2 exec} +dup 0 11 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 10 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 9 -1 roll put dup 2 7 -1 roll put +{dummy1 exec dummy2 exec} +dup 0 8 -1 roll put dup 2 7 -1 roll put +systemdict /setcolortransfer get exec} if +band 0 ne {__settransfer pop pop pop} if +} if +gsave diff --git a/contrib/vips2dj/share/vips2dj/lab/head5 b/contrib/vips2dj/share/vips2dj/lab/head5 new file mode 100644 index 00000000..fc3b5619 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head5 @@ -0,0 +1,70 @@ +level2 { +band 0 eq { +[/CIEBasedABC 5 dict begin +/RangeABC [0 100 -128 127 -128 127] def +/DecodeABC [{16 add 116 div} bind +{500 div} bind {200 div} bind] def +/MatrixABC [1 1 1 1 0 0 0 0 -1] def +/DecodeLMN [ +{dup 6 29 div ge {dup dup mul mul} +{4 29 div sub 108 841 div mul} +ifelse 0.9505 mul} bind +{dup 6 29 div ge {dup dup mul mul} +{4 29 div sub 108 841 div mul} +ifelse} bind +{dup 6 29 div ge {dup dup mul mul} +{4 29 div sub 108 841 div mul} +ifelse 1.0890 mul} bind +] def +/WhitePoint [0.9505 1 1.0890] def +currentdict end] +} {/DeviceGray} ifelse +setcolorspace} if +/picstr1 cols string def +/picstr2 cols string def +/picstr3 cols string def +/readdata {currentfile exch readhexstring pop} def +/image2 level2 {/image load def} {{begin +Width Height BitsPerComponent ImageMatrix +Decode length 2 eq +{/DataSource load image} if +Decode length 6 eq +{DataSource 0 get DataSource 1 get DataSource 2 get +true 3 colorimage} if +Decode length 8 eq +{DataSource 0 get DataSource 1 get +DataSource 2 get DataSource 3 get +true 4 colorimage} if +end} def} ifelse +/_image2 level2 {/_image load def} {{begin +Width Height BitsPerComponent ImageMatrix +/DataSource load _image end} def} ifelse +/beginimage { +band 0 eq band 4 eq or band 5 eq or +{image2} +{negative {{pop 0}} {{pop 1}} ifelse +_settransfer _image2} ifelse +} def +12 dict begin +/ImageType 1 def +/Width cols def +/Height rows def +/ImageMatrix [cols 0 0 rows 0 0] def +/BitsPerComponent 8 def +band 0 eq level2 and +{/Decode [0 100 -128 127 -128 127] def +/MultipleDataSources true def +/DataSource [ +{picstr1 readdata} +{picstr2 readdata} +{picstr3 readdata} +] def} +{/Decode [0 1] def +/DataSource { +picstr1 readdata +picstr2 readdata pop +picstr3 readdata pop +} def} +ifelse +currentdict end +beginimage diff --git a/contrib/vips2dj/share/vips2dj/lab/head6 b/contrib/vips2dj/share/vips2dj/lab/head6 new file mode 100644 index 00000000..31f60335 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/lab/head6 @@ -0,0 +1,9 @@ + +grestore end +grestore +pse +endp +%%PageTrailer +%%Trailer +end +%%EOF diff --git a/contrib/vips2dj/share/vips2dj/mono/Makefile.am b/contrib/vips2dj/share/vips2dj/mono/Makefile.am new file mode 100644 index 00000000..6fa3d0df --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/Makefile.am @@ -0,0 +1,14 @@ +vips2djmonopsbitsdir = $(datadir)/vips/vips2dj/mono + +vips2djmonopsbits_DATA = \ + head1 \ + head2 \ + head3 \ + head4 \ + head5 \ + head6 + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(vips2djmonopsbitsdir) + +EXTRA_DIST = $(vips2djmonopsbits_DATA) diff --git a/contrib/vips2dj/share/vips2dj/mono/head1 b/contrib/vips2dj/share/vips2dj/mono/head1 new file mode 100644 index 00000000..6e23bccd --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head1 @@ -0,0 +1,686 @@ +%!PS-Adobe-3.0 +%%Title: (Untitled-1) +%%Creator: (Adobe\250 Photoshop\250 6.0: AdobePS 8.7.0) +%%CreationDate: (3:00 pm Tuesday, April 30, 2002) +%%For: (FOTOG4) +%%Routing: (mailto:Colin.White@ng-london.org.uk) +%%Pages: 1 +%%DocumentFonts: +%%DocumentNeededResources: +%%DocumentSuppliedResources: +%%DocumentData: Clean7Bit +%%PageOrder: Ascend +%%Orientation: Portrait +%%DocumentMedia: (Default) 612 792 0 () () +%RBINumCopies: 1 +%RBINupNess: 1 1 +%ADO_ImageableArea: 30 33 582 761 +%RBIDocumentSuppliedFonts: +[{ +%%BeginPluginPS: SetTime +/HPDict /ProcSet findresource /SetTime get (20020430145733) exch exec +%%EndPluginPS +}stopped cleartomark +[{ +%%BeginPluginPS: HPJobname +/HPDict /ProcSet findresource /SetJobName get (Untitled_1) exch exec +%%EndPluginPS +}stopped cleartomark +%%EndComments +%%BeginDefaults +%%ViewingOrientation: 1 0 0 1 +%%EndDefaults +userdict/dscInfo 5 dict dup begin +/Title(Untitled-1)def +/Creator(Adobe\250 Photoshop\250 6.0: AdobePS 8.7.0)def +/CreationDate(3:00 pm Tuesday, April 30, 2002)def +/For(FOTOG4)def +/Pages 1 def +end put +%%BeginProlog +/md 178 dict def md begin/currentpacking where {pop /sc_oldpacking currentpacking def true setpacking}if +%%BeginFile: lw8_feature-1.01 +%%Copyright: Copyright 1990-1999 Adobe Systems Incorporated and Apple Computer Incorporated. All Rights Reserved. +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/Z{0 def}bd +/T true def +/F false def +/level2 +/languagelevel where +{ +pop languagelevel 2 ge +}{ +F +}ifelse +def +/odictstk Z +/oopstk Z +/fcl +{ +count oopstk sub dup 0 gt +{ +{pop}repeat +}{ +pop +}ifelse +countdictstack odictstk sub dup 0 gt +{ +{end}repeat +}{ +pop +}ifelse +}bd +/sfcl2 +{ +/odictstk countdictstack store +count/oopstk xs +}bd +/efcl2 +{ +stopped{$error/newerror F put}if +fcl +}bd +/noload Z +/startnoload +{ +{/noload save store}if +}bd +/endnoload +{ +{noload restore}if +}bd +/setcopies{ +level2 +{ +1 dict begin/NumCopies exch def currentdict end setpagedevice +}{ +userdict/#copies 3 -1 roll put +}ifelse +}def +level2 startnoload +/ststpgdev{}def +/dopgdev{}def +/stpgdev{}def +/buf Z +/didstop T def +/sfcl +{ +/didstop T store +/odictstk countdictstack store +count/oopstk xs +currentfile cvx stopped +{ +$error/newerror F put +didstop +{ +save/didstop xs +/buf vmstatus exch sub exch pop dup 0 lt{pop 0}if +dup 64000 gt{pop 64000}if string store +{ +currentfile buf readline +{ +(}efcl)eq{exit}if +}{ +/UnexpectedEOF errordict/rangecheck get exec +}ifelse +}loop +didstop restore +}if +}if +fcl +}bd +/efcl +{ +/didstop F store +exec +stop +}bd +level2 endnoload level2 not startnoload +/setpagedevice where{pop/realstpgdev/setpagedevice ld}if +/SC_topddict Z +/SC_spdict Z +/$spusrdict F def +/dopgdev +{ +userdict/setpagedevice undef +$spusrdict +{ +userdict/setpagedevice/realstpgdev load put +/$spusrdict F store +}if +SC_topddict realstpgdev +}bd +/stpgdev +{ +SC_topddict dup 3 -1 roll +{ +SC_spdict 2 index known +{ +SC_spdict 2 index get +dup 3 -1 roll +{ +put dup +}forall +pop put dup +}{ +put dup +}ifelse +}forall +pop pop +}bd +/ststpgdev +{ +/setpagedevice where +{ +userdict eq +{ +/$spusrdict T store +}if +}if +userdict/setpagedevice/stpgdev load put +/SC_topddict 0 dict store +/SC_spdict 3 dict begin +/InputAttributes 0 dict def +/Policies 0 dict def +/OutputAttributes 0 dict def +currentdict +end +store +}def +/sfcl/sfcl2 ld +/efcl/efcl2 ld +level2 not endnoload +%%EndFile +%%BeginFile: lw8_basic-4.0 +/xdf{exch def}bd +/:L/lineto +/lw/setlinewidth +/:M/moveto +/rl/rlineto +/rm/rmoveto +/:C/curveto +/:T/translate +/:K/closepath +/:mf/makefont +/gS/gsave +/gR/grestore +/np/newpath +12{ld}repeat +/framewidth -1 def +/QDframwid -1 def +/numframes Z +/mTS matrix def +/$m matrix def +/av 87 def +/por T def +/normland F def +/psb-nosave{}def +/pse-nosave{}def +/us Z +/psb{/us save store}bd +/pse{us restore}bd +/level3 +/languagelevel where +{ +pop languagelevel 3 ge +}{ +F +}ifelse +def +level2 startnoload +/setjob +{ +statusdict/jobname 3 -1 roll put +}bd +/devg/DeviceGray def +/devr/DeviceRGB def +/devc/DeviceCMYK def +level2 endnoload level2 not startnoload +/setjob +{ +1 dict begin/JobName xdf currentdict end setuserparams +}bd +/devg[/DeviceGray]def +/devr[/DeviceRGB]def +/devc[/DeviceCMYK]def +level2 not endnoload +/pm Z +/mT Z +/sD Z +/mTSsetup{ +mT $m currentmatrix mTS concatmatrix pop +}bd +/pmSVsetup{ +/pm save store +}bd +/initializepage +{ +mT concat +}bd +/endp +{ +pm restore +}bd +/adjRect +{ +dup 2 mul 6 2 roll +4 index sub exch 5 -1 roll sub exch +4 2 roll +4 index add exch 5 -1 roll add exch +4 2 roll +}bd +/frame1up +{ +gS +mTS setmatrix +QDframwid lw +/setstrokeadjust where{pop T setstrokeadjust}if +clippath pathbbox +2 index sub exch +3 index sub exch +currentlinewidth framewidth mul +adjRect +numframes dup 0 lt{pop 0}if +{ +4 copy +rS +currentlinewidth framewidth +mul 4 mul +adjRect +}repeat +pop pop pop pop +gR +}bd +/$c devr def +/rectclip where +{ +pop/rC/rectclip ld +}{ +/rC +{ +np 4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +clip np +}bd +}ifelse +/rectfill where +{ +pop/rF/rectfill ld +}{ +/rF +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +fill +gR +}bd +}ifelse +/rectstroke where +{ +pop/rS/rectstroke ld +}{ +/rS +{ +gS +np +4 2 roll +:M +1 index 0 rl +0 exch rl +neg 0 rl +:K +stroke +gR +}bd +}ifelse +%%EndFile +%%BeginFile: lw8_level1_colorspace-2.0 +/G/setgray ld +/:F1/setgray ld +/:F/setrgbcolor ld +/:F4/setcmykcolor where +{ +pop +/setcmykcolor ld +}{ +{ +3 +{ +dup +3 -1 roll add +dup 1 gt{pop 1}if +1 exch sub +4 1 roll +}repeat +pop +setrgbcolor +}bd +}ifelse +/:Fx +{ +counttomark +{0{G}0{:F}{:F4}} +exch get +exec +pop +}bd +/$cs Z +/:rg{devr :ss}bd +/:sc{$cs :ss}bd +/:dc +{ +dup type/arraytype eq{0 get}if +dup/DeviceCMYK eq +{ +pop devc +}{ +/DeviceGray eq +{ +devg +}{ +devr +}ifelse +}ifelse +/$cs xdf +}bd +/:sgl{}def +/:dr{}bd +/:fCRD{pop}bd +/:ckcs{}bd +/:ss{/$c xdf}bd +%%EndFile +%%BeginFile: lw8_uniform_graphics-2.0 +/@a +{ +np :M 0 rl :L 0 exch rl 0 rl :L fill +}bd +/@b +{ +np :M 0 rl 0 exch rl :L 0 rl 0 exch rl fill +}bd +/@c +{ +moveto 0 rlineto stroke +}bd +/@w +{ +moveto 0 exch rlineto stroke +}bd +/arct where +{ +pop +}{ +/arct +{ +arcto pop pop pop pop +}bd +}ifelse +/x1 Z +/x2 Z +/y1 Z +/y2 Z +/rad Z +/@q +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +fill +}bd +/@s +{ +/rad xs +/y2 xs +/x2 xs +/y1 xs +/x1 xs +np +x2 x1 add 2 div y1 :M +x2 y1 x2 y2 rad arct +x2 y2 x1 y2 rad arct +x1 y2 x1 y1 rad arct +x1 y1 x2 y1 rad arct +:K +stroke +}bd +/@i +{ +np 0 360 arc fill +}bd +/@j +{ +gS +np +:T +scale +0 0 .5 0 360 arc +fill +gR +}bd +/@e +{ +np +0 360 arc +:K +stroke +}bd +/@f +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 0 360 arc +:K +$m setmatrix +stroke +}bd +/@k +{ +gS +np +:T +0 0 :M +0 0 5 2 roll +arc fill +gR +}bd +/@l +{ +gS +np +:T +0 0 :M +scale +0 0 .5 5 -2 roll arc +fill +gR +}bd +/@m +{ +np +arc +stroke +}bd +/@n +{ +np +$m currentmatrix +pop +:T +scale +0 0 .5 5 -2 roll arc +$m setmatrix +stroke +}bd +%%EndFile +%%BeginFile: lw8_bubn-2.1 +/$t Z +/$p Z +/$s Z +/$o 1. def +/2state? F def +/ps Z +level2 startnoload +/pushcolor/currentrgbcolor ld +/popcolor/setrgbcolor ld +/setcmykcolor where +{ +pop/currentcmykcolor where +{ +pop/pushcolor/currentcmykcolor ld +/popcolor/setcmykcolor ld +}if +}if +level2 endnoload level2 not startnoload +/pushcolor +{ +currentcolorspace $c eq +{ +currentcolor currentcolorspace T +}{ +currentcmykcolor F +}ifelse +}bd +/popcolor +{ +{ +setcolorspace setcolor +}{ +setcmykcolor +}ifelse +}bd +level2 not endnoload +/pushstatic +{ +2state? +$o +$t +$p +$s +$cs +ps +}bd +/popstatic +{ +/ps xs +/$cs xs +/$s xs +/$p xs +/$t xs +/$o xs +/2state? xs +}bd +/pushgstate +{ +currentpoint +pushcolor +currentlinewidth +currentlinecap +currentlinejoin +currentdash exch aload length +np clippath pathbbox +$m currentmatrix aload pop +}bd +/popgstate +{ +$m astore setmatrix +2 index sub exch +3 index sub exch +rC +array astore exch setdash +setlinejoin +setlinecap +lw +popcolor +np :M +}bd +/bu +{ +errordict/nocurrentpoint{pop 0 0}put +2state? +{ +pushgstate +gR +}if +pushgstate +gR +pushgstate +pushstatic +pm restore +mTS setmatrix +}bd +/bn +{ +/pm save store +popstatic +popgstate +gS +popgstate +2state? +{ +gS +popgstate +}if +}bd +/cpat{pop 64 div setgray 8{pop}repeat}bd +%%EndFile +/currentpacking where {pop sc_oldpacking setpacking}if end +%%EndProlog +%%BeginSetup +md begin +%RBIIncludeNonPPDFeature: NumCopies 1 +%RBIBeginNonPPDFeature: WaitTimeout 600 + 600/languagelevel where{pop languagelevel 2 ge}{false}ifelse{1 dict dup/WaitTimeout 4 -1 roll put setuserparams}{statusdict/waittimeout 3 -1 roll put}ifelse +%RBIEndNonPPDFeature +sfcl{ +%%BeginFeature: *HPColorAsGray No +<< /ProcessColorModel /DeviceCMYK >> setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPIntent Colorimetric + + + userdict /UserRenderIntent (RelativeColorimetric) put + + <<>> setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPAutoScaling Off + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *PageSize Letter +/HPDict /ProcSet findresource /SetMargins get [ 0 0 0 0 ] exch exec diff --git a/contrib/vips2dj/share/vips2dj/mono/head2 b/contrib/vips2dj/share/vips2dj/mono/head2 new file mode 100644 index 00000000..3cbee582 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head2 @@ -0,0 +1,160 @@ +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *OutputMode Best + + + << /PostRenderingEnhance true + + /PostRenderingEnhanceDetails + + << /PrintQuality 3 + + /Type 36 >> + + >> systemdict /setpagedevice get exec +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPMaxDetail False + + + << /PostRenderingEnhance true + + /PostRenderingEnhanceDetails + + << /MaxQualityResolution false + + /Type 36 >> + + >> systemdict /setpagedevice get exec +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *MirrorPrint False +<>setpagedevice +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPTransverse False + + +userdict /HPCustTrans known + + { + + (<<) cvx exec + + /Orientation + + userdict /HPCustTrans get + + (>>) cvx exec setpagedevice + + } + + { + + <> setpagedevice + + } ifelse +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPPantone False + + +/SpotColorMatching where { + +pop + +false SpotColorMatching + +} if +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPColorMan Native + + + /CMYKColorManagement where { + + pop + + /Native CMYKColorManagement + + /sRGB RGBColorManagement + + } if +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPCMYKEmulation None + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPRGBEmulation None + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPCyanBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPMagentaBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPYellowBrightness leveleven + +%%EndFeature + + +}efcl + +sfcl{ +%%BeginFeature: *HPBlackBrightness leveleven + +%%EndFeature + + +}efcl + +(FOTOG4)setjob diff --git a/contrib/vips2dj/share/vips2dj/mono/head3 b/contrib/vips2dj/share/vips2dj/mono/head3 new file mode 100644 index 00000000..685562e1 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head3 @@ -0,0 +1,12 @@ +%RBIIncludeStartNup +/sD 16 dict def +{/Courier findfont[10 0 0 -10 0 0]:mf setfont}stopped{$error/newerror F put}if +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%RBIIncludePageSlotInvocation +mTSsetup +pmSVsetup +initializepage +(FOTOG4; page: 1 of 1)setjob +%%EndPageSetup diff --git a/contrib/vips2dj/share/vips2dj/mono/head4 b/contrib/vips2dj/share/vips2dj/mono/head4 new file mode 100644 index 00000000..6a625041 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head4 @@ -0,0 +1,29 @@ +11 -30000 -29999.5 @c +-29990 -30000 :M +psb +gsave %% Print PostScript gsave +40 dict begin +/_image systemdict /image get def +/_setgray systemdict /setgray get def +/_currentgray systemdict /currentgray get def +/_settransfer systemdict /settransfer get def +/_currenttransfer systemdict /currenttransfer get def +/blank 0 _currenttransfer exec +1 _currenttransfer exec eq def +/negative blank +{0 _currenttransfer exec 0.5 lt} +{0 _currenttransfer exec 1 _currenttransfer exec gt} +ifelse def +/inverted? negative def +/level2 systemdict /languagelevel known +{languagelevel 2 ge} {false} ifelse def +/level3 systemdict /languagelevel known +{languagelevel 3 ge} {false} ifelse def +blank not { +{} bind +/__settransfer {{dummy1 exec dummy2 exec} +dup 0 4 -1 roll put dup 2 _currenttransfer put +_settransfer} def +__settransfer +} if +gsave % Image Header gsave diff --git a/contrib/vips2dj/share/vips2dj/mono/head5 b/contrib/vips2dj/share/vips2dj/mono/head5 new file mode 100644 index 00000000..04bf69aa --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head5 @@ -0,0 +1,27 @@ +level2 { +/DeviceGray +setcolorspace currentdict /PhotoshopDuotoneColorSpace undef currentdict /PhotoshopDuotoneAltColorSpace undef } if +/picstr1 cols string def +/_rowpadstr cols string def +/rawreaddata {currentfile exch readhexstring pop} def +/padreaddata { _topPad 0 gt { /_topPad _topPad 1 sub def pop _rowpadstr } + { _subImageRows 0 gt { /_subImageRows _subImageRows 1 sub def + dup _leftPad _picsubstr rawreaddata putinterval } + { pop _rowpadstr } ifelse } ifelse } def +/image2 level2 {/image load def} {{begin +Width Height BitsPerComponent ImageMatrix +/DataSource load image end} def} ifelse +/beginimage { +image2 +} def +/readdata /rawreaddata load bind def +12 dict begin +/ImageType 1 def +/Width cols def +/Height rows def +/ImageMatrix [cols 0 0 rows 0 0] def +/BitsPerComponent 8 def +/Decode [0 1] def +/DataSource {picstr1 readdata} def +currentdict end +beginimage diff --git a/contrib/vips2dj/share/vips2dj/mono/head6 b/contrib/vips2dj/share/vips2dj/mono/head6 new file mode 100644 index 00000000..81dc27d0 --- /dev/null +++ b/contrib/vips2dj/share/vips2dj/mono/head6 @@ -0,0 +1,11 @@ + +grestore end % Image Trailer grestore +grestore % Print PostScript grestore +pse +endp +showpage +%%PageTrailer +%%Trailer +end +%%EOF + diff --git a/contrib/vips2dj/vips2ah.c b/contrib/vips2dj/vips2ah.c new file mode 100644 index 00000000..782f7f4d --- /dev/null +++ b/contrib/vips2dj/vips2ah.c @@ -0,0 +1,170 @@ +/* Output IM_CODING_LABQ as band-separated ASCIIHEX for PostScript + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +static int +writeimage( REGION *ir, FILE *out ) +{ + IMAGE *im = ir->im; + int x, y, z; + int l = 0; + PEL *p; + Rect area; + + /* Set up input area. + */ + area.left = 0; + area.top = 0; + area.width = im->Xsize; + area.height = 1; + +/* Write a byte. + */ +#define PUT( C ) {\ + int c1 = (C);\ + \ + if( putc( c1, out ) == EOF ) {\ + im_errormsg( "vips2hp2500cp: write error - disc full?" );\ + return( -1 );\ + }\ +} + +/* Write a hex character. + */ +#define writehexc( C ) {\ + int c = (C);\ + \ + if( c < 10 ) {\ + PUT( c + '0' );\ + }\ + else\ + PUT( (c - 10) + 'A' );\ +} + +/* Write a hex byte. + */ +#define writehexb( B ) { \ + int b = (B);\ + \ + writehexc( (b >> 4 ) & 0xf );\ + writehexc( b & 0xf );\ +} + +/* Output a hex byte, linefeed on eol. + */ +#define writewrap( B ) { \ + writehexb( B ); \ + if( l++ > 30 ) { \ + PUT( '\n' ); \ + l = 0; \ + } \ +} + + /* Loop for each scan-line. + */ + for( y = 0; y < im->Ysize; y++ ) { + /* Ask for this scan-line. + */ + area.top = y; + if( im_prepare( ir, &area ) ) + return( -1 ); + p = (PEL *) IM_REGION_ADDR( ir, 0, y ); + + if( im->Coding == IM_CODING_LABQ ) { + /* Do L* ... easy. + */ + for( x = 0; x < im->Xsize; x++ ) + writewrap( p[x*4] ); + + /* a* and b* ... more difficult. Photoshop uses + * bizzare coding for a/b. + */ + for( z = 1; z < 3; z++ ) { + for( x = 0; x < im->Xsize; x++ ) { + int i = (signed char) p[x*4 + z]; + + writewrap( i + 128 ); + } + } + } + else if( im->Bands == 4 ) { + for( z = 0; z < 4; z++ ) + for( x = 0; x < im->Xsize; x++ ) { + int v = p[x*4 + z]; + + writewrap( v ); + } + + /* Extra channel?? Just send zeros. + */ + for( x = 0; x < im->Xsize; x++ ) + writewrap( 0xff ); + } + else if( im->Bands == 1 ) { + for( x = 0; x < im->Xsize; x++ ) { + int v = p[x]; + + writewrap( v ); + } + } + } + PUT( '\n' ); + + return( 0 ); +} + +/* Start here! + */ +int +vips2asciihex( IMAGE *in, FILE *out ) +{ + REGION *ir; + + if( im_pincheck( in ) ) + return( -1 ); + if( !(ir = im_region_create( in )) ) + return( -1 ); + + if( writeimage( ir, out ) ) { + im_region_free( ir ); + return( -1 ); + } + im_region_free( ir ); + + return( 0 ); +} diff --git a/contrib/vips2dj/vips2dj.c b/contrib/vips2dj/vips2dj.c new file mode 100644 index 00000000..f75d13ef --- /dev/null +++ b/contrib/vips2dj/vips2dj.c @@ -0,0 +1,377 @@ +/* Convert lab, cmyk and mono images to postscript. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#include "vips2dj.h" + +static const char *argv0 = NULL; + +/* Geometries for the printers we know about. + */ +PrinterGeometry printer_data[] = { + /* name, paper width, print width, print length, left, top */ + { "2500cp", 2592, 2502, 3728, 51, 82 }, + { "3500cp", 3888, 3786, 5212, 51, 82 }, + { "5000ps", 4320, 4280, 5212, 20, 99 }, + { "4600dn", 594, 570, 817, 11, 15 } +}; + +/* Print a geo entry. + */ +static void +print_printers( void ) +{ + int i; + + printf( "%12s %12s %12s %12s %12s %12s\n", "printer name", + "paper width", "print width", "print length", + "left margin", "top margin" ); + for( i = 0; i < IM_NUMBER( printer_data ); i++ ) + printf( "%12s %12d %12d %12d %12d %12d\n", + printer_data[i].name, + printer_data[i].pwidth, + printer_data[i].width, + printer_data[i].length, + printer_data[i].left, + printer_data[i].top ); +} + +/* Turn a name to a printer geometry. + */ +static PrinterGeometry * +find_printer( char *name ) +{ + int i; + + for( i = 0; i < IM_NUMBER( printer_data ); i++ ) + if( strcmp( name, printer_data[i].name ) == 0 ) + return( &printer_data[i] ); + + im_errormsg( "vips2dj: unknown printer \"%s\"", name ); + return( NULL ); +} + +/* Copy between two fds + */ +static int +copy_bytes( FILE *in, FILE *out ) +{ + int ch; + + while( (ch = getc( in )) != EOF ) + if( putc( ch, out ) == EOF ) { + im_errormsg( "vips2dj: write error -- disc full?" ); + return( -1 ); + } + + return( 0 ); +} + +/* Send a file to out. Used to transmit the preludes. + */ +static int +transmit_file( char *mode, char *name, FILE *out ) +{ + const char *prefix; + char buf[PATH_MAX]; + FILE *in; + + if( !(prefix = im_guess_prefix( argv0, "VIPSHOME" )) ) + return( -1 ); + + /* Send it! + */ + im_snprintf( buf, PATH_MAX, "%s/share/vips/vips2dj/%s/%s", + prefix, mode, name ); + if( !(in = fopen( buf, "r" )) ) { + im_errormsg( "vips2dj: can't find \"%s\"", name ); + return( -1 ); + } + if( copy_bytes( in, out ) ) { + fclose( in ); + return( -1 ); + } + fclose( in ); + + return( 0 ); +} + +/* Send the file to fp. width and height are the size to print at in points. + */ +static int +send_file( PrinterGeometry *geo, IMAGE *im, char *mode, + FILE *out, int width, int height ) +{ + /* Send all the start stuff. + */ + if( transmit_file( mode, "head1", out ) ) + return( -1 ); + + /* Set page size. + */ + fprintf( out, "<>setpagedevice\n", + geo->pwidth, height + 2*geo->top ); + + if( transmit_file( mode, "head2", out ) ) + return( -1 ); + + /* Set mT (margin transform? don't know) + */ + fprintf( out, "/mT[1 0 0 -1 %d %d]def\n", + geo->left, height + geo->top ); + + if( transmit_file( mode, "head3", out ) ) + return( -1 ); + + /* Set rC ... printable area. + */ + fprintf( out, "gS 0 0 %d %d rC\n", width, height ); + + if( transmit_file( mode, "head4", out ) ) + return( -1 ); + + /* Set image params. + */ + fprintf( out, "/rows %d def\n", im->Ysize ); + fprintf( out, "/cols %d def\n", im->Xsize ); + fprintf( out, "%d %d scale\n", width, height ); + + if( transmit_file( mode, "head5", out ) ) + return( -1 ); + + /* Send the body of the image. + */ + if( vips2asciihex( im, out ) ) + return( -1 ); + + if( transmit_file( mode, "head6", out ) ) + return( -1 ); + + return( 0 ); +} + +/* Start here! + */ +int +main( int argc, char **argv ) +{ + IMAGE *im = NULL; + FILE *out = stdout; + int width = -1; + int height = -1; + int dpi = -1; + int max = 0; + int one2one = 0; + PrinterGeometry *geo = find_printer( "2500cp" ); + char *mode; + int i; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + argv0 = argv[0]; + + if( argc <= 1 ) { + printf( +"usage:\n" +"\t%s [options] \n" +"convert LAB, CMYK and mono image files to postscript\n" +"\tLAB printed with printer colour management\n" +"\tCMYK sent directly as dot percent\n" +"\tmono prints as K only\n" +"options include:\n" +"\t-printer \tformat for printer \n" +"\t-3500cp\t\tfor HP 3500CP printer (default 2500cp)\n" +"\t-max\t\tprint as large as possible\n" +"\t-1:1\t\tsize the image to print at 1:1 ... resolution in\n" +"\t\t\timage header must be set for this\n" +"\t-width \tforce specified width, in points\n" +"\t-height \tforce specified height, in points\n" +"\t-dpi \tforce specified resolution (default 150dpi)\n" +"\t-a5, -a4, -a3, -a2, -a1, -a0\n" +"\t\t\tforce specified height (width ignored)\n" +"\t-o \toutput to file (default stdout)\n", + argv0 ); + printf( "supported printers:\n" ); + print_printers(); + return( 1 ); + } + + /* Decode args .. just look for file names and our three options. + */ + for( i = 1; i < argc; i++ ) + if( *argv[i] == '-' ) { + if( strcmp( argv[i]+1, "width" ) == 0 ) { + if( !argv[i+1] || sscanf( argv[i+1], + "%d", &width ) != 1 || width <= 10 ) + error_exit( "bad width" ); + i++; + } + else if( strcmp( argv[i]+1, "height" ) == 0 ) { + if( !argv[i+1] || sscanf( argv[i+1], + "%d", &height ) != 1 || height <= 10 ) + error_exit( "bad height" ); + i++; + } + else if( strcmp( argv[i]+1, "3500cp" ) == 0 ) { + geo = find_printer( "3500cp" ); + } + else if( strcmp( argv[i]+1, "printer" ) == 0 ) { + if( !argv[i+1] || + !(geo = find_printer( argv[i+1] )) ) + error_exit( "bad printer model" ); + i++; + } + else if( strcmp( argv[i]+1, "dpi" ) == 0 ) { + if( !argv[i+1] || sscanf( argv[i+1], + "%d", &dpi ) != 1 || dpi <= 1 || + dpi >= 600 ) + error_exit( "bad dpi" ); + i++; + } + else if( strcmp( argv[i]+1, "o" ) == 0 ) { + if( !argv[i+1] || !(out = fopen( + argv[i+1], "w" )) ) + error_exit( "bad output name" ); + i++; + } + else if( strcmp( argv[i]+1, "1:1" ) == 0 ) + one2one = 1; + else if( strcmp( argv[i]+1, "a5" ) == 0 ) + height = 595; + else if( strcmp( argv[i]+1, "a4" ) == 0 ) + height = 839; + else if( strcmp( argv[i]+1, "a3" ) == 0 ) + height = 1187; + else if( strcmp( argv[i]+1, "a2" ) == 0 ) + height = 1678; + else if( strcmp( argv[i]+1, "a1" ) == 0 ) + height = 2373; + else if( strcmp( argv[i]+1, "a0" ) == 0 ) + height = 3356; + else if( strcmp( argv[i]+1, "max" ) == 0 ) + max = 1; + else + error_exit( "bad flag" ); + } + else { + /* Try to open the file. + */ + if( im != NULL || !(im = im_open( argv[i], "r" )) ) + error_exit( "bad input image" ); + } + + if( im == NULL ) + error_exit( "no input image" ); + + /* Stop used-before-set complaints on mode. + */ + mode = "lab"; + + /* Pick a PS mode. + */ + if( im->Coding == IM_CODING_LABQ ) + mode = "lab"; + else if( im->Coding == IM_CODING_NONE && + im->Bands == 4 && im->BandFmt == IM_BANDFMT_UCHAR ) + mode = "cmyk"; + else if( im->Coding == IM_CODING_NONE && + im->Bands == 1 && im->BandFmt == IM_BANDFMT_UCHAR ) + mode = "mono"; + else + error_exit( "unsupported image type " + "(IM_CODING_LABQ, mono, IM_TYPE_CMYK only)" ); + + /* Make sure width and height are both set. + */ + if( one2one ) { + /* Set width/height from res. + */ + if( im->Xres <= 0 || im->Xres >= 100 || + im->Yres <= 0 || im->Yres >= 100 ) + error_exit( "uanble to print 1:1 - resolution not " + "set in image" ); + + height = (((im->Ysize / im->Yres) / 10.0) / 2.54) * 72.0; + width = (((im->Xsize / im->Xres) / 10.0) / 2.54) * 72.0; + } + else if( max ) { + float iaspect = (float) im->Xsize / im->Ysize; + float paspect = (float) geo->width / geo->length; + + if( iaspect > paspect ) + /* Image aspect ratio > paper ... fit width. + */ + width = geo->width; + else + height = geo->length; + } + else if( dpi > 0 ) { + /* Given res ... set width/height. + */ + height = (im->Ysize / (float) dpi) * 72.0; + width = (im->Xsize / (float) dpi) * 72.0; + } + + if( width >= 0 || height >= 0 ) { + /* Given width or height or both --- set other one. + */ + if( height < 0 ) { + float fdpi = im->Xsize / (width / 72.0); + height = (im->Ysize / fdpi) * 72.0; + } + else { + float fdpi = im->Ysize / (height / 72.0); + width = (im->Xsize / fdpi) * 72.0; + } + } + else { + /* Nothing set ... default to 150 dpi. + */ + height = (im->Ysize / 150.0) * 72.0; + width = (im->Xsize / 150.0) * 72.0; + } + + if( send_file( geo, im, mode, out, width, height ) ) + error_exit( "error sending file" ); + + return( 0 ); +} diff --git a/contrib/vips2dj/vips2dj.h b/contrib/vips2dj/vips2dj.h new file mode 100644 index 00000000..bbbd51c7 --- /dev/null +++ b/contrib/vips2dj/vips2dj.h @@ -0,0 +1,45 @@ +/* Header for vips2dj. + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Geometry for a printer model. + */ +typedef struct { + char *name; /* Printer name (eg. "2500cp") */ + int pwidth; /* Paper width (36/54 inches) */ + int width; /* Printable width, points */ + int length; /* Printable length, points */ + int left; /* Left margin, points */ + int top; /* Top margin, points */ +} PrinterGeometry; + +/* All the models. + */ +extern PrinterGeometry printer_data[]; + +extern int vips2asciihex( IMAGE *in, FILE *out ); diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..c2f953ee --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,13 @@ +# + +prefix = /home/john/vips + +install all: + cd src; $(MAKE) -f Makefile install + echo "Making html man pages" + ./Makehtmlman $(prefix)/share/man/man1 + ./Makehtmlman $(prefix)/share/man/man3 + +.PHONEY: clean +clean: + cd src; $(MAKE) -f Makefile clean diff --git a/doc/Makehtmlman b/doc/Makehtmlman new file mode 100755 index 00000000..806bea96 --- /dev/null +++ b/doc/Makehtmlman @@ -0,0 +1,93 @@ +#!/bin/bash + +# Make html in html/man from the man pages in the named +# directories + +# Config stuff +out_dir=`pwd`/html/man + +# print usage and exit +usage() { + echo usage: $0 dir1 dir2 ... + echo make html in $out_dir from the man pages in the + echo named directories + + exit 1 +} + +# If a dir does not exist, make it +makedir() { + if test ! -d "$1"; then + mkdir "$1" + status=$? + if test $status -ne 0 && test ! -d "$1"; then + exit $status + fi + fi +} + +makedir `pwd`/html +makedir `pwd`/html/man +makedir $out_dir + +# echo the filename given a path +filename() { + echo ${1/#*\//} +} + +# echo the dirname given a path... make sure there's a trailing slash +dirname() { + # break off the filename, then junk that many chars off the end of + # $1 to make the dirname + local file=`filename $1` + local dir=${1:0:$((${#1}-${#file}))} + + # is dir "/"? return immediately + if test "$dir" == "/"; then + echo "/" + return + fi + + # remove trailing slash, provided we're not removing everything + dir=${dir/%\//} + + # if there's no dir, we must be in the current dir + if test "$dir" == ""; then + dir="." + fi + + # finally add a trailing "/" back again + echo $dir/ +} + +# Need VIPSHOME +export VIPSHOME=`vips im_guess_prefix im_version VIPSHOME` + +: ${VIPSHOME:?} + +if test $# -le 0; then + usage +fi + +# Loop over args, adding source man pages +for dir in $*; do + if test -d "$dir"; then + files="$files $dir/*.[0-9]" + else + echo "$0: directory $dir does not exist" + exit 1 + fi +done + +# Make output! +let j=0 +for i in $files; do + file=`filename $i` + dir=`dirname $i` + + ( cd $dir/.. ; + rman -f html -r '%s.%s.html' $dir$file > $out_dir/$file.html ) + let j+=1 +done + +echo "$0: made $j pages of html" diff --git a/doc/README b/doc/README new file mode 100644 index 00000000..9054eb38 --- /dev/null +++ b/doc/README @@ -0,0 +1,15 @@ +VIPS documentation + +edit Makefile and change "prefix" to be your install prefix (eg. +"/usr/local/vips-7.8") ... the HTML man page maker will pull manual pages from +this area + +type 'make' to rebuild postscript and html. You will need gnu make, latex +and latex2html. It will also scan the VIPS man pages and build a set of +HTML versions using the Makehtmlman script. + +src/intro/ has some MS word documents written by Joe Padfield while at the +Hamilton-Karr. + +once everything has built, copy the html and ps directories to +$prefix/share/doc/vips diff --git a/doc/src/Makefile b/doc/src/Makefile new file mode 100644 index 00000000..07ca891f --- /dev/null +++ b/doc/src/Makefile @@ -0,0 +1,65 @@ +# + +PDF = vipsmanual.pdf +SRC = \ + applintro.tex \ + cppintro.tex \ + fileformat.tex \ + func.tex \ + iosys.tex \ + ipio.tex \ + mydefs.tex \ + operintro.tex \ + packages.tex \ + pio.tex \ + refintro.tex \ + vdisplay.tex \ + verror.tex \ + vimage.tex \ + vipsmanual.tex \ + vmask.tex \ + wio.tex + +mkinstalldirs = $(SHELL) ../../mkinstalldirs +destdir = ../ + +all: $(PDF) html + +install: all $(PDF) html + -rm -rf ${destdir}/pdf/*.pdf + -rm -rf ${destdir}/html/vips* + -rm -rf ${destdir}/html/figs + $(mkinstalldirs) ${destdir}/pdf + $(mkinstalldirs) ${destdir}/html + -cp $(PDF) ${destdir}/pdf + -cp -r vipsmanual/* ${destdir}/html + +$(PDF): $(SRC) + pdflatex vipsmanual.tex + pdflatex vipsmanual.tex + +.PHONEY: html +html: + -rm -rf vipsmanual + mkdir vipsmanual + htlatex vipsmanual.tex html.cfg,3 "" -dvipsmanual/ + cp -r figs vipsmanual + +.PHONEY: clean +clean: + -rm -f *.4ct + -rm -f *.4tc + -rm -f *.log + -rm -f *.xref + -rm -f *.tmp + -rm -f *.html + -rm -f *.css + -rm -f *.lg + -rm -f *.idv + -rm -f *.aux + -rm -f *.dvi + -rm -f *.lof + -rm -f *.lot + -rm -f *.toc + -rm -f *.pdf + -rm -rf vipsmanual diff --git a/doc/src/applintro.tex b/doc/src/applintro.tex new file mode 100644 index 00000000..690300fe --- /dev/null +++ b/doc/src/applintro.tex @@ -0,0 +1,51 @@ +\section{Introduction} +\mylabel{sec:appl} + +This chapter explains how to call VIPS functions from C programs. It does not +explain how to write new image processing operations (See \pref{sec:oper}), +only how to call the ones that VIPS provides. If you want to call VIPS +functions from C++ programs, you can either use the interface described here +or you can try out the much nicer C++ interface described in \pref{sec:cpp}. + +See \pref{sec:ref} for an introduction to the image processing operations +available in the library. \fref{fg:architecture} tries to show +an overview of this structure. + +\begin{fig2} +\figw{5in}{arch.png} +\caption{VIPS software architecture} +\label{fg:architecture} +\end{fig2} + +VIPS includes a set of UNIX manual pages. Enter (for example): + +\begin{verbatim} +example% man im_extract +\end{verbatim} + +\noindent +to get an explanation of the \verb+im_extract()+ function. + +All the comand-line vips operations will print help text too. For example: + +\begin{verbatim} +example% vips im_extract +usage: vips im_extract input output + left top width height band +where: + input is of type "image" + output is of type "image" + left is of type "integer" + top is of type "integer" + width is of type "integer" + height is of type "integer" + band is of type "integer" +extract area/band, from package + "conversion" +flags: (PIO function) + (coordinate transformer) + (area operation) + (result can be cached) +vips: error calling function +im_run_command: too few arguments +\end{verbatim} diff --git a/doc/src/cppintro.tex b/doc/src/cppintro.tex new file mode 100644 index 00000000..99e7ba43 --- /dev/null +++ b/doc/src/cppintro.tex @@ -0,0 +1,105 @@ +\section{Introduction} +\mylabel{sec:cpp} + +This chapter describes the C++ API for the VIPS image processing library, +version 7.12. The C++ API is as efficient as the C interface to VIPS, but is +far easier to use: almost all creation, destruction and error handling issues +are handled for you automatically. + +The Python interface is a very simple wrapping of this C++ API generated +automatically with SWIG. The two interfaces are identical, except for language +syntax. + +\subsection{If you've used the C API} + +To show how much easier the VIPS C++ API is to use, compare \fref{fg:negative} +to \fref{fg:invert-c++}. \fref{fg:invert-py} is the same thing in Python. + +A typical build line for the C++ program might be: + +\begin{verbatim} +g++ invert.cc \ + `pkg-config vipsCC-7.12 \ + --cflags --libs` +\end{verbatim} + +The key points are: + +\begin{itemize} + +\item +You just include \verb++ --- this then gets all of the +other includes you need. Everything is in the \verb+vips+ namespace. + +\item +The C++ API replaces all of the VIPS C types --- \verb+IMAGE+ becomes +\verb+VImage+ and so on. The C++ API also includes \verb+VDisplay+, +\verb+VMask+ and \verb+VError+. + +\item +Image processing operations are member functions of the \verb+VImage+ class +--- here, \verb+VImage( argv[1] )+ creates a new \verb+VImage+ object using +the first argument to initialise it (the input filename). It then calls the +member function \verb+invert()+, which inverts the \verb+VImage+ and returns a +new \verb+VImage+. Finally it calls the member function \verb+write()+, which +writes the result image to the named file. + +\item +The VIPS C++ API uses exceptions --- the \verb+VError+ class is covered +later. If you run this program with a bad input file, for example, you get the +following output: + +\begin{verbatim} +example% invert jim fred +invert: VIPS error: im_open: + "jim" is not a supported + format +\end{verbatim} + +\end{itemize} + +\begin{fig2} +\begin{verbatim} +#include +#include + +int +main( int argc, char **argv ) +{ + if( argc != 3 ) { + std::cerr << "usage: " << argv[0] << " infile outfile\n"; + exit( 1 ); + } + + try { + vips::VImage fred( argv[1] ); + + fred.invert().write( argv[2] ); + } + catch( vips::VError e ) { + e.perror( argv[0] ); + } + + return( 0 ); +} +\end{verbatim} +\caption{\texttt{invert} program in C++} +\label{fg:invert-c++} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +#!/usr/bin/python + +import sys +from vipsCC import * + +try: + a = VImage.VImage (sys.argv[1]) + a.invert ().write (sys.argv[2]) +except VError.VError, e: + e.perror (sys.argv[0]) +\end{verbatim} +\caption{\texttt{invert} program in Python} +\label{fg:invert-py} +\end{fig2} diff --git a/doc/src/figs/arch.png b/doc/src/figs/arch.png new file mode 100644 index 0000000000000000000000000000000000000000..2719aea86dfaad9a05e0293eff25c2854a68dc8d GIT binary patch literal 32167 zcmb5VWl$VlxGp-lySoMp?he77;ADckySux)JHdhlcZU!>xVyU(^z^s)IsfmailS(l z?q1XDU5~wyUzMa$5D5`MAP|bIjD#u(1d$BmX&QtY+8UF9 z)`{{eZJleXnjq$ZElX2Ym{JMn1Eph$TyKHz1iDZPvQ`dA_i^C*(~h1o1Kn){10(zp z7}?8OeXqnph1HKG!%iQrT8UDJZefgk?2fOuj^O)iB1S$ZTD<$J){V6TN1I6$l~vQa z>-X93kn#!Y%#^|uE5x60{e5GwQ#K)onB#Hs>T|EGt>>7(AK-rCe@e+8kCTg7kxPn1 zDpe=1gFJ$TpxqwZckpdrc8t7jqCyYTgM~YV{3gm$eXo}sj9H|aWU?bhe~-h8n-Zp+ z-goXKISpg~9sRC@(CIb6SCpsPHE&r3L1k5jywoyKn}0KdZa06(s?w@h_N+OMsHX3d_C$ zs+>Z~Xl({nu!pp>`iB(N+SYmOmA5k;GVD8=$J|*&NV#H(2_#+V0fc7C1JaB{Ryiyg^YFZh=>qPW>u+aLGk**|<#H(6F2Rdr||B4?Dk|Nm|i_1)E% zp-57&Dq^TvQwn6FnVyQI#9-$qrP*e~Hq z{dr*Z7y2U%rQ)R*YCJlI+F^~iJ)T&0gxmIMM`3opD*RF8V>CgU)G*jss|=gFwA@7j z$AHQHoAW_Jt1;ZjBrR%b&a5`X>2Cb(Y>jS)&wS2=*~YKX@-E;lc1lrT;EGSCrO)Jw z4hG4mIe4scleX<)o@}_{)!!aq^cX-)!orID3lSE1m-)L?-w37Mnb8Mg>FfoU)VD5}>u*O>?;6i?Pq;gZ+ zlZTqk?db3ayScPf#izY@wT0H>MBQt?N_uNizrps|Wd{Nn`u1n5tjT=ksC=?CGCzZ> z&$V@aHzrgI))V3%sTvwnb+iSeW_1|vD+ zqbSSk&`-G*r|s^olJvYHNk-zGF$?T;J4N0z{Mgt+Y}aINHo8paDuORl=+%F5nKO+_ z!1E+nF_-<(a$c6loczaWuM{v5cK;jWCOGISQ!rCF?$PVMXp*VLVfkPp=joIM74-Vb z;VANv!Q1PO+^Y{VOyEbpNdB08d!+DKT0IL#a_5xrZW^0$ z1hkEi_ur$74gy{?%Zan#<&y|jF2Oc4&i!=xl{~yu32p)Z$#Pl@y>IqS-q2A z4c>0?7x|Y=Gh;eS<3>Z1Bw&sjY2S@-**PGbJ<*E2VAOZ88$8t;I6nHP$mH56>Ek&% z91^O}D6p#b$Ku|o&)f6q8I*QQTzkBT(xrX3VcbyA#28YoJhP#aBU^Kmwd~uwH;sq4 z@n?Ga57kvNf0Kb=v|qReqnNLk-PwBoT_$=+hWlYk3c}|arb^#uhK}OyQh8P2lKNmj zecAWp?M|mF%hq`KD%-bt0hlW#UF~Ct7B8+?*Zkl}WzhbsC{D@WsJa#AEv&pu(%>K;4O$Q9n+juHuF>HqQmm{tCE zJEw;1y@L>kgZ=etly-Ph)7rQV7l~VXRD!xhg(U*0}=on{KHnzE` zsWQ+@+=`jNCt1E-fpz}jVO6{sXXa|KN^LO6z9&x zLMT^awp9V!{QT;id*^o%mx!DKa>#6+5f-*Y$bfP>v3Sx?yDr!H0t-&b@Ih^Vj z<%?Fe{egc!7b_iac!qc3BBhOxnJ?`O{d7eZaSC}IS75Jc3u7IrW7l%#t8M(HT{kq8 z=zK*EN9vbx5g0gKg-6llHRE>KbOBe+x|z@sndDnh^Agljh5frD@VogaY@&QFjN+2 zpZQ;EFps@bgVVyXAb(qyJiL2?$xex_x}Eg?5?{Y1X52)W>nNrs91Mn_DdMT_=F0(* z-^-P_-Jj9z%z9%>od*+P)5`vfo_u@YZQ+)VXZPHfqxnRg;Irxb(_wI1712@Ylny6l zHZki@np?ZjNg$%k_VCApnzEZW};x1PKs zOhm<}o$_*Z8@@dHE+-PvzAsOUt&Vrgzd~&#C9!ia8iXE|CjWcMNk2*?OXo1R7>+s! z-=7!O$G*3xY*SD`-yIL|Xt4D#>%z;*79JhV%<4aQ*NBN-SuMl_kO=%1dRZ#NBa;7W z#UM#QkfP2_6MpCNe%W!U?RVMrvtlhL?+CGW6A|08`mKtZmm(yG!%+q2F*9djt1o2T zn?;wUE#v~2O<)Zs>oMHJ8UND#J%iQyf!3*DInKIPZ=U7U*!{Q16d zQe^V`YH4GUW_`L$$^U+-LAxf-^*lF%SZE37=KhXr{}+dBm3#4TcP=Zk|H@WP3EPOBH?&70UR^`|1e9^HA@x7;EUK8q*^hCUE18*yHQVtA-u|%411t{ z0Os_@l^5+(K@!hUWY%dJt4w_T>OyC&S`Kw*H z%3j)p8A(h(+ItctC?)2BQj)9s2l6Led!Rk4j}StQh?Sd8(J#~&vBUH&33+n3qk<-3 z3paJ(){_);=t_-PG3D^*_&_2;D85xcHS|>0*}Do(cN6CDioH(xtM4HjiZP40lT1{@ zRINb6`~AY`8L+hqq2ZWQ+_~kJnu~#_G~!_Cmw>Xy1(s~WB=n{X>SQOnQYi?15X6=S zyp|8F{s;7Z)tRAkf9}O81P85{U#o9#75MangNvHNitm4jLOXs2ngoG3q?+MT`qhb? zg`WDv_V08TO;22pW-K2^q!RaEXpE2-3t)dcGZQfnNLd>5f|Mwb9^~+L=govFbRtpV z7}z2cv07R5Pys#(H@qqqXXzW9Tez4J)zTUY>RnK>tF9CnRj{&Y!{q<+lS5JZd5kAC z*10q7!Efgv8tZDjOp*xnJc%S(7`oCd$bmo%QM7`*HVlLU&=LY8$Zr~K&%;MbA(%v+ zELXS5M+PGS(xVCAmE{~Bs7AG}<`_qnf~NrB3xxzu!hw1Sx|n(vH5Nq(dMeHTY%G;! z#}I>8u>|n;|EFCCwr(Ii%2IWq0$TLrzyC4`Q_Sljr310aah9AYL>AXK0xa-ixHl*R z9ZITOPSiu{nfTgW!KVt&;_X*5t2&1=^z-no7{x!;5grbgA-QTJ5e$HH4nkQ9TI8W z7G6Z6|Ch|~tCk*>kn5E3igs9=Jvn+9K@`Ez2DwH*3S@=AQ&j^Oe&{6yWED`t1&Y38 zZ@-K-4n%028iZx9F8CD2OiC+RmYol!bj~CuHxbgFC?+a=@j-O<#P7X>*E|&0qY$Q( z3~4#eS^G9%wQ<4IOtLfu5mFegh9_)}KIp}5z!_nS1)*e^{|GL=CD@axa4<;a#8=Mx zEowP&ULr~1PaWBpceLHMOZ+L14>K+oE?aYIhLa4Qm@a%jOEa=M7<`LiS&f$=!ZB8* zI;h}e!v!h$WYu#RV`uc3V<^YM2Z($K_qh;Edqv9866DgkhF&D8&Hd4BD2o)sP)pRb;5-pm{v*M}s4T?_GzjHds7q+`3Ikax+@aapP3yC{u)(2i{Z`eH zUY^npSy^2coUMXN^o zub`z;!jRMiKEd{yZ5g>QxUWui*h069jqTRmN?#?DfvYj_K)KRlg=kl43(pu0RFw$S znLts*?X~O0yI4Zbw_y{YQJL?K3i%$kPy|UP~CApx`$b+p0V(( zt-I!od{?J@|@|9Corce=Q{8v+2JJ0CY`>+9+&{Z0+}90~wUWY3DZbWUsw zjNeI;*8XPJ3_gKeiO91=Qws+XfKR6z;`F}ARbYwwdOVwZIBJ<5K8}#-m^Kx$^yHM5 z5gk5mw*8Vu12cXf&hvY-$W4&|x7dxtEj31BKtMb(gvu#?s?uTscRdh}+4bbs2)HNx zttG40_l_0O^GWOifp32|+ftjGC6SRTT^xOt|T6t^PWsvljXqAErZwhdlqndOU~^PF`O#tN1b5B0fUC!HF%DDUITkk`Wf%MTdF&rLV;W2l zs*c2$wVb}1Pticss zJ?Fz;o31MxU6G)NMtkLA*Ham%P9AS=*(pI|b#vDb$?++AP%-Hom{=bX~*hCMEsO z6V44$DUQGw&N*n=oILq6d`8-yg^J&J3*XL;O1!20@-RHv(fMf0{ZmhmZt=0J^D8)= zbmnkhWzS>NYyhl%B10NFHR5a>+S&!(=mHxKZgcC#M|Dw{3(`&>QcQ!U?<$BJ-(dDM#zn{vv z`YW_#)1MZ_U=V(#b#hg>yJ@_UySLq$HXNAkrK*gJgu`*4R@mP2giJQQS;`uhY~yFy z?DzSBA;6WljH%guf8~&M-x%!;f&R5v%Odpk7wsU!Z|M0Zp?RMqg`BnT?3sMY5Q^ED zZZndd`3a&B_>o)N;g)J{ZH0wTB_&rG`D7o$t3DhYo_{y<)0OfBPtn9b!w3r>K|op5 zIZ|a35b%;sa~eS-&Cb%py1YGmq^A@hA=Td>i@%9{>`5$VN=j@I5y}51^LJP?g)`0; z%7zkokJ$Ai$>JXRn5IB1F1~(z9I?LCQ}XDzCC?}*QjOgdVqFl9|FnF!`i|A8Rr(7sFJJoePQ3{O3 zU3wAh@iF(`OBl4Jh)GMTh;XICMMcrsO$R(riA;5B=^3K+*61qaum;v1PJY_&xNQBq zNtVxTYvFZ6{YkEG0FA@{!Dja)V^P0s43Ny5EG>y|Z~9LM5q=Si9E^|_pFdsSSuQ7i zgobRnZh@4?^QZnmKppJ?I^DxLwT=_QW^iHxfJP36V=YtimHUQc2X#F^2?YmdW~Cq2 zBu#{-DuD9*^Um}ANO=$CWtu`#s(WOGivtoFlE0$wC^F3GcJMZz>AzZW7t*_5d~jKsaa= z3M7?KX+=JA_GXGBS6bqoSY|$+tudSJ_VEoGz_Gj}r(B-wJ=8R5HM^aj)U?zI`$s+7 zuzqB`3Dr<=gz2_D|7cFrm`kIw{$5{=8S2WgJ38re{U=#Zk0Ff(!4UJ0m}>v2@Z!y# zC7ozfMO?3^VybPAdQ4~9eLAh3y+1A^;5LSJJU+(m(8Qq&tyZH2KO7Hu3%Dh`Oz`Nq zU3R(T0xx)dJ%B&a#mRhASjv=X{q8I%0eyPo{xlNq=e~yY)5|Un>11O7h!GC;>T0@> z6YwD#k7tijj?$Pscb^=U-tVx)&$shbCsdnk=GB=DtNxzT^JR9T32!x`EoO0q2Rk36 z&|riQMe~&o?I>m|2}!4?N%f`c6QNBbfCFC^q<5%uAmY9!C%9rv(EvaH6~N3iYs zdaoVzdvi>q=~&E3YvreT9aJaFy5xqesy`hG=$(yHhkH5iaqHjh3a{VaVm5p7uTK{o zt6F$A;@;n02!Qf6JWo3iK0EE&IuvBO$ZY3+x9I?du|J_t?;-{Xm&QazQm2^NuSrJc zQ`42^AXf#KGY?C|^UHb!bvNR!EPrXUo@xSO^=O8ZiLSr^uv0**Yi-0ziu>iKrdbbY zvj=pBU2oi{(b4pP6wk{Xw|81Y1zYEODs_wVc_}qX>36oi78lP~$@=f2_(8$L_nwM;Y->u3^gf)MKsHZg zk2;XNZ+Nf$TloHZ(Youo&R@(>aQ2!iW^R(AsbylWmXj{67YzH<8gu!Cd{=K=I*-D~ zhyCv#!R~x@w6dn=u@A7!jk?K7_;-*QEHYEMu`;`&pmHDVpGHqbVwGwzNXPM3foxlk zJ6fk}4lud$Ot1iPSgBd9Myx{aE_;+~|b&yf8l($1myY_#BX=s~$DmT88It1x>rbh?u-ew+ z&&%VERcL{ht9n2`S6h-Fep7MAMf85=N;T5gXJi7ScUfY+oX@Uo*=w!y9-8)K{f5;> zBi@qP>>dQ2LP*6?57!f~X->Er>P~#J;?Ji3?{-Vqyk>Xa9&g`THrWjy?S@(OCnu9I z|MP|@sY42uw1pi>9tD+q^SLrTJhz)XIgI^ZVs(v2{`!yMHZ$q#f#U(2SYi3IN6z(l zj_lutsidbx{TP5xXrhrRwd@$}<{(Lp4r(@=>^!gB|H;PFA3yq>{{ON7Cd-|kf_1(E zExEj3+*~*@K-}5ia@RR^CUm* zCEL(-taS>?9ZVgjJ4c$o`}on*v&rF_6JDn4FWsKjp4mi!~4@&XiH- z_sx1o>`8g4{BmZZiWk;Wx33A4b}w1n`$kvhbsyZ_MTkR|2-Gh1T zprRTV-Zg~WOVug1;NpEzYwc@J{^iFT4#1PKhh)55nxB7@nx8G3b)F{YcRg~jvTUPd z#8ubCWf8oF&iA`T31??t^K>w1mbVMh@D0pwS`Z(P8&*(dcl>K7U{3kx@G#u#oXhU@ z$@m1yYE$@O!;NTjdx6SpO2FJ{55BF#o#=3w5qA!WW%{`5amjhpx3Nk4D@)g4To9zq z%*>PfiG=!k`<&;sBtI^;!_9tr?14MKJI!94fCUkHz?7DL{h}k4^7AVFBqgt{?wRLr zE8C>vX$}su3GOaBItkDN7H5??49D@^_7Bz2(E~Q2&{)~olkf<+`e^}kD=-lH;Y6~b zQxGvQ7fo1ZEM5N~Zct+9_J*!?A2&=fYjEVUOZxS9BB$>Wg%lo+N=kxgQ9hY0i2x8n zU<524M$VME;aI-c_m{O!V>s`(x9e=-+`eGg`n!kKe^^^Xc*la4m@Tw4m@V!sqsoGP z3GpHaz5x^21b8HhcLx(T9RL{F^!l9MY-!x7#Q`jSfR&@}?DtAKIUzD>)nQpbY@=+- z9Q74crPVM|&mrQKQg+dmelYt3<8jsAhLAUbSxe8dQ5AGG&JpwP-8*f~jc5#_`#mc~ zHW~k6!@IKryWLKg5SeKBHX4^8y$g+?zx&_q@dbZ8jiMYHA&sKZ>8!Hz%X!PKv?wSI z0K`97diFX5E=@|U*H}75%K&^sgfpa;nVItR&ydeACq3UMsW}dF_M7@ZoLWZ&yY+@DzCULMPq8!gZ1XM-Ss_lL(u zW_L0-@>@$BT{H|1rkG5zl!Q?aw&3Sz;~MwE^hJ-oZ2P~yTcZ4u(pbgV47?fb8yico zC`DgHL4C7j4Uz2mT0rH)#!%$(se2U;a!Q1i$aJP^0yjvZ2BsSiUX;okDC|ursegGj3TW+dA zXy}}km~N_zGaDtJ2ZGN!upjld-z~lM55`<NjBw|2ARe; zSS?<0b^?yDX6d+uyMKpLLCy+{XfvkwR(|fWFH<7R90njQMt!A| z8r@Qq68YR%xq@w;>E3rmcuECo2Vj z1npbqa~Rd*Yka@#nacZk5fMvD_GDy=Ef(@(y=^;uI*aA;p!*cM^!5^Iy_CROg<~C# z>htx-5Y*y6U$|oS@H3H}!Q4Xi8V0sRoxH_D5v7fN&qNt81qOrR+IZ2~l}Dlz?bCbjI`}lc$_qiRq9Y)_w*y`EG8p#9VyXMFn9Sov$YjQsUqeC9 zi#-;P3*XPirg+>QilwnKGi9%9d(t$G*FC5bRaBsrv&}fyv9N+ablqoYD1-PTBXc}H zd>?@IOZIRQ`td?>Nt!HG?++1a++}qO<}HvYa7K=f8GO7RIZjCun;aoacKCQ}bQ6l) zXrOidxJCoR!0kSs)>2Pqs)3z4|CtprccmNw%P)}U*y#_wuy}j)>0E>)_1M^${bt?u zreM721~_h@6jcOK=c67_+7z1r={9q6d#`o8F)0)D{^sro=Kw% zSw#)G72};4mD;NMdj?7Da!J-0E13sA_g!#f+jKeZX8q}C_ocmm{Q^lOw5WA%WeOPS zjyvt~M9Q5}I)iCqWJw=1{64TN%g#0qA`N-{+G@2dgFlkaNZ#{%pVCc8ht zsSz1;{kHY8O2P6+*ny7~Z0&odpw^zO%7C1Kgv=TSO%j;Mu|1n=&d0{Gnp}bU)X%io zcS2N1{ag5oWJ^Vs+MjlG53szj0$M;XVSDKi)oBA;^H=?DeC zH)7l_?a zgp_ms|J>C%fx0DTW`Es;@fFQ-Un2rZ10kWZX_8AI0V%VQPzM5VlJKYUTX8KyXLMSp z2xu|Ibm)GudS8l;eJi(LG|iQdlS!2%+&fNq808n?Pl@yk8k+Zme9n z|C5K+h37|J1~6gXZSR0cR|7 z6}G^?Spu>a{2!m25X}dEDIac~0xgdpdeJ;E-^W61&PrDJ%8lxY4)caYS!EqM2Jq0> zIeuly0n_3k`J@&ioL*nbvYC5cq71;}MIY<{&*UznI8Bs$d9&_ue9 zVGFMMUIwU1^wgO^P4WXti4GVowIXEYaSG5-NGRSJ8GAIBb{#rZ-(UU+QxlhAhe%QN z4bYI>cboJ0H24EPG({a1ZmcOvPaGs5=WoxjggS=P(8Rn70UOEHra_8j4DgWwi>t z3I$5k*(mkS&*B9q(p+HK-N2VrFZc%G>eC!al{=!2nuDk3R-Dd18=;FhTvYyo{nO?zfDUSEtVFSQ0Cm;l2uLwypIjH zjQl|L#Lm-G0l0b4e~J_l1a9j8xH61FLbMwG*RrGyxwAvZNS}B3l|f5vhHj1tMc*1Ajy+Gb70U;5*+ zXV&^R`bisb*vgico9@({?dD`}2ENb4291?{83rtUCa)})mPTYmZ<#Ds&_!FgVw_<` z1@`WbsTObdATqya@i2ya3yIo_e90tm4|y z0z$^?R^?Z~2JpP)J>dT-4hKoAKz@=a9pQ}Z{pJdu$WuHCL)qK#q{Wc?(&hD};|PP2 z&iv^-7>#$Qoc)*fS7jl|26<+Rxe>UrDG_IvVPw^d?rbv*4Cu)$Nx16mgY4xQAYB$3 zy5x37j<&TEjgZF~d4NK+(_)F037jjYlE}76z!C9w>Cfl!@g@--vh}v%P5tpMQ0A*N zbl!sHH>)U7TBgQnPg?QNM`&>LwLgxu^l6DL3ebH!4oQo=9%&lO;a597i8I-P=k&VZ z3Am!9P8XA1U(PuvCQ42IU_V^0n5w6u5X-C!TolN&&ukxC7m%mXN8_| zlDO~5WHnlfg9=_AY47&akLOxg z?){$vQ?7R-Jx`gy0>iqU88rZH2gD}chdwy!g^FNzKj8)2)!)A-_V;#%h=KjZ0i?VM zu<3eozM?I5v70yhIw$9`Spw+;1L0Tz^IxyiqKG39A0N+j5poI%$`u&ZYfX?hGQKX( zIT=R4g7-fpX}8-{GIL`0wD0^v`|@B6WwWTSJe$T8Ybo*+u(YHd^q(cm=gg~KhN>4( ztt;?Kt5hK}Ijw_Dh^TYn)eAOsKhCn)Jfa~dRe9nj~mL8WhOyb-5fb;7S%d{MY$#Rup zA0B%e>v@KnqlMT4#q8fOPk{U`c#DhxdV8KR7{~9=_s<*r_1O*344wcTIM1V>P8SB& zsO^w+WjQ@mwR9{lwshm}`0VBVMNQrU)YtbELoya7wYoN=@)atyrmdn1;n7)tr4wKj$WH1N?Id~I*g(e(>nYX;>{al) z&)-@4N-eY=px5Bfq}FK+yU?AOy3qYC7ObM

j9t!t}lF3?$_cJ>#G?+;nZZZQ9zf-H#krpjF(~|Ge3iRL!|D1KP%)v)EtU zuKRvJK5Hc%pGKQ*iJm|{CrVp@%O>fi*G$VCCl43(`4UqlXHw`JZytHv`c+h(KwcAs zzbL5?5E1)TC-0s|$YhUaf~D3wux#pkXO(9uIgqClteOqcRew1^z7-Jnmao$#&x}M z`2xph?!>UnS}D3bW=hf+_PAlZxYl;p0Imnx0GzQzGnL)AKkW|J19I?@ z=NMBXzhh9p@d(|~axxAeDg&8l;)rPDx*m~@!a~MIEoVzn{~HA9=SN!0c?MYE%d@C2 zxM+{&Vm@?m_|{$Qb!LAJCHR2fk;c5AG$>IsIaOvdiizrWE+@X|NHV6|n&3yPY%~FM zdDOD#0T!0#h{x!tiN5D23@qBeG=ez=K>viUe7X~)!3w9F-xGxpkf+$$G5YEMK1w0& zbA65d`b0JA=!lF8-`nBmi@V{Bw%YxccH#ZN%E_1c6R2cltO1+u=afA;uFi^ictJ>? zl;RI(;?FhY=AqbQx?y_y>UMv?stYN6^uN8<0m+rce;c_QoqX?dU&J6oaWa`%(d7Y0 zacD^8>+(dmr}Jw(ap97q=k>9K^zPw)sGI_0tm~@>B_KE{Rex{bq=ek;7qiLCETiW3 z@m1O0ej*i=nQ0qVuO;1xyBGMbQ&fq32IVgS#nH5|`@d2r{eJ73a_cu>C2r)Le5r7W zj2wQ$$4SkPcij{|obf8nu||eNeHGenOY>1x8&k#|8afJw`<%we!NHfAdAxC89KuTi zfLr#ra@{$<)#gMYYuuN_bHzEJrzod{4@X6a+lJhG0JaUOD=on{EEfPbx(7`*_(eq~ z+1D1&_AG*_WwgnHF}KvXyeGjq9RC5N=J+?sxC+BL5P+&07e2{V7>iQLIBlRX0a)!I zWcSB>RO61$XHOUQdU*)KJ_>*-L?H5$TKH3p4xWLm0@iya>$s4Ct#iI^-fs6wK86lB@b9kf-A@B{YB>79E&ua2u$c z)!*P~o(m9^r>4xeA+hsO#mFU3ANCorIRJSf!uq9i4V{ReJYK^nLU@zovqhbtyr%@! z#49KS+ijWQO4Z$Cj4Ff5G<)ZS%toj~$}8ls8(T?H!yv627ktO#%7H5ZS-WT0Rtz!3 z?OM-i`o*&hiP%pfgz;|ncu)2$-czOE)71Zk&p^Lf6Q~(Pf$ogl{0efFpXAO(?@#XB zL?#6sd5DI_1)gjY6-D6rLshNtub>P9FJ!MyS(3hnpohVr7>+y8-N7_NrUT*O@wiGL z;|GL!Ph__~71w$aGQJ}4|AwUskjUmD|Dc4;~lMw#9hzp^}()^d}#quC+g}masU20lCLb*EEHaR%046na z!zJ~Hdu^Kr*FuFcl%7C}2O5fclS5j-YbzkvW?3GRQCKBH#&9>sfyM&=^?F&-?61kP zSAeQrafdxqYDorCRP{tz3&x|WvR)xwMx66yjtx(LlAuKiz`JmBBw2keYD&va`7E0x z`^mDpS~P9hu{B%uk5%ViLQl$4+Iq`sjtNw;IUvk1`0uK&*hQIoUZBW`Ix_O%#=V4tD? zIsKNxJ8MO`+gJt2n@LD|s*Vn-Ei@22O-iW8P-Cflae~|c1au4>s!<8`ght>xPk;$l zR3uoDH3qO9Mh2kSubiAd(*yQjl;qHV2Yv!RJ1Ff^Tm82uEzo8N0}n+RC>5}nS%$7r zj=tN5R5AZCP1-gk(F$~Lq7HKe5kkHH7mNNWN;o{w8)xb32Zwo>qi{A_%)?5&yZzOc zfxXKCtyEpO4tCd2__k?bbiPhomZmB1rS?Eojtfd^sbfR5pd+1rvQmP+2~8WG5wwtF z?Zjuz{$D#K@)hQE5xWXv3p_OJaz{(5s(gSf`1JhZTTY6C#6DdqKSV7g;NeKV#_|lS znXE#6z){nc=0SGzt*X&ZWdI!QEx;0Ad8xr#8x*X%b>m{83C|Xa?unUlW>BeE4}<#j z<(J4ALeLOK-46_GZ^Kv7T6H;JSiy=W;gc|s)vdd3_F^@pjfUT(1U(LFCvwcdzjAB+ z&v#0}g%#7xQ%hwMc!9M!4HewrFHvaJ|A<~vHVOML6B9@SR05s|JrwBgBLIQr_@GEB z-6lm9&4?QFr7dBj;%ZF~KS^c2in3BH4bNu{f5Qfj{>uZO;-#{=K8Fg{pR-C#hnlSAl$*E>f@_Do+X0pWb)QLR_ggVV;!*ne-SEPAtGZ zMU^Drs@IceXiA*GO3ZT^);s2CLS84}G%JVEm z-P<`aS*hq4*d_?I@QV5d*PCcyP@`H|w!mMAXvRERvQXs%^hV7?W*|L@?&>@_^EER5 z^sy;==Hf;8tl4+3Yv_lxLZ|qpu~qk5V8O2|Naw@rZ{^ z2>lNQ=#6;1?TT9?M-lQ0`|}CQ+x4GL0R(g&XY)WBj0RRVYd8!Q%N=6aL~KBOiYoAB zb&y>0cS1giSwJv?p@c7FsKM63t?0W)4V3~7BHC!@7ktR;#Od(L zDsul+`M3;nOPuY?`1Bvnt)pfZ&&%uIGP{IHSKAClHd?2DV}WAo4$;g<_bhkyXKgZa zzCsCujw?I8ZKL1x3h)gW4;AHTNau2E>qTSgySkc7VSk3ZXly7dvEINKOp$FoJUGA- z3|y?j2&>?(3FkioIt)@YP3 z$zYo`Lyr|$O)`UW9*5LxsS!?~&76aC+pdKy=+G+&6$A?@k~gBj@EwMd8UFAM2MroB zhM{H^O~Xv8=tndi`_1qI{1$_K3;%&OFK)b7+e{wou9L@9J^0s4y2ANaDK9_T53JXjeHSu@A&*{s>JDP znymTcPioahW(uF{snOwSwEXpJ7I&#vla+Q_DJdRHlCYm(_#gK88^v?VNeLc`*wh)6 zQ#29uv&*U8lll5i&t#ndZd-?|+u~=AsjD(QGD(%Lf+F%p@F0*OgS6RD@DYP7266@M zu_y<)xEacMB6L-8y;ZfU*&245;6 zS6vnx6d472zOU5Q{+J2K5fR@huX%XD{_P@4B6=Isz(ic~~0+@R*>&&LETjL{{XB=4fnTFsvL z6*))e;t2l`-njMkZD^H$=w)-anwlhhoYscad=n=YO{Q6z$cd~iqkYh-P2x0$)ta51 z0lhvD0ZFm2EqXPJ|5MjDhUe9^(Z;sfu(8?LM&rh8*r2h|*lEGsHqiO7% z>HD4QI={}pCwot3Yi4e(wdSK`*zQ+~47QQZQPe1tp5om<<4OUq&vAPlFY+_Nh%@f) z{cfW~_W#J!+5WNbmm8by7DVqZb|sN>dE!U2V%)#;Xw=pBKK<$VdEW&Ov$t2;(eoRw zt}n_OWP&aar>Vhcl#e0O(5Vh!1A-Uj7URF?=kZgD>CSx4AwN)3vN=uXB@1uW+5PR* zldhnAei3Ne+LHaw5z_Kd4{q=CCY0lc%FkhWA^CT;#k#o2jEqcqc>^Ggq37nN4H5`t zXRD43TAeQxz7HyO&X%Q6-Q(zwfV9tJj@16k9eZ~_?078G<@HQp$)Hs>;V0kF%k$Md z*>Vx8@|Q0K07Rbr6+?~JF3+ex_8nB79JXzfeWAY>|4(={3;~BX71`dgG+9~N&|9ZX zC7`bFf{H|G-j7BJg1e$9{B6tLV?&~(preMQ+GMo|B3mZkKi`bRmP$2UigmD;t2*|M z8to*Tn~S->?TywTBN$hM&)k;2VKG|lwS&-h)gMC=@dq@PQ>0#A&mP$F^zbm|HicPx zxHjpry4sF&iH)PZyfC+aJ%<+Zf6W{Nqul+RoITrLISdiI7#0B^e`IyF=KhdYoX3vx zWRSK5KtAhdvjMt{|Mnzef36~UzAEl{-e|h;J(&$u_aNfs<-X_CH1@CAGQ`HZo~>TQ ztm=lok?{F8SweP{tGl?E-^Rmu;?l7No8>zC^V~|@>AZ2%eJ=DWYbzvmxQtdN4sqsy zncV*QOLNByCG19F%DeO5#CN3py&S%r_5g}#Y(>kmP@`K9wZtc(R*Sp0yk2v#D zuqqTcEfnuc;yk{_of;Z8vluCF?2Sr&pAmT>=$QTyK`8KT8UjS07}!0Wnf#js7&(o$ zAgO&mlBVDB{sq?a&!WUaG@(8$0b4+eW|=AHXL5fiF8ktucblcC~W!>6NRR`QoWuU4t?VrMyGB=Fibfd|d+ zq+9PO-wc1lyl1Ui&sRDBg_;pz9o?JoHn{aKoE@O!bj{=cV$iI*=`N9>rG)U-6E zN_P9>iEuVuP{Gncva%b>!{6Rz@JTGGzu)AsM6=U3sF9PWoL(gjt7cP!P;FvC+aD|G zhdfywu2|7;aQOWjt`D61V3>P2%sSmWg3`A_!U+#ws;!f|vnR~siy8Ne({tsT7b*@5 zrT(0{@N?{=B_)QI7OcZgz=O~C@A&P7<7qu5Kb;S`ym``3ml4whQgQ}lYl*qL7ki-l z!x2ImkRtN}q=V-A;eQ1{%r>4Z72d3WOKSR}#IWFZpt5;MMyjN1RlKy1jDaP-RBbXd zU-f56Dy2xtad?Q5l@~uIF_Ax~Scmh3lXFnWD=9Eem{otHsIG3Qdvia`xxr?E@W{?G z%(cyHVNGeiRxD{pafbP~y7*hcjB@oK;Taqup$(t5KW{+ra&XY4iWGV{Crbypb;F$T zIU56!1Tx>=Yj7z-!PFM8a-R#i&ENJNn1$u)(TH@&!naKbG{lJ%?f#B|InJDkz`mMw zP>`nt`J9G9CqiWM=z{tllqkD11k<9 z?!5F(WSwl`W?elqOpt@%Z-Y-cwwN?6?bE)i4zD<`u zekA(GtzV#9CS)5~DNM9QAI+nlF7`%vJlt*QHnHeI{*LJ}+m&zN6XN;CBp^^u#8WU7 zL;MkPrYy|NhJi0YR+;AU!4+${$?{dguu-i)XS~;iJ!P((!s^_(6{t?E-L7M6*m{eN z_KN=T&&YQ$(g&6=)NgefyDXNpBsp~e8r73$Kmx~mnazg|hP+g>hlNk4twhLsYo(>Z zPm;v62d;amK6h@RW~N*RdzjDJs%RE3>vdcNF`Fl*sNs`Yp@`A`80=U-f||`Jz8XWl zpp$d)%low9vCLi_ZDXEKc!rv#IT~f5eZ&qM*!VI>A;<-KI=S^WA-;aXOQ^)*M!&w1 zVW_H_**Nxf3N5e*CUjco9EGF$!py3|lJI6#g+^O7i+cs}VM z;2c_C&1Uca7k2d1Oy@-i{<+ms{X~0(o{UGC)(jDMRrd0~4Jl9OcI5X>FCI*MoqUDD;d0Iu zs4@}m8)rp{gY2C*a=uA?e*ge;^^otpl2o1>0W|t}Cc;@EWr~UyfAry6e{}RMfW!-m zf+c}=zUs$u3~_WsB<12F;rB$^2qG?7J9S@0HaWg{f<(2SebV%_DXh->pr*y)Oq03h z5yUe$r`_A1$m2*E#zj<*hNeyOt}_u(>@6%4E$w3${a=UWL;)lo)xm)ToyeRASToNpg8F3uY#jr>ANKMe3#k zv*CoLhLVJ@qG!(!iV_lpHRj0k>z?qwckVc_KE5@>L*mXNdKx@TCGgV^VtfYytahem zgRwEpk5Q$JNpHtU0MG6U3618bG^(xlO+nXELk8ywJXEuDn2)bahok3=9@C>D1AO7! zh`B~!&|vd&BQfvUn+s|YGRMD^hvxaSZ4r?Si?Jga5g&k;w%RIZ5%*@Mrmn~7LSw5*UI?x3g$Hk9XGCG}TpipL>x4m}qgc)E>tqFTUC?h}I|L z0?%rp+;B9R%#U1xWM zj*0d+@F0+Z2Ult2pK&4!346OxuAPqAPb>1m;YjJ)=wKO8a_xcON#N2?|A1awPz;U9 zP>kaP`AWAdH3U7tIj-XT#}MwjZd_Pm_ZGz`nz+ty%)RK;Sj`+8#!C0?)`lMAGd$aF!56#^tJS9#`n>V2w(zZ;z;vf)4A*2-B?n? zB+CsH9@~8#4Vq$=Wnw4LF)DOT{``606abN>{WGCaSvlm95y|w%)(4%e$Ve0ddVl;! zgr8mzoW}hwK0gc$+xfiF;l`%w-P;FXC2y7;Tm)Ss#RS}^3|=*D>ns+0ADNkxt8 zPL5WbN1X$oOW-rPrtA1CcEI^%kJ~U_3k=LTuq^deTByi9&Y;k+7KS#-OCCV`jVbr1 z@W17n`;!$JMrWt%@qtEDB@rhw1~j~y>#6QiHh*8v>aWAe{4oCc$T)N!uozL22Ydnl zetSJXi;i)yn@fqh-VA&OEPg61%8j}?(nL%XkF1#5;`|5l9^CbYPCv+p5RgvgUf(6vJ!=muzK!|twV4N^p zYT|fCG7je0X*##QquXzO@__h%#;+vsVwe>LIQ?c_P-8GK7>pU%}I?kGn))82o5*224L6m7(YzWGDXh-U(W5sIB87>l{9i0Fcjz*B4>cR3nF|k zR8o0lZ}0rDta$~<-K~5nDY2dX{KK_OAqWs2xnDD2KVEfRZ;07Lle`n12u8GFYjo%+ zboJubY4ff_H~wR|#cE*MJ&~({j{4SYLo_{xsPAF)`)pGw_or~EyB+NGzTU4_GwLL8 z+U*(PdQGW!E6yIlA@L@_4mosSI^J`*^VlYgf8w#u?xrf|D^>r4T%80y$ww?#+5iHskPNWF+kn8<5 zbt)I4J9HS++D%ToS-;kBrx&m}IY;>2EmAP5O`ugVFozJ;12m+ z(y%IL$Yz@LH=COU&9=A(XY)xA;Vi{V$9&eS4-YW%e-0zX?0c!RCruG~2>%4J`b*vY z$>RvbKpR(w_3s=5iMM~3bZi129gj&xPlhexndPU2=@2hPr2&;zhu;Zf8+I0i38%> zzFjaH>3scaY6`gQ%XwUX{@QlglH7CvRopXX|&dySC@4b^pZMZY{bKar5k z&=do~wS#4c>H9q;Ll7q4tgYEJ-<#Gz!gkJ8gt$@jr{g@=_cqvUUfr&TxP9!8g|akP zdqmy+0c|)_{3S^El|hru+3oksja}`vHNem)LZ>H)e~Ux6e{j{)8!^zYelJfcAe_ZS zOk7UP(@R5LaG>%FxY#U))M`f13}dP6Bvrb}>e#eu4oh78zgkQIYGS|rF7P-C5A$k- zb0%FllLw@<@(lzU%{CBlV3|dg(THZC&~RV^r;C0h?rsMq`ssb=d3WqKNpxmph$&7D zJQtJ$#IRpb{fgxY1tSYeT=N3d;`?*wb}5Gi1VvV;vVZe$^6V`1R1mfM8{`vVzyaLbT4pHusz#SHH^Co}1x$lOZ=k2Uw@JrmJlxmKj?+QFx0TH>yXg4qTew7Bj#B_SquQ#W`4|gh`IwACN zVNZW#Z>6Njyu_0BzIoRG%rYNX^PqhSvo~bqLlS&hKG*n9K0dRh zeiTK|%4&Yz7qKb6xn1iS2BE9Jzv}yfo_8PBxT_HEb5U_ zRAMQypJlX#fM*)%loI#bADn0(K>q zJc_deHGONjxhm)V{JsiK?_%A`-Cr3vT^*CngYZ?rm%f~<=vEnO;q_cPPhizg>n0PO4WZl8n5vm^kVkN|%0pA*$)Afvekn)62b?;UNb18n?4P`3=$>OePX|2R~a=U+TiKEB2fQ zD0^rlk*z`bo-9GO$iU{$UnWA359@3R=f=hs{GZr6WTf%n)VMMXTp!)G0O;eONGV!Q zfhu1fxBTVN$n7{tDwb?KRpQ9)LhQjl@L(6L}PTz7ty~nP}r5X@s`RzXvi%E^2EB8mc=_F30)QPKXGXVkl#! z{@Ct~Y}!m0X&AJnZ@Qm-1+K@wfJ>(UcSH5>YX_Faj<(-#Rf?TAhkX1~9jgcthoeCb zdT%3O7G!N~R|tMhsMI<+o%x&C+JQ0LeX}JsrQo%0KZCFg)|y{FKdm&Bq`vHpI?5_g zi7=H{=jiB}%{8;Tsj7ZlZ&%9NWCz)IYp(DuB}??-?vQ$q8=WN&kLp^kvD=L+52#q4 zU8B&P`Fy$|I#K+7;^C~Xr-Von6Yd=-GJdSC zxPQ7p#<2VY`o3Y?!uEEM!AEKx9i7`IDjuFxPj54KU~gUODDiUrd~Huo**kpF1VY`U zRdcM>PdGUyf4?VGhLQCGHa4besIq+m*OTa7%5;;-iGckI5%O{^Zsz0g?RtVRtxjopDCn>$c&5U$dD;4&z!cthHtj)_kVo|e}dm#;d^o3 zLs3N~+E{8AG6Z-Vg2cr$C%izvS3gQyTO{F@lCK3n5}UlZ_kpU%(fwubSX$2@G`xI< z=7pm%*Jq^q>&?WtPB$7wWZnj~gfMdnRF6~afjanv01EN&sZ{KN;_*u|e+QEZ9P zgd^pNgb+aT!NVIf!w916g#i}!t@Li2#rbU5*L{Q?==@-eLKW$l^{E=SiU%QCsr~N` z&5$c>4R09Cmmyh8U=S!O=_03y&*7Dr?+zv2wWIGE&8MSv04^}c!m~EA!#+L6?_-8@ka-% zTIj5GWKMCViu{6iub08NLYpW4=*QH`M{q@u9gg~xnrox3T`A{2mjAjc*>JY9KvK74>mnAO1 zU8b`frr4RN^tZ(9$Kos$yxkY*(a50RDE55frkb)&LB(@lRhNy!P>eCFMG;hoF0((J zrA$X)mjI)7mPq5MmVI+nR?p`c7#;*qw%k7DP5FtY&uWDr#bWdvLiu<)oqL-XyHw*0 zQ+x*k8CU8a(*xfF@pIS-5yn-4bwgs3DAqagc?rfAMLv`WM)F+N-QC7Y5o%T^qOb09 zqb*0Syt(9k)aY9vGrP38*_xC!rpIf4-hD7RGvgrO7X1UPJm18`q7{_PAB_Cj>$NJn zC;s+?sc#=?W+^Fbj}FMv0F++W{2~Z)W}fc3I^1IRIXFCi*&CGmKgHiTI<_W&9w#Ri zeJ+)VpiFxaR)9u^^YNUAl+5MD{be*<@5?ia&#gi0gs)(|4KAs}+75Rc3&Eb<5RR%<*6%%6nxC8z&C+CWjw!5tZ&~Nz<{eQQ{vRLCnHU$+@Fp(bn=Ir!lT{@m)pyHKNl`mqqd>CfQ9xd z?3avnUzz1#7(*omvxjwm&hCDhnDWhy03FZ}u(=JvyNZt8+YLds&K0Rtr_F$ZBKVfn zOH5C%xZIKbrKn|%U?I^7Q@L**f7zkKe55N#-+C>>{D(4Ttqtz@Q4fo^$Hv1}l*{80 zW32Esj)-POWDf|S$uI!P@#zA2`{rabvWZtAg=Kf3J0#j>UO6+WK#N)5$ScBrIK{cU zKkRKy%}3zbi_cd+X|(Z2ePiA}X!VS~y6$s3)-vU_qul6*6;J0fkxF5)g^?UhfA^uR z2(M+a&Kd+6q`U1sA#t6&d38aw14@8n$P!(<)=EwDJw3k!~HKi=5kT|1OQcIq$p=!9RHE zL5I2eGcFGt-MK2uuP+NR#GWsAj^aMPIz0Zu@cSbs9^+ZCka(F!KJjNAPu+wL4&Pwl z68tuyB0$L3sLPEvnmzs{PQ*g{m)kpl3*1_2AVEf|0MRWVl{XFma#t$# zQXFjNEe0=kGk9+rSdzrO)mUt35^1+H@euIHXE$ zhHsYsbkK4E2NU>S6SKI-Ad-5x&JV?~q4TN0v$J9F1S!+w@!Yr=PFsS}mxm}ug%m8O zEto+g-@bn;$$D*GUDSd;FMR#6WVF`THkA4K5&nm7i4Oe3Qt<6hmo`(=<=2XoLMBCG z=x8Jm;%S2izL|0jw!IqAFYDPWdhX&9SvOdL3Sz#A$;b9P%Ko~#LBDIwhTjmNs7Ybu zuT*`uC^Xl?9dmlZ_Fb5C{mSJGNOmQI(liPJIlEv+s^d1&9^vK)Vc_1_uC6W9qK=N$ zH`YAr;q5-Lh0@!ueunwK9@rGxG5yOhirB1U$5LUB`8RbBicjsb*a2Rp&f zEv_iD*{qCFwgxULkl~z`k#Jti?U{bedTOr^u6aQC3k2cKEqEhITGW1V_N320^$i?X z8p?8{yDANicEnpOJ-^hMTv-NG=_XoG(!n5<%V61i1A&RzJ6o)3B~xj@SWhM3hGA(DIt6?((89_x zEGCz9(rTih5LcQ-#J|0m6i}w$7_@ABGM1@lVF}@6AzDBcdN5b5AVQ;kk0qT;DmP&> z>E&rchX3O?^-4!aIO$-qszd`7FjB^w;w`p|-N^Lx1)w}D1N2X94h;tVCHNkpg!$8x z+I_7{#@C0C>Oi`gka(ETR~PuvFk?_CZ=z*@(~XJwQA#>trCrEVDZTo}et+HysfTaA zEKI4xuO2LJeO+B2`T0!ZzlyrIK@Sd7t*^8i&(%U*!EWE!sV&uaLBB(1^J#vdak}TQ zuB_BPx?w(xhAF1NgF`WHYT)*^7%y6sA!BzL#KIGnC8?c~c)*Ha$AI;ZmXd^_r;^z+ zv4GR9NOuQP)BG;s$6f~MJj@4C=Di3uHVsx)`V>Nxfgi#Ah{Aiy$Tq57)3wVbb#(5r z8NlFoJ?yRpEH zWI#dx81a2+Kv8#giRc)lEGKKIlwV%1 zrmY5y8q0Gq!g=h@v)G}lXtQ#bb_QL50Mgi}1}l7)oxBR>M!lw~UkdmcZLQUwuf9Gl z?jM%ad!^8WU9Y@2ReAU|8J?I-v zjTKSDR!XYV7=5hE45CUe_mlAHxr(bs5TXoXRptmso%t)no^cWpv~(uYa6Q}wFjAD1sAc;tpW}W4w&OW*SGtgU^r+K)WD+^KV$ORW8mY7q$K*0`rw(nTC1c^_M*!vG*b zSvgny;dAtOYxUOz8I2gnJ4i1}0UfhMNB*1D3=hooYfvL$CPh)aS zpi!XNRK*^TCS{5eZT}t%YXC6zHNmsKW(s&xu*VnFnCajW=@JxAu4i{hN9Wwnl*}NY z(b(+Bx$ROE5i24a{0WleVPYweh2gM>2 z``NP}?Ik)w^%kfz6ae1S-NUg97pH{vYPxG^BhYsjx0&C&b=*F98e-J==@xSiXj#bN zPd@b&6v393`IGzjc+#dHsp$jGulwvC-CDGDO$l)%fr542TV8xBlv29*KyO=&K))N- z^G$ASdYAXz-KoF{^dHNzD|PFNl>G798KEWk5y2~B#}Fqp=Z^;(6wh~EGKorq?T}1? zpKNAu`-&9`eJ09gsn;Zeeny=s?$2-Fces-Xf-V-y86s~j<>lj?*wc^wS|3@KS@l)Q z!iwgkDB}g$c8@%LUw!@15$R77zb3)$+H%c5FEqy!s2{61(xo(Mt8Ib>VhH)=<%rEs zu0$w z;S?*w5t&N$66R;lkH0CvI!}$+OGu(|9?y?r)Iv&1yl|RDYrhWitsq~(pH*>C$1xXR zYrL|=e7dEJ+=6&QL7`W?Mhbq}-)Jb?NHJkpjr^O+#~cw6pZ}BLUGjqMT5dRQ(zoZYv zy6Bn@n{jeAw;ob?mV4~;Yd@o}WkBgV7-#0u1x2$@De#(pBHci>AWD4|Nwujh;*m>J zm&=^XM@Ri8ZFrH&#tp+_8=O3z<2TNZrfnA--G6uiZMaZ;L=i8@u{&o?IHy=|eD#x? z8jGrW>;1bmm`Yw~VND$KAPmZ~awm11KDav#gUg=*S))hYr9y+QfvKjQ347oV+wxpF{`uo3AlmAW) z$^WGf80I3o7nEaoSDU7wW(|&F$sJOB|4E&U51Pj(z4>=o=YNM)oj6mNo)Y8(!1ZP2 z<7Bd}RVh97|2s$cubwL(`rirkf7huWuwaQOxcVv)^!ESuMC4>uT`)xnx=v7&m4n^4&|DRdhQ+90q4gj~?kMQS{vgwYp(9NwNwo7{1KKW$CJZ}B5~!@u z13U~%L~duL;xtLM&uyg?y$WQHFSjm+rP&ZwNz%lx1rZJRK} zdqEx_=>Xy-XmeRzmKr^btpVzG@3a4EHwA^HuFejK!_{$iG1wr`uHn%HPfFxfE4*`8 z{P80SsGs1E@strMwnkhSjY?m)bh}V=1WG(EsvY`q5A3{5Wtw*ZJaq_f_1@vWmDw+9hT)@c8JKt29^O{RcoUO-j9toURfG>)^FR9INyX-dkCtY*pT39Y^7<$w%*y7>JDl=&=u(41`Tx+NwI@mL7 zX=I4*k)(oNgR0*YI}DHpWBeHA2NI1-P_&So#qK!Wd*58Mu=3R+GQX|kH1iYo9X}*N>{K*+hXr9E%sJa|epdRXs2xvD zUX`JQ`G{9zUlzjRm=uvN;LqrQdE)5Ou=i=j?Q?MkrF+?vU35EFQ@GkT1~mTmSm3 zks@VTkx_DdC2oI23SDL3Zg`u(VQ%J{dV#iL3%VpFpa>q|X{3qF(|~M*cwbCEJKn>K zY%-%{b_^^d$;<4Z8Ac>`FDL)m=M5hjd^LLjW5=( z`x0bd-3F_FfE2+jdhkP27N!W9!jHQ!G?Xs#g6W)lqex*8z6p6^>pg)Jx5XkL38p=?1&y85NfvUW!9(DAb=@Bc_L#&Exs5RVw&pW$6a)w zWPgLS(=6IYU}A#`qDmTZ^FEHk~9#{l$C=l{a z(O41m1ClZ9O{5DH!LmMIgWfEp;qb|-+6G9ADrLS?FX=Z>h>jMmaO;P56s^Xf?5}4K z5{%5#hBho7PY|%Fs|5?@Z*d6v;&C++49eiU`OymWT}9xGOH#7;p1k~${rA(!p@1_U z{2HX9$^<9kW#14U{@3PWK-LocNu3}g&5!->j3CEU+P{C|c0|AwIon^)R7S$ZA@zrvLvcf)ZWi(y(-!sSEq zb!_Jia5)IIp|nfsnXydvxCtDQM|MMiF=K+y*i<)qr>2lX9`HV3vDT;%JRv=#lg09I@b1NHSf=D4Z zvMkjKLDPr9YN~m{KD93j=*K$ zm9;bl(@-y9b0Y!K(p|jGR{fYdUUmrEdT+u9L$VoyHUxuIsY2CLj31jcGS3C-MQz^V z6_G47RUMGu4uCg zax-PHmDl6idHKQqu3Yx*-~tz;b~Rt%rd5(I!SQ#)XCf z8j?{3rj;Oe=_gIR?3wc0s-JN&!PY;&+Jz96IeaPM=HQ-6D;6wyxr_+%A}rDbet43< zZNmf$RIDfDd-BW(7aD$|l2;$B_y#cYE=21i=lK%7islLg!MVs^4^k*TaJWeSit|Zz2prQ_zlZNb)CAA^@X)T+=&=83l)04FZ|yj12!*@d<#!qo?0l zZ3&4+WA+pUAF6j`1}TX(7_Y&CvM~B1h<5c`dJnb^0ph86^4u^C?Hy1&Ki)9Tng&4F zt%fI`HNcS0B<3Y(0-5dd3do6@0_>>1+K}uw zrp=6Ovg_|WeRFKSOI+z-|1G}@>cPlB6&$t&mNoxuB`vLy?|g~ARMHdnM;b&BOXI@% zdio@Tvt^eJ--(A?+jLr78N1%@R?r=g`7!7C?voOYrT1;zu3LuHaT+Zz*L_|WW_f(P z^l^M1l3!}b7t7>dNPTFR^(2TN>%}-!j8tJ5lcd@`bGl=T*xZyHPZd-5P0z~#>Wm=w z;H=zKDfQf~+d<07dA(GrC$m$cu1_pXVk9LtT~B}Q{)dK+F-lMr8&~T0H|E|X0DR`0 zgh(gkJg75A?(cZb!Vsl`N&jF-K~5b!P}NcNNp7Ej4Pw6I)k0DpNnKqn_2|$0_oB{6 zI6Xg<3$r%cQ&=xY>c+S1?PJ~MSsr@({>p=NeowbY*EDt%v)%znWw99f4XU%Vw*tih zC5X}U&1M6ToVT`aB_tdS=3S!`{8M9wVpttu@#TwPNy9`IY}#fvcqCyOdT}bgm{OrCx@$@Z8hkGF`zLx9g$xq z$MtjP$avh6jx|`NaWu)Qm$A$WH3#b!Bp1z*?`8_$zF|j<{8D9>wMz1%;4GRl188${sAmg#M z_+)-)XE(f)CS zckUWbF6)qJlxO6o5hX zTfLI&PnUmAn~wyeSz%-l-&*T?OuIxfD5IONnj3C%RhmuD4&n zDATI20)zJl#OTw#KG^%L!(!BQ;&gcgVDSv`@T^?-aroP~0*c)1GBPqggMA52KFxCg zab<#pWGO8Rn#BNVRg4c+SXJvHZ$qw%i@UFbVWvn)YnbKS=?m9J3dPg{Txg^)c5?!> z&;XU@hEc&FHKt7PG|_GussPg$}Kym#PSbx=CKTla4eE3K!LhF?BV(FVejtI*=dgH#mpQw z0ANfJ52nK!$$TZj@@bzTti+K6FyXJW9}qvg@0b0^-tz~zI9-td(K0DByO=x?|Buv7 zmlp%qmu7{zAh%=b`iR4IFD7QEpb`l9<#V7!`rilB;bi{?lqPc1(mn!UMQ58gpLz3W zwSxq$6iTrn6B7pQdk!10Byw2?o+7hu+^>0GDan!x^L#7cdc^0gf4%j$JzfAR!aNgY z67VGO=)X_@ba*axLq*9YzSB=wi^897F;_ULC>PBaOZ+Bn14dfF&Ad^Bq5{=*n&rXk zFl)vH+2x!bm(PQ>&C*id&Rzm53?*GSj;QxD|7O1Qq{{%t3P9n2JypgQmrQ?|!9V|U z?_cbP|E^PKwNi%9(y~tOf+qQ?I(Cy^>N`SVQrdtpw#1H1l+;(FPGUf{xd#ZuC9rFx zC0p|uJ9|NJNQo~dgo%k{Z$JK(9Zt?{-b$<7g`UGC5RcDravDHHquILO*D>-j3fTEr zDxMtJ9lsJPuU-Q0S_AC;Xm8c)yuDZYb-Je)(f%@onMvb`?l; zO;_EY1Fa)uif*Nnn38zzM9P8mh@H(;o)~E$_}RJk2GDpm5o$>^p+HFg-`dTOJ=o)T zLh#qx+7@7G2cDjWgbVG=#dwYhC^O|Sk%V=VWM6PC#}j;RmzL#&fem~*vd=nR=wLT+ zBhR|_w2Z@JE zCYGuzf35lHLKmq9j{sGkyUl}e*EhotT=nv;`J3olP20G=fBXUJkyo+guVfeEnt zme;>NMGLs=Gmr&cvj-_29#k|}D)>K#{DSM8I459~`EG|Z71PwFjXEJrRCpr}pkO_AC1lIW6jEt| zwN2HMhroAVfz6cAL0kybc~ifD#yBTdhN6B9Jl1HjB;06XsQ&Cw9|3Z*RF+_E3!Bj) zD$pLY#+ocu(&>r64 z$CIKllCeQ^9|ehx0o=S~u^+PjS%5Tq%r_8YZL;BiwSY#n|Ehs%>Pyi_JRvenWQ==W zgCYKVqXm8{O_03hkID;jG>pDtc`Wd@^*B^|3P$k60Y@RNAAG?V5LszMtOig7u&OA~ zRu;{G?BiGzWoYmI}$FhC={&-c3Ov!lTwzf6gLk1KQyv; AXaE2J literal 0 HcmV?d00001 diff --git a/doc/src/figs/arch.svg b/doc/src/figs/arch.svg new file mode 100644 index 00000000..6e44ee30 --- /dev/null +++ b/doc/src/figs/arch.svg @@ -0,0 +1,385 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + Python binding + + + + C++ binding + + + VIPS imageprocessingoperations + User imageprocessingoperations + + VIPS IO system + Function dispatch + + + + Command-lineinterface + + + + diff --git a/doc/src/figs/interconvert.png b/doc/src/figs/interconvert.png new file mode 100644 index 0000000000000000000000000000000000000000..bc98d461178640ece6845fc8898a6c3c42e75694 GIT binary patch literal 26322 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Lab + + + + XYZ + + + + LCh + + + + disp + + + + Yxy + + + + UCS + + + + LabQ + + + + LabS + + + + ICC + + + + + + + + + + Any VIPS RGB space + Any device withan ICC profile + + diff --git a/doc/src/fileformat.tex b/doc/src/fileformat.tex new file mode 100644 index 00000000..f80ae96b --- /dev/null +++ b/doc/src/fileformat.tex @@ -0,0 +1,160 @@ +\section{The VIPS file format} + +VIPS has its own very simple file format. It is used inside VIPS to hold +images during computation. You can save images in VIPS format if you want, +but the VIPS format is not widely used and you may have problems reading +your images into other packages. + +If you intend to keep an image, it's much better to save it as TIFF, +JPEG, PNG or PBM/PGM/PPM. VIPS can transparently read and write all these +formats. + +\subsection{VIPS file header} +\label{sec:header} + +All VIPS image files start with a 64-byte header giving basic information +about the image dimensions, see \tref{fg:header}. This is followed by the +image data. This is usually just the pixel values in native format (ie. the +byte order used by the machine that wrote the file) laid out left-to-right and +top-to-bottom. After the image data comes a block of optional XML which holds +extra image metadata, such as ICC profiles and image history. +You can use the command-line program \verb+header+ to extract the XML from an +image and \verb+edvips+ to replace it, see the man pages. + +The \ct{Type} field, the \ct{Xres}/\ct{Yres} fields, and the +\ct{Xoffset}/\ct{Yoffset} fields are advisory. VIPS maintains their value +(if you convert an image to \cielab{} colour space with \ct{im\_XYZ2Lab()}, +for example, VIPS will set \ct{Type} to be \ct{IM\_TYPE\_LAB}), but never +uses these values itself in determining the action of an image processing +function. These fields are to help the user, and to help application +programs built on VIPS which are trying to present image data to the user +in a meaningful way. + +The \ct{BandFmt}, \ct{Coding} and \ct{Type} fields can take the values +shown in tables~\ref{fg:bandfmt}, \ref{fg:coding} and \ref{fg:type}. The C++ +and Python names for these values are slightly different, for historical +reasons. + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|} +\hline +Bytes & Represent & VIPS name \\ +\hline +0--3 & VIPS magic number (in hex, 08 f2 f6 b6) & \\ +4--7 & Number of pels per horizontal line (integer) & \ct{Xsize} \\ +8--11 & Number of horizontal lines (integer) & \ct{Ysize} \\ +12--15 & Number of bands (integer) & \ct{Bands} \\ +16--19 & Unused (legacy) & \ct{Bbits} \\ +20--23 & Band format (eg. \ct{IM\_BANDFMT\_USHORT}) & \ct{BandFmt} \\ +24--27 & Coding type (eg. \ct{IM\_CODING\_NONE}) & \ct{Coding} \\ +28--31 & Type (eg. \ct{IM\_TYPE\_LAB}) & \ct{Type} \\ +32--35 & Horizontal resolution (float, pixels mm$^{-1}$) & \ct{Xres} \\ +36--39 & Vertical resolution (float, pixels mm$^{-1}$) & \ct{Yres} \\ +40--43 & Unused (legacy) & \ct{Length} \\ +44--45 & Unused (legacy) & \ct{Compression} \\ +46--47 & Unused (legacy) & \ct{Level} \\ +48--51 & Horizontal offset of origin & \ct{Xoffset} \\ +52--55 & Vertical offset of origin & \ct{Yoffset} \\ +56--63 & For future expansion (all zeros for now) & \\ +\hline +\end{tabular} +\end{center} +\caption{VIPS header\label{fg:header}} +\end{tab2} + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|l|} +\hline +\ct{BandFmt} & C++ and Python name & Value & Meaning \\ +\hline +\ct{IM\_BANDFMT\_NOTSET} & \ct{FMTNOTSET} & -1 & \\ +\ct{IM\_BANDFMT\_UCHAR} & \ct{FMTUCHAR} & 0 & Unsigned 8-bit int \\ +\ct{IM\_BANDFMT\_CHAR} & \ct{FMTCHAR} & 1 & Signed 8-bit int \\ +\ct{IM\_BANDFMT\_USHORT} & \ct{FMTUSHORT} & 2 & Unsigned 16-bit int \\ +\ct{IM\_BANDFMT\_SHORT} & \ct{FMTSHORT} & 3 & Signed 16-bit int \\ +\ct{IM\_BANDFMT\_UINT} & \ct{FMTUINT} & 4 & Unsigned 32-bit int \\ +\ct{IM\_BANDFMT\_INT} & \ct{FMTINT} & 5 & Signed 32-bit int \\ +\ct{IM\_BANDFMT\_FLOAT} & \ct{FMTFLOAT} & 6 & 32-bit IEEE float \\ +\ct{IM\_BANDFMT\_COMPLEX} & \ct{FMTCOMPLEX} & 7 & Complex (2 floats) \\ +\ct{IM\_BANDFMT\_DOUBLE} & \ct{FMTDOUBLE} & 8 & 64-bit IEEE double \\ +\ct{IM\_BANDFMT\_DPCOMPLEX} & \ct{FMTDPCOMPLEX} & 9 & Complex (2 doubles) \\ +\hline +\end{tabular} +\end{center} +\caption{Possible values for \ct{BandFmt}\label{fg:bandfmt}} +\end{tab2} + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|l|} +\hline +\ct{Coding} & C++ and Python name & Value & Meaning \\ +\hline +\ct{IM\_CODING\_NONE} & \ct{NOCODING} & 0 & VIPS computation format \\ +\ct{IM\_CODING\_LABQ} & \ct{LABQ} & 2 & LABQ storage format \\ +\hline +\end{tabular} +\end{center} +\caption{Possible values for \texttt{Coding}\label{fg:coding}} +\end{tab2} + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|l|} +\hline +\ct{Type} & C++ and Python name & Value & Meaning \\ +\hline +\ct{IM\_TYPE\_MULTIBAND} & \ct{MULTIBAND} & 0 & Some multiband image \\ +\ct{IM\_TYPE\_B\_W} & \ct{B\_W} & 1 & Some single band image \\ +\ct{IM\_TYPE\_HISTOGRAM} & \ct{HISTOGRAM} & 10 & Histogram or LUT \\ +\ct{IM\_TYPE\_FOURIER} & \ct{FOURIER} & 24 & Image in Fourier space \\ +\ct{IM\_TYPE\_XYZ} & \ct{XYZ} & 12 & \ciexyz{} colour space \\ +\ct{IM\_TYPE\_LAB} & \ct{LAB} & 13 & \cielab{} colour space \\ +\ct{IM\_TYPE\_CMYK} & \ct{CMYK} & 15 & \ct{im\_icc\_export()} \\ +\ct{IM\_TYPE\_LABQ} & \ct{LABQ} & 16 & 32-bit \cielab{} \\ +\ct{IM\_TYPE\_RGB} & \ct{RGB} & 17 & Some RGB \\ +\ct{IM\_TYPE\_UCS} & \ct{UCS} & 18 & \cieucs{} colour space \\ +\ct{IM\_TYPE\_LCH} & \ct{LCH} & 19 & \cielch{} colour space \\ +\ct{IM\_TYPE\_LABS} & \ct{LABS} & 21 & 48-bit \cielab{} \\ +\ct{IM\_TYPE\_sRGB} & \ct{sRGB} & 22 & sRGB colour space \\ +\ct{IM\_TYPE\_YXY} & \ct{YXY} & 23 & \cieyxy{} colour space \\ +\ct{IM\_TYPE\_RGB16} & \ct{RGB16} & 25 & 16-bit RGB \\ +\ct{IM\_TYPE\_GREY16} & \ct{GREY16} & 26 & 16-bit monochrome \\ +\hline +\end{tabular} +\end{center} +\caption{Possible values for \texttt{Type}\label{fg:type}} +\end{tab2} + +\subsection{Computation formats} + +This type of image has \ct{Coding} set to \ct{IM\_CODING\_NONE}. The +header is then followed by a large array of pixels, laid out left-to-right, +top-to-bottom. Each pixel contains the specified number of bands. Each band +has the specified band format, which may be an 8-, 16- or 32-bit integer +(either signed or unsigned), a single or double precision IEEE floating +point number, or a pair of single or double precision floats forming a +complex number. + +All values are stored in the host-machine's native number representation (that +is, either most-significant first, as in SPARC and 680x0 machines, or +least-significant first, for Intel and DEC machines). The VIPS library will +automatically byte-swap for you if necessary, but this can be slow. + +\subsection{Storage formats} + +All storage formats have other values for the \ct{Coding} field. This +release supports only \ct{IM\_CODING\_LABQ} format. + +\ct{IM\_CODING\_LABQ} stores $L^{*}$, $a^{*}$ and $b^{*}$ for each pixel, +with 10 bits for $L^{*}$ and 11 bits for each of $a^{*}$ and $b^{*}$. These +32 bits are packed into 4 bytes, with the most significant 8 bits of each +value in the first 3 bytes, and the left-over bits packed into the final +byte as 2:3:3. + +This format is a little awkward to process. Some VIPS functions can work +directly on \ct{IM\_CODING\_LABQ} images (\ct{im\_extract()}, for example), +but most will require you to unpack the image to one of the computation +formats (for example with \ct{im\_LabQ2Lab()}) first. diff --git a/doc/src/func.tex b/doc/src/func.tex new file mode 100644 index 00000000..e6cb9fff --- /dev/null +++ b/doc/src/func.tex @@ -0,0 +1,676 @@ +\section{Function dispatch and plug-ins} + +As image processing libraries increase in size it becomes progressively more +difficult to build applications which present the operations the libbrary +offers to the user. Every time a new operation is added, every user interface +needs to be adapted --- a job which can rapidly become unmanageable. + +To address this problem VIPS includes a simple database which stores an +abstract description of every image processing operation. User interfaces, +rather than having special code wired into them for each operation, can +simply interrogate the database and present what they find to the user. + +The operation database is extensible. You can define new operations, and even +new types, and add them to VIPS. These new operations will then automatically +appear in all VIPS user interfaces with no extra programming effort. Plugins +can extend the database at runtime: when VIPS starts, it loads all the plugin +in the VIPS library area. + +\subsection{Simple plugin example} + +As an example, consider this function: + +\begin{verbatim} +#include + +#include + +/* The function we define. Call this + * from other parts of your C + * application. + */ +int +double_integer( int in ) +{ + return( in * 2 ); +} +\end{verbatim} + +\noindent +The source for all the example code in this section is in the vips-examples +package. + +The first step is to make a layer over this function which will make it +look like a standard VIPS function. VIPS insists on the following pattern: + +\begin{itemize} + +\item +The function should be int-valued, and return 0 for success and non-zero +for error. It should set \verb+im_error()+. + +\item +The function should take a single argument: a pointer to a +\verb+NULL+-terminated array of \verb+im_objects+. + +\item +Each \verb+im_object+ represents one argument to the function (either output +or input) in the form specified by the corresponding entry in the function's +argument descriptor. + +\end{itemize} + +The argument descriptor is an array of structures, each describing one +argument. For this example, it is: + +\begin{verbatim} +/* Describe the type of our function. + * One input int, and one output int. + */ +static im_arg_desc arg_types[] = { + IM_INPUT_INT( "in" ), + IM_OUTPUT_INT( "out" ) +}; +\end{verbatim} + +\verb+IM_INPUT_INT()+ and \verb+IM_OUTPUT_INT()+ are macros defined in +\verb++ which make argument types easy to define. Other +macros available are listed in table~\ref{tab:type}. + +\begin{tab2} +\begin{center} +\begin{tabular}{|l|l|l|} +\hline +Macro & Meaning & + \texttt{im\_object} has type \\ +\hline +\texttt{IM\_INPUT\_IMAGEVEC} & Vector of input images & + \texttt{IMAGE **} \\ +\texttt{IM\_INPUT\_IMAGE} & Input image & + \texttt{IMAGE *} \\ +\texttt{IM\_OUTPUT\_IMAGE} & Output image & + \texttt{IMAGE *} \\ +\texttt{IM\_RW\_IMAGE} & Read-write image & + \texttt{IMAGE *} \\ +\texttt{IM\_INPUT\_DOUBLE} & Input double & + \texttt{double *} \\ +\texttt{IM\_INPUT\_DOUBLEVEC} & Input vector of double & + \texttt{im\_realvec\_object *} \\ +\texttt{IM\_INPUT\_INTVEC} & Input vector of int & + \texttt{im\_intvec\_object *} \\ +\texttt{IM\_OUTPUT\_DOUBLE} & Output double & + \texttt{double *} \\ +\texttt{IM\_INPUT\_INT} & Input int & + \texttt{int *} \\ +\texttt{IM\_OUTPUT\_INT} & Output int & + \texttt{int *} \\ +\texttt{IM\_INPUT\_STRING} & Input string & + \texttt{char *} \\ +\texttt{IM\_OUTPUT\_STRING} & Output string & + \texttt{char *} \\ +\texttt{IM\_INPUT\_DISPLAY} & Input display & + \texttt{im\_col\_display *} \\ +\texttt{IM\_OUTPUT\_DISPLAY} & Output display & + \texttt{im\_col\_display *} \\ +\texttt{IM\_OUTPUT\_COMPLEX} & Output complex & + \texttt{double *} \\ +\texttt{IM\_INPUT\_DMASK} & Input double array & + \texttt{im\_mask\_object *} \\ +\texttt{IM\_OUTPUT\_DMASK} & Output double array to file & + \texttt{im\_mask\_object *} \\ +\texttt{IM\_OUTPUT\_DMASK\_STATS}& Output double array to screen & + \\ +\texttt{IM\_INPUT\_IMASK} & Input int array & + \texttt{im\_mask\_object *} \\ +\texttt{IM\_OUTPUT\_IMASK} & Output int array to file & + \texttt{im\_mask\_object *} \\ +\texttt{IM\_INPUT\_GVALUE} & Input GValue & + \texttt{GValue *} \\ +\texttt{IM\_OUTPUT\_GVALUE} & Output GValue & + \texttt{GValue *} \\ +\hline +\end{tabular} +\end{center} +\caption{Argument type macros\label{tab:type}} +\end{tab2} + +The argument to the type macro is the name of the argument. These names +are used by user-interface programs to provide feedback, and sometimes as +variable names. The order in which you list the arguments is the order in +which user-interfaces will present them to the user. You should use the +following conventions when selecting names and an order for your arguments: + +\begin{itemize} + +\item +Names should be entirely in lower-case and contain no special characters, +apart from the digits 0-9 and the underscore character `\_'. + +\item +Names should indicate the function of the argument. For example, +\verb+im_add()+ has the following argument names: + +\begin{verbatim} +example% vips -help im_add +vips: args: in1 in2 out +where: + in1 is of type "image" + in2 is of type "image" + out is of type "image" +add two images, from package + "arithmetic" +flags: + (PIO function) + (no coordinate transformation) + (point-to-point operation) +\end{verbatim} + +\item +You should order arguments with large input objects first, then output +objects, then any extra arguments or options. For example, \verb+im_extract()+ +has the following sequence of arguments: + +\begin{verbatim} +example% vips -help im_extract +vips: args: input output left top + width height channel +where: + input is of type "image" + output is of type "image" + left is of type "integer" + top is of type "integer" + width is of type "integer" + height is of type "integer" + channel is of type "integer" +extract area/band, from package + "conversion" +flags: + (PIO function) + (no coordinate transformation) + (point-to-point operation) +\end{verbatim} + +\end{itemize} + +This function sits over \verb+double_integer()+, providing VIPS with an +interface which it can call: + +\begin{verbatim} +/* Call our function via a VIPS + * im_object vector. + */ +static int +double_vec( im_object *argv ) +{ + int *in = (int *) argv[0]; + int *out = (int *) argv[1]; + + *out = double_integer( *in ); + + /* Always succeed. + */ + return( 0 ); +} +\end{verbatim} + +Finally, these two pieces of information (the argument description and +the VIPS-style function wrapper) can be gathered together into a function +description. + +\begin{verbatim} +/* Description of double_integer. + */ +static im_function double_desc = { + "double_integer", + "double an integer", + 0, + double_vec, + IM_NUMBER( arg_types ), + arg_types +}; +\end{verbatim} +\label{sec:number} + +\verb+IM_NUMBER()+ is a macro which returns the number of elements in a +static array. The \verb+flags+ field contains hints which user-interfaces +can use for various optimisations. At present, the possible values are: + +\begin{description} + +\item[\texttt{IM\_FN\_PIO}] +This function uses the VIPS PIO system (see \pref{sec:pio}). + +\item[\texttt{IM\_FN\_TRANSFORM}] +This the function transforms coordinates. + +\item[\texttt{IM\_FN\_PTOP}] +This is a point-to-point operation, that is, it can be replaced with a +look-up table. + +\item[\texttt{IM\_FN\_NOCACHE}] +This operation has side effects and should not be cached. Useful for video +grabbers, for example. + +\end{description} + +This function description now needs to be added to the VIPS function database. +VIPS groups sets of related functions together in packages. There is only +a single function in this example, so we can just write: + +\begin{verbatim} +/* Group up all the functions in this + * file. + */ +static im_function + *function_list[] = { + &double_desc +}; + +/* Define the package_table symbol. + * This is what VIPS looks for when + * loading the plugin. + */ +im_package package_table = { + "example", + IM_NUMBER( function_list ), + function_list +}; +\end{verbatim} + +The package has to be named \verb+package_table+, and has to be exported +from the file (that is, not a static). VIPS looks for a symbol of this name +when it opens your object file. + +This file needs to be made into a dynamically loadable object. On my machine, +I can do this with: + +\begin{verbatim} +example% gcc -fPIC -DPIC -c + `pkg-config vips-7.12 --cflags` + plug.c -o plug.o +example% gcc -shared plug.o + -o double.plg +\end{verbatim} + +You can now use \verb+double.plg+ with any of the VIPS applications which +support function dispatch. For example: + +\begin{verbatim} +example% vips -plugin double.plg \ + double_integer 12 +24 +example% +\end{verbatim} + +If you copy \verb+double.plg+ into your VIPS library area (the directory where +libvips is installed, or \verb+$VIPSHOME/lib+) it will be automatically +loaded by all VIPS programs as they start up. + +\subsection{A more complicated example} + +This section lists the source for \verb+im_extract()+'s function +description. Almost all functions in the VIPS library have descriptors --- +if you are not sure how to write a description, it's usually easiest to +copy one from a similar function in the library. + +\begin{verbatim} +/* Args to im_extract. + */ +static im_arg_desc + extract_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "channel" ) +}; + +/* Call im_extract via arg vector. + */ +static int +extract_vec( im_object *argv ) +{ + IMAGE_BOX box; + + box.xstart = *((int *) argv[2]); + box.ystart = *((int *) argv[3]); + box.xsize = *((int *) argv[4]); + box.ysize = *((int *) argv[5]); + box.chsel = *((int *) argv[6]); + + return( im_extract( + argv[0], argv[1], &box ) ); +} + +/* Description of im_extract. + */ +static im_function + extract_desc = { + "im_extract", + "extract area/band", + IM_FN_PIO | IM_FN_TRANSFORM, + extract_vec, + NUMBER( extract_args ), + extract_args +}; +\end{verbatim} + +\subsection{Adding new types} + +The VIPS type mechanism is extensible. User plug-ins can add new types +and user-interfaces can (to a certain extent) provide interfaces to these +user-defined types. + +Here is the definition of \verb+im_arg_desc+: + +\begin{verbatim} +/* Describe a VIPS command argument. + */ +typedef struct { + char *name; + im_type_desc *desc; + im_print_obj_fn print; +} im_arg_desc; +\end{verbatim} + +The \verb+name+ field is the argument name above. The \verb+desc+ field +points to a structure defining the argument type, and the \verb+print+ field +is an (optionally \verb+NULL+) pointer to a function which VIPS will call +for output arguments after your function successfully completes and before +the object is destroyed. It can be used to print results to the terminal, +or to copy results into a user-interface layer. + +\begin{verbatim} +/* Success on an argument. This is + * called if the image processing + * function succeeds and should be + * used to (for example) print + * output. + */ +typedef int (*im_print_obj_fn) + ( im_object obj ); +\end{verbatim} + +\verb+im_type_desc+ is defined as: + +\begin{verbatim} +/* Describe a VIPS type. + */ +typedef struct { + im_arg_type type; + int size; + im_type_flags flags; + im_init_obj_fn init; + im_dest_obj_fn dest; +} im_type_desc; +\end{verbatim} + +Where \verb+im_arg_type+ is defined as + +\begin{verbatim} +/* Type names. You may define your + * own, but if you use one of these, + * then you should use the built-in + * VIPS type converters. + */ +#define IM_TYPE_IMAGEVEC "imagevec" +#define IM_TYPE_DOUBLEVEC "doublevec" +#define IM_TYPE_INTVEC "intvec" +#define IM_TYPE_DOUBLE "double" +#define IM_TYPE_INT "integer" +#define IM_TYPE_COMPLEX "complex" +#define IM_TYPE_STRING "string" +#define IM_TYPE_IMASK "intmask" +#define IM_TYPE_DMASK "doublemask" +#define IM_TYPE_IMAGE "image" +#define IM_TYPE_DISPLAY "display" +#define IM_TYPE_GVALUE "gvalue" +typedef char *im_arg_type; +\end{verbatim} + +In other words, it's just a string. When you add a new type, you just need +to choose a new unique string to name it. Be aware that the string is printed +to the user by various parts of VIPS, and so needs to be ``human-readable''. +The flags are: + +\begin{verbatim} +/* These bits are ored together to + * make the flags in a type + * descriptor. + * + * IM_TYPE_OUTPUT: set to indicate + * output, otherwise input. + * + * IM_TYPE_ARG: Two ways of making + * an im_object --- with and without + * a command-line string to help you + * along. Arguments with a string + * are thing like IMAGE descriptors, + * which require a filename to + * initialise. Arguments without are + * things like output numbers, where + * making the object simply involves + * allocating storage. + */ + +typedef enum { + IM_TYPE_OUTPUT = 0x1, + IM_TYPE_ARG = 0x2 +} im_type_flags; +\end{verbatim} + +And the \verb+init+ and \verb+destroy+ functions are: + +\begin{verbatim} +/* Initialise and destroy objects. + * The "str" argument to the init + * function will not be supplied + * if this is not an ARG type. + */ +typedef int (*im_init_obj_fn) + ( im_object *obj, char *str ); +typedef int (*im_dest_obj_fn) + ( im_object obj ); +\end{verbatim} + +As an example, here is the definition for a new type of unsigned +integers. First, we need to define the \verb+init+ and \verb+print+ +functions. These transform objects of the type to and from string +representation. + +\begin{verbatim} +/* Init function for unsigned int + * input. + */ +static int +uint_init( im_object *obj, char *str ) +{ + unsigned int *i = (int *) *obj; + + if( sscanf( str, "%d", i ) != 1 || + *i < 0 ) { + im_error( "uint_init", + "bad format" ); + return( -1 ); + } + + return( 0 ); +} + +/* Print function for unsigned int + * output. + */ +static int +uint_print( im_object obj ) +{ + unsigned int *i = + (unsigned int *) obj; + + printf( "%d\n", (int) *i ); + + return( 0 ); +} +\end{verbatim} + +Now we can define the type itself. We make two of these --- one for unsigned +int used as input, and one for output. + +\begin{verbatim} +/* Name our type. + */ +#define TYPE_UINT "uint" + +/* Input unsigned int type. + */ +static im_type_desc input_uint = { + TYPE_UINT, /* Its an int */ + sizeof( unsigned int ),/* Memory */ + IM_TYPE_ARG, /* Needs arg */ + uint_init, /* Init */ + NULL /* Destroy */ +}; + +/* Output unsigned int type. + */ +static im_type_desc output_uint = { + TYPE_UINT, /* It's an int */ + sizeof( unsigned int ),/* Memory */ + IM_TYPE_OUTPUT, /* It's output */ + NULL, /* Init */ + NULL /* Destroy */ +}; +\end{verbatim} + +Finally, we can define two macros to make structures of type +\verb+im_arg_desc+ for us. + +\begin{verbatim} +#define INPUT_UINT( S ) \ + { S, &input_uint, NULL } +#define OUTPUT_UINT( S ) \ + { S, &output_uint, uint_print } +\end{verbatim} + +For more examples, see the definitions for the built-in VIPS types. + +\subsection{Using function dispatch in your application} + +VIPS provides a set of functions for adding new image processing functions +to the VIPS function database, finding functions by name, and calling +functions. See the manual pages for full details. + +\subsubsection{Adding and removing functions} + +\begin{verbatim} +im_package *im_load_plugin( + const char *name ); +\end{verbatim} + +This function opens the named file, searches it for a symbol named +\verb+package_table+, and adds any functions it finds to the VIPS function +database. When you search for a function, any plug-ins are searched first, +so you can override standard VIPS function with your own code. + +The function returns a pointer to the package it added, or \verb+NULL+ +on error. + +\begin{verbatim} +int im_close_plugins( void ) +\end{verbatim} + +This function closes all plug-ins, removing then from the VIPS function +database. It returns non-zero on error. + +\subsubsection{Searching the function database} + +\begin{verbatim} +void *im_map_packages( + im_list_map_fn fn, void *a ) +\end{verbatim} + +This function applies the argument function \verb+fn+ to every package +in the database, starting with the most recently added package. As with +\verb+im_list_map()+, the argument function should return \verb+NULL+ +to continue searching, or non-\verb+NULL+ to terminate the search +early. \verb+im_map_packages()+ returns \verb+NULL+ if \verb+fn+ returned +\verb+NULL+ for all arguments. The extra argument \verb+a+ is carried around +by VIPS for your use. + +For example, this fragment of code prints the names of all loaded packages +to \verb+fd+: + +\begin{verbatim} +static void * +print_package_name( im_package *pack, + FILE *fp ) +{ + (void) fprintf( fp, + "package: \"%s\"\n", + pack->name ); + + /* Continue search. + */ + return( NULL ); +} + +static void +print_packages( FILE *fp ) +{ + (void) im_map_packages( + (im_list_map_fn) + print_package_name, fp ); +} +\end{verbatim} + +VIPS defines three convenience functions based on \verb+im_map_packages()+ +which simplify searching for specific functions: + +\begin{verbatim} +im_function * + im_find_function( char *name ) +im_package * + im_find_package( char *name ) +im_package * + im_package_of_function( char *name ) +\end{verbatim} + +\subsubsection{Building argument structures and running commands} + +\begin{verbatim} +int im_free_vargv( im_function *fn, + im_object *vargv ) +int im_allocate_vargv( + im_function *fn, + im_object *vargv ) +\end{verbatim} + +These two functions allocate space for and free VIPS argument lists. The +allocate function simply calls \verb+im_malloc()+ to allocate any store +that the types require (and also initializes it to zero). The free function +just calls \verb+im_free()+ for any storage that was allocated. + +Note that neither of these functions calls the \verb+init+, \verb+dest+ +or \verb+print+ functions for the types --- that's up to you. + +\begin{verbatim} +int im_run_command( char *name, + int argc, char **argv ) +\end{verbatim} + +This function does everything. In effect, + +\begin{verbatim} +im_run_command( "im_invert", 2, + { "fred.v", "fred2.v", NULL } ) +\end{verbatim} + +is exactly equivalent to + +\begin{verbatim} +system( "vips im_invert fred.v " + "fred2.v" ) +\end{verbatim} + +but no process is forked. diff --git a/doc/src/html.cfg b/doc/src/html.cfg new file mode 100644 index 00000000..c8033ad9 --- /dev/null +++ b/doc/src/html.cfg @@ -0,0 +1,17 @@ +% configuration file for output of nipguide as html +\Preamble{html} +\begin{document} + +% stop the mono font shrinkage we do for paper output +\renewenvironment{ctd}{\begin{quote}\tt}{\end{quote}} + +% make a label +% in html, write an extra label which we can link to nip's help system +\renewcommand{\mylabel}[1]{ + \label{#1} + \HCode{} +} + +% supress " on page xx" if we're making HTML +\renewcommand{\onpage}[1]{} +\EndPreamble diff --git a/doc/src/iosys.tex b/doc/src/iosys.tex new file mode 100644 index 00000000..40b88e37 --- /dev/null +++ b/doc/src/iosys.tex @@ -0,0 +1,845 @@ +\section{Core C API} + +VIPS is +built on top of several other libraries, two of which, glib and gobject, are +exposed at various points in the C API. + +You can read up on glib at the GTK+ website: + +\begin{verbatim} +http://www.gtk.org/api +\end{verbatim} + +There's also an excellent book by Matthias Warkus, \emph{The Official +GNOME 2 Developer's Guide}, which covers the same material in a tutorial +manner. + +\subsection{Startup} + +Before calling any VIPS function, you need to start VIPS up: + +\begin{verbatim} +int im_init_world( const char *argv0 ); +\end{verbatim} + +The \verb+argv0+ argument is the value of \verb+argv[0]+ your program was passed +by the host operating system. VIPS uses this with \verb+im_guess_prefix()+ +to try to find various VIPS data files. + +If you don't call this function, VIPS will call it for you the first time you +use a VIPS function. But it won't be able to get the \verb+argv0+ value for +you, so it may not be able to find it's data files. + +VIPS also offers the optional: + +\begin{verbatim} +GOptionGroup *im_get_option_group( void ); +\end{verbatim} + +You can use this with GOption to parse your program's command-line arguments. +It adds several useful VIPS flags, including \verb+--vips-concurrency+. + +\fref{fg:hello} shows both these functions in use. Again, the GOption stuff is +optional and just lets VIPS add some flags to your program. You do need the +\verb+im_init_world()+ though. + +\begin{fig2} +\begin{verbatim} +#include +#include + +static gboolean print_stuff; + +static GOptionEntry options[] = { + { "print", 'p', 0, G_OPTION_ARG_NONE, &print_stuff, + "print \"hello world!\"", NULL }, + { NULL } +}; + +int +main( int argc, char **argv ) +{ + GOptionContext *context; + GError *error = NULL; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + context = g_option_context_new( "- my program" ); + g_option_context_add_main_entries( context, + options, "main" ); + g_option_context_add_group( context, im_get_option_group() ); + if( !g_option_context_parse( context, &argc, &argv, &error ) ) { + if( error ) { + fprintf( stderr, "%s\n", error->message ); + g_error_free( error ); + } + error_exit( "try \"%s --help\"", g_get_prgname() ); + } + g_option_context_free( context ); + + if( print_stuff ) + printf( "hello, world!\n" ); + + return( 0 ); +} +\end{verbatim} +\caption{Hello World for VIPS} +\label{fg:hello} +\end{fig2} + +\subsection{Image descriptors} + +The base level of the VIPS I/O system provides \verb+IMAGE+ descriptors. +An image represented by a descriptor may be an image file on disc, an area +of memory that has been allocated for the image, an output file, a delayed +computation, and so on. Programs need (usually) only know that they have +a descriptor, they do not see many of the details. \fref{fg:image} +shows the definition of the \verb+IMAGE+ descriptor. + +\begin{fig2} +\begin{verbatim} +typedef struct { + /* Fields from image header. + */ + int Xsize; /* Pels per line */ + int Ysize; /* Lines */ + int Bands; /* Number of bands */ + int Bbits; /* Bits per band */ + int BandFmt; /* Band format */ + int Coding; /* Coding type */ + int Type; /* Type of file */ + float XRes; /* Horizontal res in pels/mm */ + float YRes; /* Vertical res in pels/mm */ + int Length; /* Obsolete (unused) */ + short Compression; /* Obsolete (unused) */ + short Level; /* Obsolete (unused) */ + int Xoffset; /* Position of origin */ + int Yoffset; + + /* Derived fields that may be read by the user. + */ + char *filename; /* File name */ + struct time_info *time;/* Timing for eval callback */ + int kill; /* Set to non-zero to block eval */ + + ... and lots of other private fields used by VIPS for + ... housekeeping. +} IMAGE; +\end{verbatim} +\caption{The \texttt{IMAGE} descriptor} +\label{fg:image} +\end{fig2} + +The first set of fields simply come from the image file header: +see \pref{sec:header} for a full description of all the fields. The next +set are maintained for you by the VIPS I/O system. \verb+filename+ is the +name of the file that this image came from. If you have attached an eval +callback to this image, \verb+time+ points to a set of timing statistics +which can be used by user-interfaces built on VIPS to provide feedback +about the progress of evaluation --- see \pref{sec:eval}. Finally, if you +set \verb+kill+ to non-zero, VIPS will block any pipelines which use this +descriptor as an intermediate. See \pref{sec:block}. + +The remaining fields are private and are used by VIPS for housekeeping. + +\subsection{Header fields} +\label{sec:fields} + +You can access header fields either directly (as \verb+im->Xsize+, for +example) or programmatically with \verb+im_header_int()+ and friends. For +example: + +\begin{verbatim} +int i; + +im_header_int( im, "Xsize", &i ); +\end{verbatim} + +There's also \verb+im_header_map()+ to loop over header fields, and +\verb+im_header_get_type+ to test the type of fields. These functions work for +image meta fields as well, see \pref{sec:meta}. + +\subsection{Opening and closing} + +Descriptors are created with \verb+im_open()+. This takes a file name +and a string representing the mode with which the descriptor is to be opened: + +\begin{verbatim} +IMAGE *im_open( const char *filename, + const char *mode ) +\end{verbatim} + +The possible values for mode are: + +\begin{description} + +\item[\texttt{"r"}] +The file is opened read-only. +If you open a non-VIPS image, or a VIPS image written on a machine with a +different byte ordering, \verb+im_open()+ will automatically convert it to +native VIPS format. If the underlying file does not support random access +(JPEG, for example), the entire file will be converted in memory. + +VIPS can read images in TIFF, JPEG, PPM/\-PGM/\-PBM, PNG and VIPS +format, all in both big- and little-endian varieties. You can control the +details of the conversion with extra characters embedded in the filename. For +example: + +\begin{verbatim} +fred = im_open( "fred.tif:2", + "r" ); +\end{verbatim} + +\noindent +will read page 2 of a multi-page TIFF. See the man pages for details. + +If VIPS has been built with libMagick, it can also read any of the 80 or so +libMagick-supported image file formats. + +\item[\texttt{"w"}] +An \verb+IMAGE+ descriptor is created which, when written to, will write +pixels to disc in the specified file. + +VIPS looks at the filename suffix to determine the save format. If there +is no suffix, or the filename ends in \verb+".v"+, the image is written +in VIPS native format. + +If the filename ends in \verb+".tif"+ or \verb+".tiff"+, the image +is written with \verb+im_vips2tiff()+. If the filename ends in +\verb+".jpg"+, \verb+".jpeg"+ or \verb+".jpe"+, the image is written with +\verb+im_vips2jpg()+. If the filename ends with \verb+".pbm"+, \verb+".pgm"+ +or \verb+".ppm"+, the image is written using \verb+im_vips2ppm()+. +If the filename ends with \verb+".png"+, the image is written using +\verb+im_vips2png()+. Case is not considered when testing the suffix. + +If you want to control the details of the conversion to the disc format (such +as setting the Q factor for a JPEG, for example), you embed extra control +characters in the filename. For example: + +\begin{verbatim} +fred = im_open( "fred.jpg:95", + "w" ); +\end{verbatim} + +\noindent +writes to \verb+fred+ will write a JPEG with Q 95. Again, see the man pages +for the conversion functions for details. + +\item[\texttt{"t"}] +As the \verb+"w"+ mode, but pels written to the descriptor will be saved +in a temporary memory buffer. + +\item[\texttt{"p"}] +This creates a special partial image. Partial images are used to join VIPS +operations together, see \pref{sec:joinup}. + +\item[\texttt{"rw"}] +As the \verb+"r"+ mode, but the image is mapped into the caller's address +space read-write. This mode is only provided for the use of paintbox-style +applications which need to directly modify an image. Most programs should +use the \verb+"w"+ mode for image output. + +\end{description} + +If an error occurs opening the image, \verb+im_open()+ calls +\verb+im_error()+ with a string describing the cause of the error and +returns \verb+NULL+. \verb+im_error()+ has type + +\begin{verbatim} +void im_error( const char *domain, + const char *format, ... ) +\end{verbatim} + +\noindent +The first argument is a string giving the name of the thing that raised +the error (just \verb+"im_open"+, for example). The format and subsequent +arguments work exactly as \verb+printf()+. It formats the message and +appends the string formed to the error log. You can get a pointer to the +error text with \verb+im_error_buffer()+. + +\begin{verbatim} +const char *im_error_buffer() +\end{verbatim} + +\noindent +Applications may display this string to give users feedback about the +cause of the error. The VIPS exit function, \verb+error_exit()+, prints +\verb+im_error_buffer()+ to \verb+stderr+ and terminates the program with an +error code of 1. + +\begin{verbatim} +void error_exit( const char *format, + ... ) +\end{verbatim} + +\noindent +There are other functions for handling errors: see the man page for +\verb+im_error()+. + +If the file name given to \verb+im_open()+ ends with \verb+".v"+, VIPS +looks in the same directory as the image for a file with the same name +but with the extension \verb+".desc"+. If present, this file is read in +and a pointer to the data put in the \verb+Hist+ field of the descriptor. See +the notes on \verb+im_updatehist()+ in \pref{sec:examples}. + +Descriptors are closed with \verb+im_close()+. It has type: + +\begin{verbatim} +int im_close( IMAGE *im ) +\end{verbatim} + +\verb+im_close()+ returns 0 on success and non-zero on error. +If the descriptor represents a disc file which +has been written to and whose name ends in \verb+".v"+, VIPS writes the +\verb+Hist+ field of the image descriptor to a file in the same directory +whose name ends in \verb+".desc"+. + +\subsection{Examples} +\label{sec:examples} + +As an example, \fref{fg:widthheight} will print the width and height +of an image stored on disc. + +\begin{fig2} +\begin{verbatim} +#include +#include + +int +main( int argc, char **argv ) +{ + IMAGE *im; + + /* Check arguments. + */ + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + if( argc != 2 ) + error_exit( "usage: %s filename", argv[0] ); + + /* Open file. + */ + if( !(im = im_open( argv[1], "r" )) ) + error_exit( "unable to open %s for input", argv[1] ); + + /* Process. + */ + printf( "width = %d, height = %d\n", im->Xsize, im->Ysize ); + + /* Close. + */ + if( im_close( im ) ) + error_exit( "unable to close %s", argv[1] ); + + return( 0 ); +} +\end{verbatim} +\label{fg:widthheight} +\caption{Print width and height of an image} +\end{fig2} + +To compile this example, use: + +\begin{verbatim} +cc `pkg-config vips-7.12 \ + --cflags --libs` myfunc.c +\end{verbatim} + +As a slightly more complicated example, \fref{fg:negative} +will calculate the photographic negative of an image. + +\begin{fig2} +\begin{verbatim} +#include +#include + +int +main( int argc, char **argv ) +{ + IMAGE *in, *out; + + /* Check arguments. + */ + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + if( argc != 3 ) + error_exit( "usage: %s infile outfile", argv[0] ); + + /* Open images for read and write, invert, update the history with our + * args, and close. + */ + if( !(in = im_open( argv[1], "r" )) || + !(out = im_open( argv[2], "w" )) || + im_invert( in, out ) || + im_updatehist( out, argc, argv ) || + im_close( in ) || + im_close( out ) ) + error_exit( argv[0] ); + + return( 0 ); +} +\end{verbatim} +\label{fg:negative} +\caption{Find photographic negative} +\end{fig2} + +\subsection{Metadata} +\label{sec:meta} + +VIPS lets you attach arbitrary metadata to an IMAGE. For example, ICC +profiles, EXIF tags, whatever you like. VIPS will efficiently propogate +metadata as images are processed (usually just by copying pointers) and will +automatically save and load metadata from VIPS files (see +\pref{sec:header}). + +A piece of metadata is a value and an identifying name. A set of +convenience functions let you set and get int, double, string and blob. For +example: + +\begin{verbatim} +int im_meta_set_int( IMAGE *, + const char *field, int ); +int im_meta_get_int( IMAGE *, + const char *field, int * ); +\end{verbatim} + +So you can do: + +\begin{verbatim} +if( im_meta_set_int( im, "poop", 42 ) ) + return( -1 ); +\end{verbatim} + +\noindent +to create an int called \verb+"poop"+, then at some later point (possibly much, +much later), in an image distantly derived from \verb+im+, you can use: + +\begin{verbatim} +int i; + +if( im_meta_get_int( im, "poop", &i ) ) + return( -1 ); +\end{verbatim} + +\noindent +And get the value 42 back. + +You can use \verb+im_meta_set()+ and \verb+im_meta_get()+ to attach arbitrary +\verb+GValue+ to images. +See the man page for \verb+im_meta_set()+ for full +details. + +You can test for a field being present with \verb+im_meta_get_type()+ (you'll +get \verb+G_TYPE_INT+ back for \verb+"poop"+, for example, or 0 if it is not +defined for this image). + +\subsection{History} +\label{sec:history} + +VIPS tracks the history of an image, that is, the sequence of operations which +have led to the creation of an image. You can view a VIPS image's history with +the \verb+header+ command, or with \nip{}'s \ct{View Header} menu. Whenever an +application performs an action, it should append a line of shell script to the +history which would perform the same action. + +The call to \verb+im_updatehist()+ in \fref{fg:negative} adds a line to the +image history noting the invocation of this program, its arguments, and the +time and date at which it was run. You may also find \verb+im_histlin()+ +helpful. It has type: + +\begin{verbatim} +void im_histlin( IMAGE *im, + const char *fmt, ... ) +\end{verbatim} + +\noindent +It formats its arguments as \verb+printf()+ and appends the string formed +to the image history. + +You read an image's history with \verb+im_history_get()+. It returns the +entire history of an image, one action per line. No need to free the result. + +\begin{verbatim} +const char * + im_history_get( IMAGE *im ); +\end{verbatim} + +\subsection{Eval callbacks} +\label{sec:eval} + +VIPS lets you attach callbacks to image descriptors. These are functions +you provide which VIPS will call when certain events occur. See +\pref{sec:callback} for a full list. + +Eval callbacks are called repeatedly during evaluation and can be used by +user-interface programs to give feedback about the progress of evaluation. + +\subsection{Detailed rules for descriptors} + +These rules are intended to answer awkward questions. + +\begin{enumerate} + +\item +You can output to a descriptor only once. + +\item +You can use a descriptor as an input many times. + +\item +You can only output to a descriptor that was opened with modes \verb+"w"+, +\verb+"t"+ and \verb+"p"+. + +\item +You can only use a descriptor as input if it was opened with modes \verb+"r"+ +or \verb+"rw"+. + +\item +If you have output to a descriptor, you may subsequently use it as an +input. \verb+"w"+ descriptors are automatically changed to \verb+"r"+ +descriptors. + +If the function you are passing the descriptor to uses WIO (see +\pref{sec:limit}), then \verb+"p"+ descriptors become \verb+"t"+. +If the function you are passing the descriptor to uses PIO, then \verb+"p"+ +descriptors are unchanged. + +\end{enumerate} + +\subsection{Automatic resource deallocation} + +VIPS lets you allocate resources local to an image descriptor, that is, +when the descriptor is closed, all resources which were allocated local to +that descriptor are automatically released for you. + +\subsubsection{Local image descriptors} + +VIPS provides a function which will open a new image local to +an existing image. \verb+im_open_local()+ has type: + +\begin{verbatim} +IMAGE *im_open_local( IMAGE *im, + const char *filename, + const char *mode ) +\end{verbatim} + +It behaves exactly as \verb+im_open()+, except that you do not need to close +the descriptor it returns. It will be closed automatically when its parent +descriptor \verb+im+ is closed. + +\begin{fig2} +\begin{verbatim} +/* Add another image to the accumulated total. + */ +static int +sum1( IMAGE *acc, IMAGE **in, int nin, IMAGE *out ) +{ + IMAGE *t; + + if( nin == 0 ) + /* All done ... copy to out. + */ + return( im_copy( acc, out ) ); + + /* Make a new intermediate, and add to it.. + */ + return( !(t = im_open_local( out, "sum1:1", "p" )) || + im_add( acc, in[0], t ) || + sum1( t, in + 1, nin - 1, out ) ); +} + +/* Sum the array of images in[]. nin is the number of images in + * in[], out is the descriptor we write the final image to. + */ +int +total( IMAGE **in, int nin, IMAGE *out ) +{ + /* Check that we have at least one image. + */ + if( nin <= 0 ) { + im_error( "total", "nin should be > 0" ); + return( -1 ); + } + + /* More than 1, sum recursively. + */ + return( sum1( in[0], in + 1, nin - 1, out ) ); +} +\end{verbatim} +\caption{Sum an array of images} +\label{fg:addemup} +\end{fig2} + +\fref{fg:addemup} is a function which will sum an array of images. +We need never close any of the (unknown) number of intermediate images which +we open. They will all be closed for us by our caller, when our caller +finally closes \verb+out+. VIPS lets local images themselves have local +images and automatically makes sure that all are closed in the correct order. + +It is very important that these intermediate images are made local to +\verb+out+ rather than \verb+in+, for reasons which should become apparent +in the section on combining operations below. + +There's also \verb+im_open_local_array()+ for when you need a lot of local +descriptors, see the man page. + +\subsubsection{Local memory allocation} +\label{sec:malloc} + +VIPS includes a set of functions for memory allocation local to an image +descriptor. The base memory allocation function is \verb+im_malloc()+. It +has type: + +\begin{verbatim} +void *im_malloc( IMAGE *im, + size_t size ) +\end{verbatim} + +It operates exactly as the standard \verb+malloc()+ C library function, +except that the area of memory it allocates is local to image descriptor +\verb+im+. If \verb+im_malloc()+ is unable to allocate memory, it returns +\verb+NULL+. If you pass \verb+NULL+ instead of a valid image descriptor, +then \verb+im_malloc()+ allocates memory globally and you must free it +yourself at some stage. + +To free memory explicitly, use \verb+im_free()+: + +\begin{verbatim} +int im_free( void *mem ) +\end{verbatim} + +\noindent +\verb+im_free()+ always returns 0, so you can use it as an argument to a +callback. + +Three macros make memory allocation even easier. \verb+IM_NEW()+ allocates +a new object. You give it a descriptor and a type, and it returns a pointer +to enough space to hold an object of that type. It has type: + +\begin{verbatim} +type-name *IM_NEW( IMAGE *im, type-name ) +\end{verbatim} + +The second macro, \verb+IM_ARRAY()+, is very similar, but allocates +space for an array of objects. Note that, unlike the usual \verb+calloc()+ +C library function, it does not initialise the array to zero. It has type: + +\begin{verbatim} +type-name *IM_ARRAY( IMAGE *im, + int n, type-name ) +\end{verbatim} + +Finally, \verb+IM_NUMBER()+ returns the number of elements in an array of +defined size. See the man pages for a series of examples, or +see \pref{sec:number}. + +\subsubsection{Other local operations} + +The above facilities are implemented with the VIPS core function +\verb+im_add_close_callback()+. You can use this facility to make your own +local resource allocators for other types of object --- see the manual page +for more help. + +\subsection{Error handling} + +All VIPS operations return 0 on success and non-zero on error, setting +\verb+im_error()+. As a consequence, when a VIPS function fails, you do not +need to generate an error message --- you can simply propogate the error back +up to your caller. If however you detect some error yourself (for example, +the bad parameter in the example above), you must call \verb+im_error()+ +to let your caller know what the problem was. + +VIPS provides two more functions for error message handling: \verb+im_warn()+ +and \verb+im_diag()+. These are intended to be used for less serious +messages, as their names suggest. Currently, they simply format and print +their arguments to \verb+stderr+, optionally supressed by the setting of an +environment variable. Future releases of VIPS may allow more sophisticated +trapping of these functions to allow their text to be easily presented to +the user by VIPS applications. See the manual pages. + +\subsection{Joining operations together} +\label{sec:joinup} + +VIPS lets you join image processing operations together so that they +behave as a single unit. \fref{fg:join} shows the definition of the +function \verb+im_Lab2disp()+ from the VIPS library. This function converts +an image in \cielab{} colour space to an RGB image for a monitor. The +monitor characteristics (gamma, phosphor type, etc.) are described by the +\verb+im_col_display+ structure, see the man page for \verb+im_col_XYZ2rgb()+. + +\begin{fig2} +\begin{verbatim} +int +im_Lab2disp( IMAGE *in, IMAGE *out, struct im_col_display *disp ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_Lab2disp:1", "p" )) || + im_Lab2XYZ( in, t1 ) || + im_XYZ2disp( t1, out, disp ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} +\caption{Two image-processing operations joined together} +\label{fg:join} +\end{fig2} + +The special \verb+"p"+ mode (for partial) used to open the image descriptor +used as the intermediate image in this function `glues' the two operations +together. When you use \verb+im_Lab2disp()+, the two operations inside it +will execute together and no extra storage is necessary for the intermediate +image (\verb+t1+ in this example). This is important if you want to process +images larger than the amount of RAM you have on your machine. + +As an added bonus, if you have more than one CPU in your computer, the work +will be automatically spread across the processors for you. You can control +this parallelisation with the \verb+IM_CONCURRENCY+ environment variable and +with \verb+im_concurrency_set()+. See the man page for \verb+im_generate()+. + +\subsubsection{How it works} + +When a VIPS function is asked to output to a \verb+"p"+ image descriptor, +all the fields in the descriptor are set (the output image size and type +are set, for example), but no image data is actually generated. Instead, +the function attaches callbacks to the image descriptor which VIPS can use +later to generate any piece of the output image that might be needed. + +When a VIPS function is asked to output to a \verb+"w"+ or a \verb+"t"+ +descriptor (a real disc file or a real memory buffer), it evaluates +immediately and its evaluation in turn forces the evaluation of any earlier +\verb+"p"+ images. + +In the example in \fref{fg:join}, whether or not any pixels are really +processed when \verb+im_Lab2disp()+ is called depends upon the mode in +which \verb+out+ was opened. If \verb+out+ is also a partial image, then +no pixels will be calculated --- instead, a pipeline of VIPS operations +will be constructed behind the scenes and attached to \verb+out+. + +Conversely, if \verb+out+ is a real image (that is, either \verb+"w"+ +or \verb+"t"+), then the final VIPS operation in the function +(\verb+im_XYZ2disp()+) will output the entire image to \verb+out+, causing +the earlier parts of \verb+im_Lab2disp()+ (and indeed possibly some earlier +pieces of program, if \verb+in+ was also a \verb+"p"+ image) to run. + +When a VIPS pipeline does finally evaluate, all of the functions in the +pipeline execute together, sucking image data through the system in small +pieces. As a consequence, no intermediate images are generated, large +amounts of RAM are not needed, and no slow disc I/O needs to be performed. + +Since VIPS partial I/O is demand-driven rather than data-driven this works +even if some of the operations perform coordinate transformations. We could, +for example, include a call to \verb+im_affine()+, which performs +arbitrary rotation and scaling, and everything would still work correctly. + +\subsubsection{Pitfalls with partials} + +To go with all of the benefits that partial image I/O brings, there are +also some problems. The most serious is that you are often not quite certain +when computation will happen. This can cause problems if you close an input +file, thinking that it is finished with, when in fact that file has not +been processed yet. Doing this results in dangling pointers and an almost +certain core-dump. + +You can prevent this from happening with careful use of +\verb+im_open_local()+. If you always open local to your output image, +you can be sure that the input will not be closed before the output has been +generated to a file or memory buffer. You do not need to be so careful with +non-image arguments. VIPS functions which take extra non-image arguments +(a matrix, perhaps) are careful to make their own copy of the object before +returning. + +\subsubsection{Non-image output} + +Some VIPS functions consume images, but make no image +output. \verb+im_stats()+ for example, scans an image calculating various +statistical values. When you use \verb+im_stats()+, it behaves as a data +sink, sucking image data through any earlier pipeline stages. + +\subsubsection{Calculating twice} + +In some circumstances, the same image data can be generated twice. +\fref{fg:thrmean} is a function which finds the mean value of an +image, and writes a new image in which pixels less than the mean are set to +0 and images greater than the mean are set to 255. + +\begin{fig2} +\begin{verbatim} +int +threshold_at_mean( IMAGE *in, IMAGE *out ) +{ + double mean; + + if( im_avg( in, &mean ) || + im_moreconst( in, out, mean ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} +\caption{Threshold an image at the mean value} +\label{fg:thrmean} +\end{fig2} + +This seems straightforward --- but consider if image \verb+in+ were a +\verb+"p"+, and represented the output of a large pipeline of operations. The +call to \verb+im_avg()+ would force the evaluation of the entire pipeline, +and throw it all away, keeping only the average value. The subsequent call to +\verb+im_moreconst()+ will cause the pipeline to be evaluated a second time. + +When designing a program, it is sensible to pay attention to these +issues. It might be faster, in some cases, to output to a file before +calling \verb+im_avg()+, find the average of the disc file, and then run +\verb+im_moreconst()+ from that. + +\subsubsection{Blocking computation} +\label{sec:block} + +\verb+IMAGE+ descriptors have a flag called \verb+kill+ which can be used +to block computation. If \verb+im->kill+ is set to a non-zero value, +then any VIPS pipelines which use \verb+im+ as an intermediate will fail +with an error message. This is useful for user-interface writers --- +suppose your interface is forced to close an image which many other images +are using as a source of data. You can just set the \verb+kill+ flag in all +of the deleted image's immediate children and prevent any dangling pointers +from being followed. + +\subsubsection{Limitations} +\label{sec:limit} + +Not all VIPS operations are partial-aware. These non-partial operations +use a pre-VIPS7.0 I/O scheme in which the whole of the input image has to +be present at the same time. In some cases, this is because partial I/O +simply makes no sense --- for example, a Fourier Transform can produce no +output until it has seen all of the input. \verb+im_fwfft()+ is therefore +not a partial operation. In other cases, we have simply not got around to +rewriting the old non-partial operation in the newer partial style. + +You can mix partial and non-partial VIPS operations freely, without worrying +about which type they are. The only effect will be on the time your pipeline +takes to execute, and the memory requirements of the intermediate images. VIPS +uses the following rules when you mix the two styles of operation: + +\begin{enumerate} + +\item +When a non-partial operation is asked to output to a partial image descriptor, +the \verb+"p"+ descriptor is magically transformed into a \verb+"t"+ +descriptor. + +\item +When a non-partial operation is asked to read from a \verb+"p"+ descriptor, +the \verb+"p"+ descriptor is turned into a \verb+"t"+ type, and any earlier +stages in the pipeline forced to evaluate into that memory buffer. + +The non-partial operation then processes from the memory buffer. + +\end{enumerate} + +These rules have the consequence that you may only process very large images +if you only use partial operations. If you use any non-partial operations, +then parts of your pipelines will fall back to old whole-image I/O and you +will need to think carefully about where your intermediates should be stored. + diff --git a/doc/src/ipio.tex b/doc/src/ipio.tex new file mode 100644 index 00000000..c0f39806 --- /dev/null +++ b/doc/src/ipio.tex @@ -0,0 +1,56 @@ +\section{Programming in-place functions} + +VIPS includes a little support for in-place functions --- functions +which operate directly on an image, both reading and writing from the same +descriptor via the data pointer. This is an extremely dangerous way to +handle IO, since any bugs in your program will trash your input image. + +Operations of this type should call \verb+im_rwcheck()+ instead of +\verb+im_incheck()+. \verb+im_rwcheck()+ tries to get a descriptor ready +for in-place writing. For example, a function which cleared an image to +black might be written as: + +\begin{verbatim} +#include +#include + +#include + +int +black_inplace( IMAGE *im ) +{ + /* Check that we can RW to im. + */ + if( im_rwcheck( im ) ) + return( -1 ); + + /* Zap the image! + */ + memset( im->data, 0, + IM_IMAGE_SIZEOF_LINE( im ) * + im->Ysize ); + + return( 0 ); +} +\end{verbatim} + +This function might be called from an application as: + +\begin{verbatim} +#include +#include + +#include + +void +zap( char *name ) +{ + IMAGE *im; + + if( !(im = im_open( name, "rw" )) || + black_inplace( im ) || + im_updatehist( im, "zap image" ) || + im_close( im ) ) + error_exit( "failure!" ); +} +\end{verbatim} diff --git a/doc/src/mydefs.tex b/doc/src/mydefs.tex new file mode 100644 index 00000000..5310b8ce --- /dev/null +++ b/doc/src/mydefs.tex @@ -0,0 +1,113 @@ +% My defs + +% Computer Text, Computer text=>, Computer Text Display +\newcommand{\ct}[1]{\texttt{#1}} +\newcommand{\ctr}[1]{\ct{#1} / } + +\newenvironment{ctd}{\begin{quote}\footnotesize\tt}{\end{quote}} +\pagecolor{white} + +% abbreviations +\newcommand{\vips}{\ct{vips}} +\newcommand{\nip}{\ct{nip2}} +\newcommand{\bs}{$\backslash$} +\newcommand{\rtp}{\^{ }} +\newcommand{\cielab}{\emph{CIE~}$L^{*}a^{*}b^{*}$} +\newcommand{\ciexyz}{\emph{CIE XYZ}} +\newcommand{\cieyxy}{\emph{CIE Yxy}} +\newcommand{\cielch}{\emph{CIE LCh}} +\newcommand{\cieucs}{\emph{UCS(1:1)}} +\newcommand{\cross}{$\times{}$} + +% make a label ... override this for HTML output +\newcommand{\mylabel}[1]{\label{#1}} + +% generate " on page xx" if a label is referring to something on another page +% override this for HTML output +\newcounter{boink} +\newcommand{\onpage}[1]{% +\addtocounter{boink}{1}% +\label{atref\theboink{}}% +\ifthenelse{\pageref{atref\theboink{}}=\pageref{#1}}% +{}% +{ on page~\pageref{#1}}} + +% format a reference to a section .. "$3.11 on page 37" +\newcommand{\pref}[1]{\S\ref{#1}\onpage{#1}} +\newcommand{\tref}[1]{Table~\ref{#1}\onpage{#1}} +\newcommand{\fref}[1]{Figure~\ref{#1}\onpage{#1}} +\newcommand{\cref}[1]{Chapter~\ref{#1}\onpage{#1}} +\newcommand{\aref}[1]{Appendix~\ref{#1}\onpage{#1}} + +% Insert a file ... height and name. +\newcommand{\fig}[2]{ + \begin{center} + \includegraphics[height=#1]{figs/#2} + \end{center} +} + +% Insert a file ... width and name. +\newcommand{\figw}[2]{ + \begin{center} + \includegraphics[width=#1]{figs/#2} + \end{center} +} + +% make a 2-column figure ... define our own so we can easily override in html +% output +\newenvironment{fig2}{\begin{figure*}}{\end{figure*}} + +% same for 2-col tables +\newenvironment{tab2}{\begin{table*}}{\end{table*}} + +% environment for setting ip defs +\newenvironment{ipdef}{ +\par +\samepage +\begin{ctd} +\begin{tabular}{@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}ll@{\hspace{0.2em}}lllllllllllllllllllllllllllll} +~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & ~ & ~~~ & \\[-1.3em] +}{ +\end{tabular} +\end{ctd} +\par +} + +% causes problems for htlatex :-( +% make this a noop for now +% \newcommand{\dtxt}[1]{\multicolumn{25}{@{\hspace{0.2em}}l}{#1}} +\newcommand{\dtxt}[1]{#1} + +% Insert a blank page +\newcommand{\blankpage}{% +\newpage +~~~~ +\pagestyle{plain} +\newpage +% Another one necessary in twocolumn mode +~~~~ +\newpage +\pagestyle{fancy} +} + +%\addtolength{\headheight}{3pt} + +% Make text a bit wider, since we are two column. +\addtolength{\textwidth}{0.5in} +\addtolength{\oddsidemargin}{-0.25in} +\addtolength{\evensidemargin}{-0.25in} + +% twocolumn seems to remove the binding offset ... add it back +%\addtolength{\oddsidemargin}{-0.2in} +%\addtolength{\evensidemargin}{0.2in} + +% More space between headers and footers and the body +\addtolength{\topmargin}{-0.5em} +\addtolength{\headsep}{0.5em} +\addtolength{\footskip}{0.5em} + +% Swap left and right binding offsets +\newlength{\fred} +\setlength{\fred}{\oddsidemargin} +\setlength{\oddsidemargin}{\evensidemargin} +\setlength{\evensidemargin}{\fred} diff --git a/doc/src/operintro.tex b/doc/src/operintro.tex new file mode 100644 index 00000000..20ae3db3 --- /dev/null +++ b/doc/src/operintro.tex @@ -0,0 +1,109 @@ +\section{Introduction} +\mylabel{sec:oper} + +This chapter explains how to write image processing operations using the +VIPS image I/O (input-output) system. For background, you should probably +take a look at \pref{sec:appl}. This is supposed to be a tutorial, if you +need detailed information on any particular function, use the on-line UNIX +manual pages. + +\subsection{Why use VIPS?} + +If you use the VIPS image I/O system, you get a number of benefits: + +\begin{description} + +\item[Threading] +If your computer has more than one CPU, the VIPS I/O system will automatically +split your image processing operation into separate threads (provided you +use PIO, see below). You should get an approximately linear speed-up as +you add more CPUs. + +\item[Pipelining] +Provided you use PIO (again, see below), VIPS can automatically join +operations together. A sequence of image processing operations will all +execute together, with image data flowing through the processing pipeline +in small pieces. This makes it possible to perform complex processing on +very large images with no need to worry about storage management. + +\item[Composition] +Because VIPS can efficiently compose image processing operations, you can +implement your new operation in small, reusable, easy-to-understand +pieces. VIPS already has a lot of these: many new operations can be +implemented by simply composing existing operations. + +\item[Large files] +Provided you use PIO and as long as the underlying OS supports large files +(that is, files larger than 2GB), VIPS operations can work on files larger +than can be addressed with 32 bits on a plain 32-bit machine. VIPS operations +only see 32 bit addresses; the VIPS I/O system transparently maps these to +64 bit operations for I/O. Large file support is included on most unixes after +about 1998. + +\item[Abstraction] +VIPS operations see only arrays of numbers in native format. Details of +representation (big/little endian, VIPS/TIFF/JPEG file format, etc.) are +hidden from you. + +\item[Interfaces] +Once you have your image processing operation implemented, it automatically +appears in all of the VIPS interfaces. VIPS comes with a GUI (\nip{}), a +UNIX command-line interface (\vips{}) and a C++ and Python API. + +\item[Portability] +VIPS operations can be compiled on most unixes, Mac OS X and Windows NT, 2000 +and XP without modification. Mostly. + +\end{description} + +\subsection{I/O styles} + +The I/O system supports three styles of input-output. + +\begin{description} + +\item[Whole-image I/O (WIO)] +This style is a largely a left-over from VIPS 6.x. WIO image-processing +operations have all of the input image given to them in a large memory +array. They can read any of the input pels at will with simple pointer +arithmetic. + +\item[Partial-image I/O (PIO)] +In this style operations only have a small part of the input image available +to them at any time. When PIO operations are joined together into a pipeline, +images flow through them in small pieces, with all the operations in a +pipeline executing at the same time. + +\item[In-place] +The third style allows pels to be read and written anywhere in +the image at any time, and is used by the VIPS in-place operations, such +as \verb+im_fastline()+. You should only use it for operations which would +just be impossibly inefficient to write with either of the other two styles. + +\end{description} + +WIO operations are easy to program, but slow and inflexible when images +become large. PIO operations are harder to program, but scale well as images +become larger, and are automatically parallelized by the VIPS I/O system. + +If you can face it, and if your algorithm can be expressed in this way, you +should write your operations using PIO. Whichever you choose, applications +which call your operation will see no difference, except in execution speed. + +If your image processing operation performs no coordinate transformations, +that is, if your output image is the same size as your input image or images, +and if each output pixel depends only upon the pixel at the corresponding +position in the input images, then you can use the \verb+im_wrapone()+ +and \verb+im_wrapmany()+ operations. These take a simple buffer-processing +operation supplied by you and wrap it up as a full-blown PIO operation. +See~\pref{sec:wrapone}. + +\subsection{What's new in this version} + +The VIPS API is mostly unaltered since 7.3, so there are not many major +changes. I've just reworked the text, reformatted, fixed a few typos, +and changed the dates. + +VIPS has acquired some crud over the years. We are planning to clean all +this stuff up at some stage (and break backwards-compatibility). Maybe for +VIPS 8 :-( diff --git a/doc/src/packages.tex b/doc/src/packages.tex new file mode 100644 index 00000000..b72c2635 --- /dev/null +++ b/doc/src/packages.tex @@ -0,0 +1,822 @@ +\section{VIPS packages} +\mylabel{sec:packages} + +\subsection{Arithmetic} + +See \fref{fg:arithmetic}. + +Arithmetic functions work on images as if each band element were a separate +number. All operations are point-to-point --- each output element depends +exactly upon the corresponding input element. All (except in a few cases +noted in the manual pages) will work with images of any type (or any mixture +of types), of any size and of any number of bands. + +Arithmetic operations try to preserve precision by increasing the number of +bits in the output image when necessary. Generally, this follows the ANSI C +conventions for type promotion --- so multiplying two \verb+IM_BANDFMT_UCHAR+ +images together, for example, produces a \verb+IM_BANDFMT_USHORT+ image, and +taking the \verb+im_costra()+ of a \verb+IM_BANDFMT_USHORT+ image produces +a \verb+IM_BANDFMT_FLOAT+ image. The details of the type conversions are +in the manual pages. + +\begin{fig2} +\begin{verbatim} +john% vips --list arithmetic +im_abs - absolute value +im_acostra - acos of image (result in degrees) +im_add - add two images +im_asintra - asin of image (result in degrees) +im_atantra - atan of image (result in degrees) +im_avg - average value of image +im_point_bilinear - interpolate value at single point, linearly +im_ceil - round to smallest integal value not less than +im_cmulnorm - multiply two complex images, normalising output +im_costra - cos of image (angles in degrees) +im_deviate - standard deviation of image +im_divide - divide two images +im_exp10tra - 10^pel of image +im_expntra - x^pel of image +im_expntra_vec - [x,y,z]^pel of image +im_exptra - e^pel of image +im_fav4 - average of 4 images +im_floor - round to largest integal value not greater than +im_gadd - calculate a*in1 + b*in2 + c = outfile +im_invert - photographic negative +im_lintra - calculate a*in + b = outfile +im_linreg - pixelwise linear regression +im_lintra_vec - calculate a*in + b -> out, a and b vectors +im_litecor - calculate max(white)*factor*(in/white), if clip == 1 +im_log10tra - log10 of image +im_logtra - ln of image +im_max - maximum value of image +im_maxpos - position of maximum in image +im_maxpos_avg - position of maximum, averaging in case of draw +im_measure - measure averages of a grid of patches +im_min - minimum value of image +im_minpos - position of minimum value of image +im_multiply - multiply two images +im_powtra - pel^x ofbuildimage +im_powtra_vec - pel^[x,y,z] of image +im_remainder - remainder after integer division +im_remainderconst - remainder after integer division by a constant +im_remainderconst_vec - remainder after integer division by a vector +im_rint - round to nearest integal value +im_sign - unit vector in direction of value +im_sintra - sin of image (angles in degrees) +im_stats - many image statistics in one pass +im_subtract - subtract two images +im_tantra - tan of image (angles in degrees) +\end{verbatim} +\caption{Arithmetic functions} +\label{fg:arithmetic} +\end{fig2} + +\subsection{Relational} + +See \fref{fg:relational}. + +Relational functions compare images to other images or to constants. They +accept any image or pair of images (provided they are the same size and +have the same number of bands --- their types may differ) and produce a +\verb+IM_BANDFMT_UCHAR+ image with the same number of bands as the input +image, with 255 in every band element for which the condition is true and +0 elsewhere. + +They may be combined with the boolean functions to form complex relational +conditions. Use \verb+im_max()+ (or \verb+im_min()+) to find out if a +condition is true (or false) for a whole image. + +\begin{fig2} +\begin{verbatim} +john% vips --list relational +im_blend - use cond image to blend between images in1 and in2 +im_equal - two images equal in value +im_equal_vec - image equals doublevec +im_equalconst - image equals const +im_ifthenelse - use cond image to choose pels from image in1 or in2 +im_less - in1 less than in2 in value +im_less_vec - in less than doublevec +im_lessconst - in less than const +im_lesseq - in1 less than or equal to in2 in value +im_lesseq_vec - in less than or equal to doublevec +im_lesseqconst - in less than or equal to const +im_more - in1 more than in2 in value +im_more_vec - in more than doublevec +im_moreconst - in more than const +im_moreeq - in1 more than or equal to in2 in value +im_moreeq_vec - in more than or equal to doublevec +im_moreeqconst - in more than or equal to const +im_notequal - two images not equal in value +im_notequal_vec - image does not equal doublevec +im_notequalconst - image does not equal const +\end{verbatim} +\caption{Relational functions} +\label{fg:relational} +\end{fig2} + +\subsection{Boolean} + +See \fref{fg:boolean}. + +The boolean functions perform boolean arithmetic on pairs of +\verb+IM_BANDFMT_UCHAR+ images. They are useful for combining the results of +the relational and morphological functions. You can use +\verb+im_eorconst()+ with 255 as \verb+im_not()+. + +\begin{fig2} +\begin{verbatim} +john% vips --list boolean +im_andimage - bitwise and of two images +im_andimageconst - bitwise and of an image with a constant +im_andimage_vec - bitwise and of an image with a vector constant +im_orimage - bitwise or of two images +im_orimageconst - bitwise or of an image with a constant +im_orimage_vec - bitwise or of an image with a vector constant +im_eorimage - bitwise eor of two images +im_eorimageconst - bitwise eor of an image with a constant +im_eorimage_vec - bitwise eor of an image with a vector constant +im_shiftleft - shift integer image n bits to left +im_shiftright - shift integer image n bits to right +\end{verbatim} +\caption{Boolean functions} +\label{fg:boolean} +\end{fig2} + +\subsection{Colour} +\label{sec:colour} + +See \fref{fg:colour}. + +The colour functions can be divided into two main types. First, functions to +transform images between the different colour spaces supported by VIPS: +\verb+RGB+ (also referred to as \verb+disp+), \verb+sRGB+, \verb+XYZ+, +\verb+Yxy+, \verb+Lab+, \verb+LabQ+, \verb+LabS+, \verb+LCh+ and +\verb+UCS+), and second, functions for calculating colour difference +metrics. Figure~\ref{fg:convert} shows how the VIPS colour spaces +interconvert. + +\begin{fig2} +\figw{5in}{interconvert.png} +\caption{VIPS colour space conversion} +\label{fg:convert} +\end{fig2} + +The colour spaces supported by VIPS are: + +\begin{description} + +\item[\texttt{LabQ}] +This is the principal VIPS colorimetric storage format. See the +man page for \verb+im_LabQ2Lab()+ for an explanation. You cannot perform +calculations on \verb+LabQ+ images. They are for storage only. Also refered +to as \verb+LABPACK+. + +\item[\texttt{LabS}] +This format represents coordinates in \cielab{} space as a three- +band \verb+IM_BANDFMT_SHORT+ image, scaled to fit the full range of bits. It is +the best format for computation, being relatively compact, quick, and +accurate. Colour values expressed in this way are hard to visualise. + +\item[\texttt{Lab}] +\verb+Lab+ colourspace represents \cielab{} colour values with a three-band +\verb+IM_BANDFMT_FLOAT+ image. This is the simplest format for general work: adding the +constant 50 to the L channel, for example, has the expected result. + +\item[\texttt{XYZ}] +\ciexyz{} colour space represented as a three-band \verb+IM_BANDFMT_FLOAT+ +image. + +\item[\texttt{XYZ}] +\cieyxy{} colour space represented as a three-band \verb+IM_BANDFMT_FLOAT+ +image. + +\item[\texttt{RGB}] +(also refered to as \verb+disp+) This format is similar to the RGB colour +systems used in other packages. If you want to export your image to a PC, +for example, convert your colorimetric image to \verb+RGB+, then turn it +to TIFF with \verb+im_vips2tiff()+. You need to supply a structure which +characterises your display. See the manual page for \verb+im_col_XYZ2rgb()+ +for hints on these guys. + +VIPS also supports \verb+sRGB+. This is a version of RGB with a carefully +defined and standard conversion from XYZ. See: + +\begin{verbatim} +http://www.color.org/ +\end{verbatim} + +\item[\texttt{LCh}] +Like \verb+Lab+, but rectangular $ab$ coordinates are replaced with polar $Ch$ +(Chroma and hue) coordinates. Hue angles are expressed in degrees. + +\item[\texttt{UCS}] +A colour space based on the CMC(1:1) colour difference measurement. This +is a highly uniform colour space, much better than \cielab{} for expressing +small differences. Conversions to and from \verb+UCS+ are extremely slow. + +\end{description} + +All VIPS colourspaces assume a D65 illuminant. + +The colour-difference functions calculate either $\Delta{}E$ \cielab{} (1976 +or 2000) or $\Delta{}E$ CMC(1:1) on two images in \verb+Lab+, \verb+XYZ+ +or \verb+disp+ colour space. + +\begin{fig2} +\begin{verbatim} +example% vips --list colour +im_LCh2Lab - convert LCh to Lab +im_LCh2UCS - convert LCh to UCS +im_Lab2LCh - convert Lab to LCh +im_Lab2LabQ - convert Lab to LabQ +im_Lab2LabS - convert Lab to LabS +im_Lab2UCS - convert Lab to UCS +im_Lab2XYZ - convert D65 Lab to XYZ +im_Lab2XYZ_temp - convert Lab to XYZ, with a specified colour temperature +im_Lab2disp - convert Lab to displayable +im_LabQ2LabS - convert LabQ to LabS +im_LabQ2Lab - convert LabQ to Lab +im_LabQ2XYZ - convert LabQ to XYZ +im_LabQ2disp - convert LabQ to displayable +im_LabS2LabQ - convert LabS to LabQ +im_LabS2Lab - convert LabS to Lab +im_UCS2LCh - convert UCS to LCh +im_UCS2Lab - convert UCS to Lab +im_UCS2XYZ - convert UCS to XYZ +im_XYZ2Lab - convert D65 XYZ to Lab +im_XYZ2Lab_temp - convert XYZ to Lab, with a specified colour temperature +im_XYZ2UCS - convert XYZ to UCS +im_XYZ2Yxy - convert XYZ to Yxy +im_XYZ2disp - convert XYZ to displayble +im_XYZ2sRGB - convert XYZ to sRGB +im_Yxy2XYZ - convert Yxy to XYZ +im_dE00_fromLab - calculate delta-E CIE2000 for two Lab images +im_dECMC_fromLab - calculate delta-E CMC(1:1) for two Lab images +im_dECMC_fromdisp - calculate delta-E CMC(1:1) for two displayable images +im_dE_fromLab - calculate delta-E for two Lab images +im_dE_fromXYZ - calculate delta-E for two XYZ images +im_dE_fromdisp - calculate delta-E for two displayable images +im_disp2Lab - convert displayable to Lab +im_disp2XYZ - convert displayable to XYZ +im_icc_ac2rc - convert LAB from AC to RC using a profile +im_icc_export - convert LAB to 8-bit device with a profile +im_icc_export_depth - convert LAB to device with a profile +im_icc_import - convert device to LAB with a profile +im_icc_import_embedded - convert device to LAB using the embedded profile +im_icc_present - test for presence of ICC library +im_icc_transform - convert between two device images with a pair of profiles +im_lab_morph - morph colourspace of a LAB image +im_sRGB2XYZ - convert sRGB to XYZ +\end{verbatim} +\caption{Colour functions} +\label{fg:colour} +\end{fig2} + +\subsection{Conversion} + +See \fref{fg:conversion}. + +These functions may be split into three broad groups: functions which convert +between the VIPS numeric formats (\verb+im_clip2fmt()+, for example, converts +an image of any type to the specified \verb+IM_BANDFMT+), functions +supporting complex arithmetic (\verb+im_c2amph()+, for example, converts +a complex image from rectangular to polar co ordinates) and functions +which perform some simple geometric conversion (\verb+im_extract()+ forms +a sub-image). + +\verb+gbandjoin+ and the C function \verb+im_gbandjoin()+ will do a bandwise +join of many images at the same time. See the manual pages. + +\begin{fig2} +\begin{verbatim} +example% vips --list conversion +im_bandjoin - bandwise join of two images +im_bernd - extract from pyramid as jpeg +im_black - generate black image +im_c2amph - convert real and imaginary to phase and amplitude +im_c2imag - extract imaginary part of complex image +im_c2ps - find power spectrum of complex image +im_c2real - extract real part of complex image +im_c2rect - convert phase and amplitude to real and imaginary +im_clip2c - convert to signed 8-bit integer +im_clip2cm - convert to complex +im_clip2d - convert to double-precision float +im_clip2dcm - convert to double complex +im_clip2f - convert to single-precision float +im_clip2fmt - convert image format to ofmt +im_clip2i - convert to signed 32-bit integer +im_clip2s - convert to signed 16-bit integer +im_clip2ui - convert to unsigned 32-bit integer +im_clip2us - convert to unsigned 16-bit integer +im_clip - convert to unsigned 8-bit integer +im_copy - copy image +im_copy_morph - copy image, setting pixel layout +im_copy_swap - copy image, swapping byte order +im_copy_set - copy image, setting informational fields +im_copy_set_meta - copy image, setting a meta field +im_csv2vips - read a file in csv format +im_extract_area - extract area +im_extract_areabands - extract area and bands +im_extract_band - extract band +im_extract_bands - extract several bands +im_extract - extract area/band +im_falsecolour - turn luminance changes into chrominance changes +im_fliphor - flip image left-right +im_flipver - flip image top-bottom +im_gbandjoin - bandwise join of many images +im_grid - chop a tall thin image into a grid of images +im_insert - insert sub-image into main image at position +im_insert_noexpand - insert sub-image into main image at position, no expansion +im_jpeg2vips - convert from jpeg +im_lrjoin - join two images left-right +im_magick2vips - load file with libMagick +im_mask2vips - convert DOUBLEMASK to VIPS image +im_msb - convert to uchar by discarding bits +im_msb_band - convert to single band uchar by discarding bits +im_png2vips - convert PNG file to VIPS image +im_exr2vips - convert an OpenEXR file to VIPS +im_ppm2vips - read a file in pbm/pgm/ppm format +im_analyze2vips - read a file in analyze format +im_print - print string to stdout +im_recomb - linear recombination with mask +im_replicate - replicate an image horizontally and vertically +im_ri2c - join two non-complex images to form complex +im_rot180 - rotate image 180 degrees +im_rot270 - rotate image 270 degrees clockwise +im_rot90 - rotate image 90 degrees clockwise +im_scale - scale image linearly to fit range 0-255 +im_scaleps - logarithmic scale of image to fit range 0-255 +im_rightshift_size - decrease size by a power-of-two factor +im_slice - slice an image using two thresholds +\end{verbatim} +\caption{Conversion functions} +\label{fg:conversion} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +im_subsample - subsample image by integer factors +im_system - run command on image +im_tbjoin - join two images top-bottom +im_text - generate text image +im_thresh - slice an image at a threshold +im_tiff2vips - convert TIFF file to VIPS image +im_vips2csv - write an image in csv format +im_vips2jpeg - convert to jpeg +im_vips2mask - convert VIPS image to DOUBLEMASK +im_vips2mimejpeg - convert to jpeg as mime type on stdout +im_vips2png - convert VIPS image to PNG file +im_vips2ppm - write a file in pbm/pgm/ppm format +im_vips2tiff - convert VIPS image to TIFF file +im_zoom - simple zoom of an image by integer factors +\end{verbatim} +\caption{Conversion functions (cont.)} +\end{fig2} + +\subsection{Matricies} + +See \fref{fg:matricies}. + +VIPS uses matricies for morphological operations, for convolutions, and +for some colour-space conversions. There are two types of matrix: integer +(\verb+INTMASK+) and double precision floating point (\verb+DOUBLEMASK+). + +For convenience, both types are stored in files as ASCII. The first +line of the file should start with the matrix dimensions, width first, +then on the same line an optional scale and offset. The two size fields +should be integers; the scale and offset may be floats. Subsequent lines +should contain the matrix elements, one row per line. The scale and +offset are the conventional ones used to represent non-integer values in +convolution masks --- in other words: + +\[ +result = {value \over scale} + offset +\] + +If the scale and offset are missing, they default to 1.0 and 0.0. See the +sections on convolution for more on the use of these fields. So as an example, +a 4 by 4 identity matrix would be stored as: + +\begin{verbatim} +4 4 +1 0 0 0 +0 1 0 0 +0 0 1 0 +0 0 0 1 +\end{verbatim} + +And a 3 by 3 mask for block averaging with convolution might be stored as: + +\begin{verbatim} +3 3 9 0 +1 1 1 +1 1 1 +1 1 1 +\end{verbatim} + +\noindent +(in other words, sum all the pels in every 3 by 3 area, and divide by 9). + +This matrix contains only integer elements and so could be used as an +argument to functions expecting both \verb+INTMASK+ and \verb+DOUBLEMASK+ +matricies. However, masks containing floating-point values (such as the +output of \verb+im_matinv()+) can only be used as arguments to functions +expecting \verb+DOUBLEMASK+s. + +A set of functions for mask input and output are also available for +C-programmers --- see the manual pages for \verb+im_read_dmask()+. For +other matrix functions, see also the convolution sections and the arithmetic +sections. + +\begin{fig2} +\begin{verbatim} +example% vips --list matrix +im_matcat - append matrix in2 to the end of matrix in1 +im_matinv - invert matrix +im_matmul - multiply matrix in1 by matrix in2 +im_mattrn - transpose matrix +\end{verbatim} +\caption{Matrix functions} +\label{fg:matricies} +\end{fig2} + +\subsection{Convolution} + +See \fref{fg:convolution}. + +The functions available in the convolution package can be split into five +main groups. + +First, are the convolution functions. The most useful function is +\verb+im_conv()+ which will convolve any non-complex type with an +\verb+INTMASK+ matrix. The output image will have the same size, type, and +number of bands as the input image. Of the other \verb+im_conv()+ functions, +functions whose name ends in \verb+_raw+ do not add a black border around the +output image, functions ending in \verb+f+ use a \verb+DOUBLEMASK+ matrix +and write float (or double) output, and functions containing \verb+sep+ +are for seperable convolutions. \verb+im_compass()+, \verb+im_lindetect()+ +and \verb+im_gradient()+ convolve with rotating masks. \verb+im_embed()+ +is used by the convolution functions to add the border to the output. + +Next, are the build functions. \verb+im_gauss_*mask()+ and its ilk +generate gaussian masks, \verb+im_log_*mask()+ generate logs of Laplacians. +\verb+im_addgnoise()+ and \verb+im_gaussnoise()+ create or add gaussian +noise to an image. + +Two functions do correlation: \verb+im_fastcor()+ does a quick and dirty +correlation, \verb+im_spcor()+ calculates true spatial correlation, and is +rather slow. + +Some functions are provided for analysing images: \verb+im_zerox()+ counts +zero-crossing points in an image, \verb+im_mpercent()+ finds a threshold +that will isolate a percentage of points in an image. + +Finally, \verb+im_resize_linear()+ and \verb+im_shrink()+ do as you would +expect. + +\begin{fig2} +\begin{verbatim} +example% vips --list convolution +im_addgnoise - add gaussian noise with mean 0 and std. dev. sigma +im_compass - convolve with 8-way rotating integer mask +im_contrast_surface - find high-contrast points in an image +im_contrast_surface_raw - find high-contrast points in an image +im_conv - convolve +im_conv_raw - convolve, no border +im_convf - convolve, with DOUBLEMASK +im_convf_raw - convolve, with DOUBLEMASK, no border +im_convsep - seperable convolution +im_convsep_raw - seperable convolution, no border +im_convsepf - seperable convolution, with DOUBLEMASK +im_convsepf_raw - seperable convolution, with DOUBLEMASK, no border +im_convsub - convolve uchar to uchar, sub-sampling by xskip, yskip +im_embed - embed in within a set of borders +im_fastcor - fast correlate in2 within in1 +im_fastcor_raw - fast correlate in2 within in1, no border +im_gauss_dmask - generate gaussian DOUBLEMASK +im_gauss_imask - generate gaussian INTMASK +im_gaussnoise - generate image of gaussian noise with specified statistics +im_gradient - convolve with 2-way rotating mask +im_rank_image - point-wise pixel rank +im_lindetect - convolve with 4-way rotating mask +im_log_dmask - generate laplacian of gaussian DOUBLEMASK +im_log_imask - generate laplacian of gaussian INTMASK +im_maxvalue - point-wise maximum value +im_mpercent - find threshold above which there are percent values +im_rank - rank filter nth element of xsize/ysize window +im_rank_raw - rank filter nth element of xsize/ysize window, no border +im_read_dmask - read matrix of double from file +im_resize_linear - resize to X by Y pixels with linear interpolation +im_rotate_dmask45 - rotate DOUBLEMASK clockwise by 45 degrees +im_rotate_dmask90 - rotate DOUBLEMASK clockwise by 90 degrees +im_rotate_imask45 - rotate INTMASK clockwise by 45 degrees +im_rotate_imask90 - rotate INTMASK clockwise by 90 degrees +im_sharpen - sharpen high frequencies of L channel of LabQ +im_shrink - shrink image by xfac, yfac times +im_spcor - normalised correlation of in2 within in1 +im_spcor_raw - normalised correlation of in2 within in1, no black padding +im_spcor2 - normalised correlation of in2 within in1 +im_spcor2_raw - normalised correlation of in2 within in1, no black padding +im_stretch3 - stretch 3%, sub-pixel displace by xdisp/ydisp +im_zerox - find +ve or -ve zero crossings in IM_BANDFMT_INT image +\end{verbatim} +\caption{Convolution functions} +\label{fg:convolution} +\end{fig2} + +\subsection{In-place operations} +\label{sec:inplace} + +See \fref{fg:inplace}. + +A few of the in-place operations are available from the command-line. Most are +not. More will be added. + +\begin{fig2} +\begin{verbatim} +example% vips --list inplace +im_circle - plot circle on image +im_flood_blob_copy - flood while pixel == start pixel +im_insertplace - draw image sub inside image main at position (x,y) +im_line - draw line between points (x1,y1) and (x2,y2) +im_lineset - draw line between points (x1,y1) and (x2,y2) +\end{verbatim} +\caption{In-place operations} +\label{fg:inplace} +\end{fig2} + +\subsection{Frequency filtering} + +See \fref{fg:freq}. + +The basic Fourier functions are \verb+im_fwfft()+ and +\verb+im_invfft()+, which calculate the fast-fourier transform and inverse +transform of an image. Also \verb+im_invfftr()+, which just returns the real +part of the inverse transform. +The Fourier image has its origin at pel (0,0) --- +for viewing, use \verb+im_rotquad()+ to move the origin to the centre of +the image. + +Once an image is in the frequency domain, it can be filtered by multiplying +it with a mask image. The VIPS mask generator is \verb+im_create_fmask()+ +see the manual page for details of the arguments, but it will create low +pass, high pass, ring pass and band pass filters, which may each be ideal, +Gaussian or Butterworth. There is also a fractal mask option. + +The other functions in the package build on these base +facilities. \verb+im_freqflt()+ transforms an input image to +Fourier space, multiplies it by a mask image, and transforms it back +again. \verb+im_flt_image_freq()+ will create a mask image of the correct +size for you, and call \verb+im_freqflt()+. \verb+im_disp_ps()+ will call +the right combinations of functions to make a displayable power spectrum +for an image. + +\begin{fig2} +\begin{verbatim} +example% vips --list freq_filt +im_create_fmask - create frequency domain filter mask +im_disp_ps - make displayable power spectrum +im_flt_image_freq - frequency domain filter image +im_fractsurf - generate a fractal surface of given dimension +im_freqflt - frequency-domain filter of in with mask +im_fwfft - forward fast-fourier transform +im_rotquad - rotate image quadrants to move origin to centre +im_invfft - inverse fast-fourier transform +im_invfftr - real part of inverse fast-fourier transform +\end{verbatim} +\caption{Fourier functions} +\label{fg:freq} +\end{fig2} + +\subsection{Histograms and LUTs} + +See \fref{fg:hist}. + +VIPS represents histograms and look-up tables in the same way --- as images. + +They should have either \verb+Xsize+ or \verb+Ysize+ set to 1, and the +other dimension set to the number of elements in the table. The table can be +of any size, have any band format, and have any number of bands. + +Use \verb+im_histgr()+ to find the histogram of an image. Use +\verb+im_histnD()+ to find the n-dimensional histogram of an n-band +image. Perform operations on histograms with \verb+im_histcum()+, +\verb+im_histnorm()+, \verb+im_histspec()+, \verb+im_invertlut()+. Visualise +histograms with \verb+im_histplot()+. Use a histogram (or LUT) to transform +an image with \verb+im_maplut()+. Build a histogram from scratch with +\verb+im_identity()+ or \verb+im_identity_ushort()+. + +Use \verb+im_lhist*()+ for local histogram equalisation, and +\verb+im_stdif*()+ for statisticaol differencing. The \verb+im_tone_*()+ +functions are for operations on the L channel of a LAB image. Other +functions are useful combinations of these basic operations. + +\begin{fig2} +\begin{verbatim} +example% vips --list histograms_lut +im_gammacorrect - gamma-correct image +im_heq - histogram-equalise image +im_hist - find and graph histogram of image +im_histcum - turn histogram to cumulative histogram +im_histeq - form histogram equalistion LUT +im_histgr - find histogram of image +im_histnD - find 1D, 2D or 3D histogram of image +im_histnorm - form normalised histogram +im_histplot - plot graph of histogram +im_histspec - find histogram which will make pdf of in match ref +im_hsp - match stats of in to stats of ref +im_identity - generate identity histogram +im_identity_ushort - generate ushort identity histogram +im_ismonotonic - test LUT for monotonicity +im_lhisteq - local histogram equalisation +im_lhisteq_raw - local histogram equalisation, no border +im_invertlut - generate correction table from set of measures +im_buildlut - generate LUT table from set of x/y positions +im_maplut - map image through LUT +im_project - find horizontal and vertical projections of an image +im_stdif - statistical differencing +im_stdif_raw - statistical differencing, no border +im_tone_analyse - analyse in and create LUT for tone adjustment +im_tone_build - create LUT for tone adjustment of LabS images +im_tone_build_range - create LUT for tone adjustment +im_tone_map - map L channel of LabS or LabQ image through LUT +\end{verbatim} +\caption{Histogram/LUT functions} +\label{fg:hist} +\end{fig2} + +\subsection{Morphology} + +See \fref{fg:morph}. + +The morphological functions are used on one-band \verb+IM_BANDFMT_UCHAR+ binary +images (images containing only zero and not-zero). They search images +for particular patterns of pixels (specified with the mask argument), +either adding or removing pixels when they find a match. They are useful +for cleaning up images --- for example, you might threshold an image, and +then use one of the morphological functions to remove all single isolated +pixels from the result. + +If you combine the morphological operators with the mask rotators +(\verb+im_rotate_imask45()+, for example) and apply them repeatedly, you +can achieve very complicated effects: you can thin, prune, fill, open edges, +close gaps, and many others. For example, see `Fundamentals of Digital +Image Processing' by A. Jain, pp 384-388, Prentice-Hall, 1989 for more ideas. + +Beware that VIPS reverses the usual image processing convention, by assuming +white objects on a black background. + +The mask you give to the morphological functions should contain only the +values 0 (for background), 128 (for don't care) and 255 (for object). The +mask must have odd length sides --- the origin of the mask is taken to be +the centre value. For example, the mask: + +\begin{verbatim} +3 3 +128 255 128 +255 0 255 +128 255 128 +\end{verbatim} + +\noindent +applied to an image with \verb+im_erode()+, will find all black pixels +4-way connected with white pixels. Essentially, \verb+im_dilate()+ +sets pixels in the output if any part of the mask matches, whereas +\verb+im_erode()+ sets pixels only if all of the mask matches. + +The \verb+_raw()+ version of the functions do not add a black border to the +output. \verb+im_cntlines()+ and \verb+im_profile+ are occasionally useful for +analysing results. + +See the boolean operations \verb+im_and()+, \verb+im_or()+ and +\verb+im_eor()+ for analogues of the usual set difference and set +union operations. + +\begin{fig2} +\begin{verbatim} +example% vips --list morphology +im_cntlines - count horizontal or vertical lines +im_dilate - dilate image with mask, adding a black border +im_dilate_raw - dilate image with mask +im_erode - erode image with mask, adding a black border +im_erode_raw - erode image with mask +im_profile - find first horizontal/vertical edge +\end{verbatim} +\caption{Morphological functions} +\label{fg:morph} +\end{fig2} + +\subsection{Mosaicing} + +See \fref{fg:mosaicing}. + +These functions are useful for joining many small images together to make one +large image. They can cope with unstable contrast, and arbitary sub-image +layout, but will not do any geometric correction. The mosaicing functions +can be grouped into layers: + +The lowest level functions are \verb+im_correl()+. and \verb+im_affine()+. +\verb+im_correl()+ searches a large image for a small sub-image, returning +the position of the best sub-image match. \verb+im_affine()+ performs +a general affine transform on an image: that is, any transform in which +parallel lines remain parallel. + +Next, \verb+im_lrmerge()+ and \verb+im_tbmerge()+ blend two images together +left-right or up-down. + +Next up are \verb+im_lrmosaic()+ and \verb+im_tbmosaic()+. These use the +two low-level merge operations to join two images given just an approximate +overlap as a start point. Optional extra parameters let you do 'balancing' +too: if your images have come from a source where there is no precise +control over the exposure (for example, images from a tube camera, or a +set of images scanned from photographic sources), \verb+im_lrmosaic()+ +and \verb+im_tbmosaic()+ will adjust the contrast of the left image to +match the right, the right to the left, or both to some middle value. + +The functions \verb+im_lrmosaic1()+ and \verb+im_tbmosaic1()+ are first-order +analogues of the basic mosaic functions: they take two tie-points and use +them to rotate and scale the right-hand or bottom image before starting to join. + +Finally, \verb+im_global_balance()+ can be used to re-balance a mosaic +which has been assembled with these functions. It will generally do a +better job than the low-level balancer built into \verb+im_lrmosaic()+ +and \verb+im_tbmosaic()+. See the man page. \verb+im_remosaic()+ uses the same +techniques, but will reassemble the image from a different set of source +images. + +\begin{fig2} +\begin{verbatim} +example% vips --list mosaicing +im_affine - affine transform +im_correl - search area around sec for match for area around ref +im__find_lroverlap - search for left-right overlap of ref and sec +im__find_tboverlap - search for top-bottom overlap of ref and sec +im_global_balance - automatically rebuild mosaic with balancing +im_global_balancef - automatically rebuild mosaic with balancing, float output +im_lrmerge - left-right merge of in1 and in2 +im_lrmerge1 - first-order left-right merge of ref and sec +im_lrmosaic - left-right mosaic of ref and sec +im_lrmosaic1 - first-order left-right mosaic of ref and sec +im_match_linear - resample ref so that tie-points match +im_match_linear_search - search sec, then resample so that tie-points match +im_remosaic - automatically rebuild mosaic with new files +im_similarity_area - output area xywh of similarity transformation +im_similarity - similarity transformation +im_tbmerge - top-bottom merge of in1 and in2 +im_tbmerge1 - first-order top-bottom merge of in1 and in2 +im_tbmosaic - top-bottom mosaic of in1 and in2 +im_tbmosaic1 - first-order top-bottom mosaic of ref and sec +\end{verbatim} +\caption{Mosaic functions} +\label{fg:mosaicing} +\end{fig2} + +\subsection{Other} + +See \fref{fg:other}. + +These functions generate various test images. You can combine them with +the arithmetic and rotate functions to build more complicated images. + +The \verb+im_benchmark*()+ operations are for testing the VIPS SMP system. + +\begin{fig2} +\begin{verbatim} +example% vips --list other +im_benchmark - do something complicated for testing +im_benchmark2 - do something complicated for testing +im_benchmarkn - do something complicated for testing +im_eye - generate IM_BANDFMT_UCHAR [0,255] frequency/amplitude image +im_grey - generate IM_BANDFMT_UCHAR [0,255] grey scale image +im_feye - generate IM_BANDFMT_FLOAT [-1,1] frequency/amplitude image +im_fgrey - generate IM_BANDFMT_FLOAT [0,1] grey scale image +im_fzone - generate IM_BANDFMT_FLOAT [-1,1] zone plate image +im_make_xy - generate image with pixel value equal to coordinate +im_zone - generate IM_BANDFMT_UCHAR [0,255] zone plate image +\end{verbatim} +\caption{Other functions} +\label{fg:other} +\end{fig2} + +\subsection{IO functions} + +See \fref{fg:other}. + +These functions are related to the image IO system. + +\begin{fig2} +\begin{verbatim} +example% vips --list iofuncs +im_binfile - open a headerless binary file +im_cache - cache results of an operation +im_guess_prefix - guess install area +im_header_get_type - return field type +im_header_int - extract int fields from header +im_header_double - extract double fields from header +im_header_string - extract string fields from header +im_version - VIPS version number +im_version_string - VIPS version string +\end{verbatim} +\caption{IO functions} +\label{fg:io} +\end{fig2} diff --git a/doc/src/pio.tex b/doc/src/pio.tex new file mode 100644 index 00000000..c951c50d --- /dev/null +++ b/doc/src/pio.tex @@ -0,0 +1,868 @@ +\section{Programming PIO functions} +\label{sec:pio} + +The VIPS PIO system has a number of advantages over WIO, as summarised in +the introduction. On the other hand, they are a bit more complicated. + +\subsection{Easy PIO with \texttt{im\_wrapone()} and \texttt{im\_wrapmany()}} +\label{sec:wrapone} + +PIO is a very general image IO system, and because of this flexibility, +can be complicated to program. As a convenience, VIPS offers an easy-to-use +layer over PIO with the funtions \verb+im_wrapone()+ and \verb+im_wrapmany()+. + +If your image processing function is uninterested in coordinates, that is, +if your input and output images are the same size, and each output pixel +depends only upon the value of the corresponding pixel in the input image +or images, then these functions are for you. + +Consider the \verb+invert()+ function of figure~\ref{fg:invert}. First, +we have to write the core of this as a buffer-processing function: + +\begin{verbatim} +#include +#include + +#include + +/* p points to a buffer of pixels which + * need inverting, q points to the buffer + * we should write the result to, and n + * is the number of pels present. + */ +static void +invert_buffer( unsigned char *p, + unsigned char *q, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) + q[i] = 255 - p[i]; +} +\end{verbatim} + +Now we have to wrap up this very primitive expression of the invert operation +as a PIO function. We use \verb+im_wrapone()+ to do this. It has type: + +\begin{verbatim} +int +im_wrapone( IMAGE *in, IMAGE *out, + im_wrapone_fn fn, void *a, void *b ) +\end{verbatim} + +\noindent +where: + +\begin{verbatim} +void +(*im_wrapone_fn)(void *in, void *out, + int n, void *a, void *b ) +\end{verbatim} + +\noindent +almost the same type as our buffer-processing function above. The values +\verb+a+ and \verb+b+ are carried around by VIPS for whatever use you +fancy. \verb+invert()+ can now be written as: + +\begin{verbatim} +int +invert( IMAGE *in, IMAGE *out ) +{ + /* Check parameters. + */ + if( in->BandFmt != IM_BANDFMT_UCHAR || + in->Bands != 1 || + in->Coding != IM_CODING_NONE ) { + im_error( "invert", "bad image" ); + return( -1 ); + } + + /* Set fields in output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Process! We don't use either of the + * user parameters in this function, + * so leave them as NULL. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) invert_buffer, + NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} + +And that's all there is to it. This function will have all of the desirable +properties of PIO functions, while being as easy to program as the WIO +\verb+invert()+ earlier in this chapter. + +This version of \verb+invert()+ is not very general: it will only accept +one-band unsigned char images. It is easy to modify for n-band images: + +\begin{verbatim} +/* As before, but use one of the user + * parameters to pass in the number of + * bands in the image. + */ +static void +invert_buffer( unsigned char *p, + unsigned char *q, int n, + IMAGE *in ) +{ + int i; + int sz = n * in->Bands; + + for( i = 0; i < sz; i++ ) + q[i] = 255 - p[i]; +} +\end{verbatim} + +We must also modify \verb+invert()+: + +\begin{verbatim} +int +invert( IMAGE *in, IMAGE *out ) +{ + /* Check parameters. + */ + if( in->BandFmt != IM_BANDFMT_UCHAR || + in->Coding != IM_CODING_NONE ) { + im_error( "invert", "bad image" ); + return( -1 ); + } + + /* Set fields in output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Process! The first user-parameter + * is the number of bands involved. + */ + if( im_wrapone( in, out, + (im_wrapone_fn)invert_buffer, + in, NULL ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} + +There are two significant hidden traps here. First, inside the buffer +processing functions, you may only read the contents of the user parameters +\verb+a+ and \verb+b+, you may not write to them. This is because on a +multi-CPU machine, several copies of your buffer-processing functions will +be run in parallel --- if they all write to the same place, there will be +complete confusion. If you need writeable parameters (for example, to count +and report overflows), you can't use \verb+im_wrapone()+, you'll have to +use the PIO system in all its gory detail, see below. + +Secondly, your buffer processing function may not be called immediately. VIPS +may decide to delay evaluation of your operation until long after the call +to \verb+invert()+ has returned. As a result, care is needed to ensure +that you never read anything in your buffer-processing function that may +have been freed. The best way to ensure this is to use the local resource +allocators, such as \verb+im_open_local()+ and \verb+im_malloc()+. This issue +is discussed at length in the sections below, and in \pref{sec:appl}. + +\verb+im_wrapone()+ is for operations which take exactly one input image. VIPS +provides a second function, \verb+im_wrapmany()+, which works for any number +of input images. The type of \verb+im_wrapmany()+ is slightly different: + +\begin{verbatim} +int +im_wrapmany( IMAGE **in, IMAGE *out, + im_wrapmany_fn fn, void *a, void *b ) +\end{verbatim} + +\noindent + +\begin{verbatim} +void +(*im_wrapmany_fn)( void **in, void *out, + int n, void *a, void *b ) +\end{verbatim} + +\noindent +\verb+im_wrapmany()+ takes a \verb+NULL+-terminated array of input images, +and creates a \verb+NULL+-terminated array of buffers for the use of your +buffer processing function. A function to add two \verb+IM_BANDFMT_UCHAR+ +images to make a \verb+IM_BANDFMT_UCHAR+ image might be written as: + +\begin{verbatim} +static void +add_buffer( unsigned char **in, + unsigned short *out, int n, + IMAGE *in ) +{ + int i; + int sz = n * in->Bands; + unsigned char *p1 = in[0]; + unsigned char *p2 = in[1]; + + for( i = 0; i < sz; i++ ) + out[i] = p1[i] + p2[i]; +} +\end{verbatim} + +This can be made into a PIO function with: + +\begin{verbatim} +int +add_uchar( IMAGE *i1, IMAGE *i2, + IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check parameters. We don't need to + * check that i1 and i2 are the same + * size, im_wrapmany() does that for + * us. + */ + if( i1->BandFmt != IM_BANDFMT_UCHAR || + i1->Coding != IM_CODING_NONE || + i2->BandFmt != IM_BANDFMT_UCHAR || + i2->Coding != IM_CODING_NONE || + i1->Bands != i2->Bands ) { + im_error( "add_uchar", "bad in" ); + return( -1 ); + } + + /* Set fields in output image. As + * input image, but we want a USHORT. + */ + if( im_cp_desc( out, i1 ) ) + return( -1 ); + out->BandFmt = IM_BANDFMT_USHORT; + out->Bbits = IM_BBITS_SHORT; + + /* Process! The first user-parameter + * is the number of bands involved. + * invec is a NULL-terminated array of + * input images. + */ + invec[0] = i1; invec[1] = i2; + invec[2] = NULL; + if( im_wrapmany( invec, out, + (im_wrapone_fn)add_buffer, + i1, NULL ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} + +\subsection{Region descriptors} + +Regions are the next layer of abstraction above image descriptors. A region +is a small part of an image, held in memory ready for processing. A region +is defined as: + +\begin{verbatim} +typedef struct { + Rect valid; + IMAGE *im; + + ... and some other private fields, + ... used by VIPS for housekeeping +} REGION; +\end{verbatim} + +\noindent +where \verb+valid+ holds the sub-area of image \verb+im+ that this region +represents, and \verb+Rect+ is defined as: + +\begin{verbatim} +typedef struct { + int left, top; + int width, height; +} Rect; +\end{verbatim} + +\noindent +two macros are available for \verb+Rect+ calculations: + +\begin{verbatim} +int IM_RECT_RIGHT( Rect *r ) +int IM_RECT_BOTTOM( Rect *r ) +\end{verbatim} + +\noindent +where \verb+IM_RECT_RIGHT()+ returns \verb+left+ + \verb+width+, and +\verb+IM_RECT_BOTTOM()+ returns \verb+top+ + \verb+height+. A small library +of C functions are also available for \verb+Rect+ algebra, see the manual +pages for \verb+im_rect_intersectrect()+. + +Regions are created with \verb+im_region_create()+. This has type: + +\begin{verbatim} +REGION *im_region_create( IMAGE *im ) +\end{verbatim} + +\noindent +\verb+im_region_create()+ returns a pointer to a new region structure, +or \verb+NULL+ on error. Regions returned by \verb+im_region_create()+ +are blank --- they contain no image data and cannot be read from or written +to. See the next couple of sections for calls to fill regions with data. + +Regions are destroyed with \verb+im_region_free()+. It has type: + +\begin{verbatim} +int im_region_free( REGION *reg ) +\end{verbatim} + +\noindent +And, as usual, returns 0 on success and non-zero on error, setting +\verb+im_error()+. You must free all regions you create. If you close +an image without freeing all the regions defined on that image, the image is +just marked for future closure --- it is not actually closed until the final +region is freed. This behaviour helps to prevent dangling pointers, and it +is not difficult to make sure you free all regions --- see the examples below. + +\subsection{Image input with regions} + +Before you can read from a region, you need to call \verb+im_prepare()+ +to fill the region with image data. It has type: + +\begin{verbatim} +int im_prepare( REGION *reg, Rect *r ) +\end{verbatim} + +Area \verb+r+ of the image on which \verb+reg+ has been created is prepared +and attached to the region. + +Exactly what this preparation involves depends upon the image --- it can +vary from simply adjusting some pointers, to triggering the evaluation of a +series of other functions. If it returns successfully, \verb+im_prepare()+ +guarantees that all pixels within \verb+reg->valid+ may be accessed. Note +that this may be smaller or larger than \verb+r+, since \verb+im_prepare()+ +clips \verb+r+ against the size of the image. + +Programs can access image data in the region by calling the macro +\verb+IM_REGION_ADDR()+. It has type + +\begin{verbatim} +char *IM_REGION_ADDR( REGION *reg, + int x, int y ) +\end{verbatim} + +Provided that point (x,y) lies inside \verb+reg->valid+, +\verb+IM_REGION_ADDR()+ returns a pointer to pel $(x,y)$. Adding to the result +of \verb+IM_REGION_ADDR()+ moves to the right along the line of pels, provided +you stay strictly within \verb+reg->valid+. Add \verb+IM_REGION_LSKIP()+ +to move down a line, see below. \verb+IM_REGION_ADDR()+ has some other +useful features --- see the manual page. + +Other macros are available to ease address calculation: + +\begin{verbatim} +int IM_REGION_LSKIP( REGION *reg ) +int IM_REGION_N_ELEMENTS( REGION *reg ) +int IM_REGION_SIZEOF_LINE( REGION *reg ) +\end{verbatim} + +\noindent +These find the number of bytes to add to the result of \verb+IM_REGION_ADDR()+ +to move down a line, the number of band elements across the region and the +number of bytes across the region. + +\fref{fg:paverage} is a version of \verb+average()+ which uses +regions rather than WIO input. Two things: first, we should really be +using \verb+im_iterate()+, see \pref{sec:sequence}, to do the rectangle +algebra for us. Secondly, note that we call \verb+im_pincheck()+ rather +than \verb+im_incheck()+. \verb+im_pincheck()+ signals to the IO system +that you are a PIO-aware function, giving \verb+im_prepare()+ much more +flexibility in the sorts of preparation it can do. Also see the manual +pages for \verb+im_poutcheck()+ and \verb+im_piocheck()+. + +\begin{fig2} +\begin{verbatim} +#include +#include +#include +#include + +int +average( IMAGE *im, double *out ) +{ + int total, i, y; + REGION *reg; + Rect area, *r; + + /* Check im. + */ + if( im_pincheck( im ) ) + return( -1 ); + if( im->BandFmt != IM_BANDFMT_UCHAR || im->Coding != IM_CODING_NONE ) { + im_error( "average", "uncoded uchar images only" ); + return( -1 ); + } + + /* Make a region on im which we can use for reading. + */ + if( !(reg = im_region_create( im )) ) + return( -1 ); +\end{verbatim} +\caption{First PIO average of image} +\label{fg:paverage} +\end{fig2} + +\begin{fig2} +\begin{verbatim} + /* Move area over the image in 100x100 pel chunks. + * im_prepare() will clip against the edges of the image + * for us. + */ + total = 0; + r = ®->valid; + area.width = 100; area.height = 100; + for( area.top = 0; area.top < im->Ysize; area.top += 100 ) + for( area.left = 0; area.left < im->Xsize; + area.left += 100 ) { + /* Fill reg with pels. + */ + if( im_prepare( reg, &area ) ) { + /* We must free the region! + */ + im_region_free( reg ); + return( -1 ); + } + + /* Loop over reg, adding to our total. + */ + for( y = r->top; y < IM_RECT_BOTTOM( r ); y++ ) { + unsigned char *p = IM_REGION_ADDR( reg, r->left, y ); + + for( i = 0; i < IM_REGION_N_ELEMENTS( reg ); i++ ) + total += p[i]; + } + } + + /* Make sure we free the region. + */ + im_region_free( reg ); + + /* Find average. + */ + *out = (double) total / (IM_IMAGE_N_ELEMENTS( im ) * im->Ysize); + + return( 0 ); +} +\end{verbatim} +\caption{First PIO average of image (cont.)} +\end{fig2} + +This version of \verb+average()+ can be called in exactly the same way as +the previous one, but this version has the great advantage of not needing +to have the whole of the input image available at once. + +We can do one better than this --- if the image is being split into small +pieces, we can assign each piece to a separate thread of execution and get +parallelism. To support this splitting of tasks, VIPS has the notion of +a sequence. + +\subsection{Splitting into sequences} +\label{sec:sequence} + +A sequence comes in three parts: a start function, a processing function, +and a stop function. When VIPS starts up a new sequence, it runs the +start function. Start functions return sequence values: a void pointer +representing data local to this sequence. VIPS then repeatedly calls the +processing function, passing in the sequence value and a new piece of image +data for processing. Finally, when processing is complete, VIPS cleans up by +calling the stop function, passing in the sequence value as an argument. The +types look like this: + +\begin{verbatim} +void * +(*start_fn)( IMAGE *out, + void *a, void *b ) +int +(*process_fn)( REGION *reg, + void *seq, void *a, void *b ) +int +(*stop_fn)( void *seq, void *a, void *b ) +\end{verbatim} + +\noindent +The values \verb+a+ and \verb+b+ are carried around by VIPS for your use. + +For functions like \verb+average()+ which consume images but produce no image +output, VIPS provides \verb+im_iterate()+. This has type: + +\begin{verbatim} +int im_iterate( IMAGE *in, + void *(*start_fn)(), + int (*process_fn)(), + int (*stop_fn)(), + void *a, void *b ) +\end{verbatim} + +VIPS starts one or more sequences, runs one or more processing functions +over image \verb+in+ until all of \verb+in+ has been consumed, and then closes +all of the sequences down and returns. VIPS guarantees that the regions +the \verb+process_fn()+ is given will be complete and disjoint, that is, +every pixel in the image will be passed through exactly one sequence. To +make it possible for the sequences to each contribute to the result of the +function in an orderly manner, VIPS also guarantees that all start and stop +functions are mutually exclusive. + +A note on types: \verb++ declares prototypes for +\verb+im_iterate()+ and \verb+im_generate()+ (see \pref{sec:generate}), +but does not give prototypes for the function arguments. This loses a +little type-safety, but gains some convenience. + +An example should make this clearer. This version of \verb+average()+ +is very similar to the average function in the VIPS library --- it is only +missing polymorphism. + +\begin{fig2} +\begin{verbatim} +#include +#include +#include +#include + +/* Start function for average(). We allocate a small piece of + * storage which this sequence will accumulate its total in. Our + * sequence value is just a pointer to this storage area. + * + * The first of the two pointers VIPS carries around for us is a + * pointer to the space where we store the grand total. + */ +static int * +average_start( IMAGE *out ) +{ + int *seq = IM_NEW( out, int ); + + if( !seq ) + return( NULL ); + *seq = 0; + + return( seq ); +} + +/* Stop function for average(). Add the total which has + * accumulated in our sequence value to the grand total for + * the program. + */ +static int +average_stop( int *seq, int *gtotal ) +{ + /* Stop functions are mutually exclusive, so we can write + * to gtotal without clashing with any other stop functions. + */ + *gtotal += *seq; + + return( 0 ); +} +\end{verbatim} +\caption{Final PIO average of image} +\label{fg:p2average} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +/* Process function for average(). Total this region, and + * add that total to the sequence value. + */ +static int +average_process( REGION *reg, int *seq ) +{ + int total, i, y; + Rect *r = ®->valid; + + /* Get the appropriate part of the input image ready. + */ + if( im_prepare( reg, r ) ) + return( -1 ); + + /* Loop over the region. + */ + total = 0; + for( y = r->top; y < IM_RECT_BOTTOM( r ); y++ ) { + unsigned char *p = IM_REGION_ADDR( reg, r->left, y ); + + for( i = 0; i < IM_REGION_N_ELEMENTS( reg ); i++ ) + total += p[i]; + } + + /* Add to the total for this sequence. + */ + *seq += total; + + return( 0 ); +} +\end{verbatim} +\caption{Final PIO average of image (cont.)} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +/* Find average of image. + */ +int +average( IMAGE *im, double *out ) +{ + /* Accumulate grand total here. + */ + int gtotal = 0; + + /* Prepare im for PIO reading. + */ + if( im_pincheck( im ) ) + return( -1 ); + + /* Check it is the sort of thing we can process. + */ + if( im->BandFmt != IM_BANDFMT_UCHAR || + im->Coding != IM_CODING_NONE ) { + im_error( "average", "uncoded uchar images only" ); + return( -1 ); + } + + /* Loop over the image in pieces, and possibly in parallel. + */ + if( im_iterate( im, + average_start, average_process, average_stop, + >otal, NULL ) ) + return( -1 ); + + /* Calculate average. + */ + *out = (double) gtotal / (IM_IMAGE_N_ELEMENTS( im ) * im->Ysize); + + return( 0 ); +} +\end{verbatim} +\caption{Final PIO average of image (cont.)} +\end{fig2} + +There are a couple of variations on \verb+im_prepare()+: you can use +\verb+im_prepare_to()+ to force writing to a particular place, and +\verb+im_prepare_thread()+ to use threaded evaluation. See the man pages. + +\subsection{Output to regions} +\label{sec:generate} + +Regions are written to in just the same way they are read from --- by +writing to a pointer found with the \verb+IM_REGION_ADDR()+ macro. + +\verb+im_iterate()+ does input --- \verb+im_generate()+ does output. It +has the same type as \verb+im_iterate()+: + +\begin{verbatim} +int +im_generate( IMAGE *out, + void *(*start_fn)(), + int (*process_fn)(), + int (*stop_fn)(), + void *a, void *b ) +\end{verbatim} + +The region given to the process function is ready for output. Each time +the process function is called, it should fill in the pels in the region +it was given. Note that, unlike \verb+im_iterate()+, the areas the process +function is asked to produce are not guaranteed to be either disjoint or +complete. Again, VIPS may start up many process functions if it sees fit. + +Here is \verb+invert()+, rewritten to use PIO. This piece of code makes use +of a pair of standard start and stop functions provided by the VIPS library: +\verb+im_start_one()+ and \verb+im_stop_one()+. They assume that the first +of the two user arguments to \verb+im_generate()+ is the input image. They are +defined as: + +\begin{verbatim} +REGION * +im_start_one( IMAGE *out, IMAGE *in ) +{ + return( im_region_create( in ) ); +} +\end{verbatim} + +\noindent +and: + +\begin{verbatim} +int +im_stop_one( REGION *seq ) +{ + return( im_region_free( seq ) ); +} +\end{verbatim} + +They are useful for simple functions which expect only one input +image. See the manual page for \verb+im_start_many()+ for many-input +functions. + +\begin{fig2} +\begin{verbatim} +#include +#include +#include +#include + +/* Process function for invert(). Build the pixels in or + * from the appropriate pixels in ir. + */ +static int +invert_process( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + int i, y; + + /* Ask for the part of ir we need to make or. In this + * case, the two areas will be the same. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* Loop over or writing pels calculated from ir. + */ + for( y = r->top; y < IM_RECT_BOTTOM( r ); y++ ) { + unsigned char *p = IM_REGION_ADDR( ir, r->left, y ); + unsigned char *q = IM_REGION_ADDR( or, r->left, y ); + + for( i = 0; i < IM_REGION_N_ELEMENTS( or ); i++ ) + q[i] = 255 - p[i]; + } + + /* Success! + */ + return( 0 ); +} +\end{verbatim} +\caption{PIO invert} +\label{fg:p2invert} +\end{fig2} + +\begin{fig2} +\begin{verbatim} +/* Invert an image. + */ +int +invert( IMAGE *in, IMAGE *out ) +{ + /* Check descriptors for PIO compatibility. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Check input image for compatibility with us. + */ + if( in->BandFmt != IM_BANDFMT_UCHAR || in->Coding != IM_CODING_NONE ) { + im_error( "invert", "uncoded uchar images only" ); + return( -1 ); + } + + /* out inherits from in, as before. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Set demand hints for out. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Build out in pieces, and possibly in parallel! + */ + if( im_generate( out, + im_start_one, invert_process, im_stop_one, + in, NULL ) ) + return( -1 ); + + return( 0 ); +} +\end{verbatim} +\caption{PIO invert (cont.)} +\end{fig2} + +Functions have some choice about the way they write their output. Usually, they +should just write to the region they were given by \verb+im_generate()+. They +can, if they wish, set up the region for output to some other place. See +the manual page for \verb+im_region_region()+. See also the source for +\verb+im_copy()+ and \verb+im_extract()+ for examples of these tricks. + +Note also the call to \verb+im_demand_hint()+. This function hints to the IO +system, suggesting the sorts of shapes of region this function is happiest +with. VIPS supports four basic shapes --- choosing the correct shape can +have a dramatic effect on the speed of your function. See the man page for +full details. + +\subsection{Callbacks} +\label{sec:callback} + +VIPS lets you attach callbacks to image descriptors. These are functions +you provide that VIPS will call when certain events occur. This release +of VIPS supports three callbacks: + +\subsubsection{Close callbacks} + +These callbacks are invoked just before an image is closed. They are useful +for freeing objects which are associated with the image. All callbacks are +triggered in the reverse order to the order in which they were attached. This +is sometimes important when freeing objects which contain pointers to +other objects. + +Use \verb+im_add_close_callback()+ to add a close callback: + +\begin{verbatim} +int +im_add_close_callback( IMAGE *im, + int (*callback)(), + void *a, void *b ) +\end{verbatim} + +\noindent +where \verb+callback()+ is a function supplied by you which has type: + +\begin{verbatim} +int callback( void *a, void *b ) +\end{verbatim} + +\noindent +and which returns 0 on success, and -1 on error, setting +\verb+im_error()+. As with \verb+im_generate()+, the values \verb+a+ +and \verb+b+ are carried around for you by VIPS, and may be used as your +function sees fit. + +\subsubsection{Eval callbacks} + +These are callbacks which are invoked by VIPS during evaluation. They +are called each time a region is filled with data (for PIO functions), or +each time \verb+im_writeline()+ is called (for WIO functions). The callback +has access to a large struct containing information about the progress of +evaluation, useful for user-interfaces built on top of VIPS. See the manual +page for \verb+im_add_eval_callback()+ for full details. + +\subsubsection{Evalend callbacks} + +These are triggered after completion of evaluation of an image, just after all +stop functions have been called. Evalend callbacks are useful for summarising +the results of a computation, see the example in the section below. + +\subsection{Memory allocation revisited} + +When you are using PIO, memory allocation becomes rather more complicated than +it was before. There are essentially two types of memory which your function +might want to use for working space: memory which is associated with each +instance of your function (remember that two copies of you function may be +joined together in a pipeline and be running at the same time --- you can't +just use global variables), and memory which is local to each sequence +which VIPS starts on your argument image. + +The first type, memory local to this function instance, typically holds +copies of any parameters passed to your image processing function, and links +to any read-only tables used by sequences which you run over the image. This +should be allocated in your main function. + +The second type of memory, memory local to a sequence, should be allocated +in a start function. Because this space is private to a sequence, it may be +written to. Start and stop functions are guaranteed +to be single-threaded, so you may write to the function-local memory within +them. + diff --git a/doc/src/refintro.tex b/doc/src/refintro.tex new file mode 100644 index 00000000..30290c05 --- /dev/null +++ b/doc/src/refintro.tex @@ -0,0 +1,105 @@ +\section{Introduction} +\mylabel{sec:ref} + +This document introduces the functions available in the VIPS image +processing library. For detailed information on particular functions, +refer to the UNIX on-line manual pages. Enter (for example): + +\begin{verbatim} +example% man im_abs +\end{verbatim} + +for information on the function \verb+im_abs()+. + +All the comand-line vips operations will print help text too. For example: + +\begin{verbatim} +example% vips im_extract +usage: vips im_extract input output + left top width height band +where: + input is of type "image" + output is of type "image" + left is of type "integer" + top is of type "integer" + width is of type "integer" + height is of type "integer" + band is of type "integer" +extract area/band, from package + "conversion" +flags: (PIO function) + (coordinate transformer) + (area operation) + (result can be cached) +vips: error calling function +im_run_command: too few arguments +\end{verbatim} + +Once you have found a function you need to use, you can call it from a C +program (see \pref{sec:appl}), you can call +it from C++ or Python (see \pref{sec:cpp}), you can call +it from the \nip{} ((see the \emph{nip Manual}), or SIAM graphical +user-interfaces, or you can run it from the UNIX command line with the +\vips{} program. For example: + +\begin{verbatim} +john% vips im_vips2tiff cam.v t1.tif none +john% vips im_tiff2vips t1.tif t2.v.v 0 +john% vips im_equal cam.v t2.v t3.v +john% vips im_min t3.v +255 +\end{verbatim} + +VIPS may have been set up at your site with a set of links which call the +vips program for you. You may also be able to type: + +\begin{verbatim} +john% im_vips2tiff cam.v t1.tif none +john% im_tiff2vips t1.tif t2.v.v 0 +john% im_equal cam.v t2.v t3.v +john% im_min t3.v +\end{verbatim} + +There are a few VIPS programs which you cannot run with \vips{}, either +because their arguments are a very strange, or because they are complete +mini-applications (like \verb+vips2dj+). These programs are listed in +table~\ref{tb:nondb}, see the man pages for full details. + +\begin{tab2} +\centerline{ +\begin{tabular}{|l|l|} +\hline +Name & Description \\ +\hline +\texttt{binfile} & Read RAW image \\ +\texttt{debugim} & Print an image pixel by pixel \\ +\texttt{edvips} & Change fields in a VIPS header \\ +\texttt{header} & Print fields from a VIPS header \\ +\texttt{printlines} & Print an image a line at a time \\ +\texttt{vips} & VIPS universal main program \\ +\texttt{vips-7.12} & VIPS wrapper script \\ +\texttt{find\_mosaic} & Analyse a set of images for overlaps \\ +\texttt{mergeup} & Join a set of images together \\ +\texttt{cooc\_features} & Calculate features of a co-occurence matrix \\ +\texttt{cooc} & Calculate a co-occurence matrix \\ +\texttt{glds\_features} & Calculate features of a grey-level + distribution matrix \\ +\texttt{glds} & Calculate a grey-level distribution matrix \\ +\texttt{simcontr} & Demonstrate simultaneous contrast \\ +\texttt{sines} & Generate a sinusoidal test pattern \\ +\texttt{spatres} & Generate a spatial resolution test pattern \\ +\texttt{squares} & Generate some squares \\ +\texttt{batch\_crop} & Crop a lot of images \\ +\texttt{batch\_image\_convert} & File format convert a lot of images \\ +\texttt{batch\_rubber\_sheet} & Warp a lot of images \\ +\texttt{light\_correct} & Correct a set of images for shading errors \\ +\texttt{mitsub} & Format a VIPS image for output to a Mitsubishi 3600 \\ +\texttt{shrink\_width} & Shrink to a specific width \\ +\texttt{vdump} & VIPS to mono Postscript \\ +\texttt{vips2dj} & VIPS to high-quality colour Postscript \\ +\hline +\end{tabular} +} +\caption{Miscellaneous programs} +\label{tb:nondb} +\end{tab2} diff --git a/doc/src/vdisplay.tex b/doc/src/vdisplay.tex new file mode 100644 index 00000000..7c5080dc --- /dev/null +++ b/doc/src/vdisplay.tex @@ -0,0 +1,75 @@ +\section{The \texttt{VDisplay} class} + +The \verb+VDisplay+ class is an abstraction over the VIPS \verb+im_col_display+ +type which gives convenient and safe representation of VIPS display profiles. + +VIPS display profiles are now obsolete. You're better off using the +ICC colour management \verb+VImage+ member functions \verb+ICC_export()+ and +\verb+ICC_import()+. + +\subsection{Constructors} + +There are two constructors for \verb+VDisplay+: + +\begin{verbatim} +VDisplay( const char *name ); +VDisplay(); +\end{verbatim} + +The first form initialises the display from one of the standard VIPS display +types. For example: + +\begin{verbatim} +VDisplay fred( "sRGB" ); +VDisplay jim( "ultra2-20/2/98" ); +\end{verbatim} + +Makes \verb+fred+ a profile for making images in sRGB format, and \verb+jim+ a +profile representing my workstation display, as of 20/2/98. The second form +of constructor makes an uninitialised display. + +\subsection{Projection functions} + +A set of member functions of \verb+VDisplay+ provide read and write access to +the fields in the display. + +\begin{verbatim} +char *name(); +VDisplayType &type(); +matrix &mat(); +float &YCW(); +float &xCW(); +float &yCW(); +float &YCR(); +float &YCG(); +float &YCB(); +int &Vrwr(); +int &Vrwg(); +int &Vrwb(); +float &Y0R(); +float &Y0G(); +float &Y0B(); +float &gammaR(); +float &gammaG(); +float &gammaB(); +float &B(); +float &P(); +\end{verbatim} + +Where \verb+VDisplayType+ is defined as: + +\begin{verbatim} +enum VDisplayType { + BARCO, + DUMB +}; +\end{verbatim} + +And \verb+matrix+ is defined as: + +\begin{verbatim} +typedef float matrix[3][3]; +\end{verbatim} + +For a description of all the fields in a VIPS display profile, see the manual +page for \verb+im_XYZ2RGB()+. diff --git a/doc/src/verror.tex b/doc/src/verror.tex new file mode 100644 index 00000000..5680e43e --- /dev/null +++ b/doc/src/verror.tex @@ -0,0 +1,76 @@ +\section{The \texttt{VError} class} + +The \verb+VError+ class is the class thrown by the VIPS C++ API when an +error is detected. It is derived from \verb+std::exception+ in the usual way. + +\subsection{Constructors} + +There are two constructors for \verb+VError+: + +\begin{verbatim} +VError( std::string str ); +VError(); +\end{verbatim} + +The first form creates an error object initialised with the specified +string, the last form creates an empty error object. + +\subsection{Projection functions} + +A function gives access to the string held by \verb+VError+: + +\begin{verbatim} +const char *what(); +\end{verbatim} + +You can also send to an \verb+ostream+. + +\begin{verbatim} +std::ostream& operator<<( + std::ostream&, const error& ); +\end{verbatim} + +\subsection{Computing with \texttt{VError}} + +Two member functions let you append elements to an error: + +\begin{verbatim} +VError &app( std::string txt ); +VError &app( const int i ); +\end{verbatim} + +For example: + +\begin{verbatim} +VError wombat; +int n = 12; + +wombat.app( "possum: no more than " ). + app( n ).app( " elements\n" ); +throw( wombat ); +\end{verbatim} + +\noindent +will throw a \verb+VError+ with a diagnostic of: + +\begin{verbatim} +possum: no more than 12 elements +\end{verbatim} + +The member function \verb+perror()+ prints the error message to \verb+stdout+ +and exits with a code of 1. + +\begin{verbatim} +void perror( const char * ); +void perror(); +\end{verbatim} + +\subsection{Convenience function} + +The convenience function \verb+verror+ creates an \verb+VError+ with the +specified error string, and throws it. If you pass \verb+""+ for the string, +verror uses the contents of the VIPS error buffer instead. + +\begin{verbatim} +extern void verror( std::string str = "" ); +\end{verbatim} diff --git a/doc/src/vimage.tex b/doc/src/vimage.tex new file mode 100644 index 00000000..c897725f --- /dev/null +++ b/doc/src/vimage.tex @@ -0,0 +1,279 @@ +\section{The \texttt{VImage} class} + +The \verb+VImage+ class is a layer over the VIPS \verb+IMAGE+ type. It +automates almost all of the image creation and destruction issues that +complicate the C API, it automates error handling, and it provides a +convenient system for composing operations. + +\subsection{Constructors} + +There are two principal constructors for \verb+VImage+: + +\begin{verbatim} +VImage::VImage( const char *name, + const char *mode = "r" ); +VImage::VImage(); +\end{verbatim} + +The first form creates a new \verb+VImage+, linking it to the named file. +\verb+mode+ sets the mode for the file: it can take the following values: + +\begin{description} + +\item[\texttt{"r"}] +The named image file is opened read-only. This is the default mode. + +\item[\texttt{"w"}] +A \verb+VImage+ is created which, when written to, will write pixels to disc +in the specified file. + +\item[\texttt{"t"}] +As the \verb'"w"' mode, but pixels written to the \verb+VImage+ will be saved +in a temporary memory buffer. + +\item[\texttt{"p"}] +This creates a special `partial' image. Partial images represent +intermediate results, and are used to join VIPS operations together, +see~\pref{sec:compute}. + +\item[\texttt{"rw"}] +As the \verb'"r"' mode, but the image is mapped into your address space +read-write. This mode is only provided for the use of paintbox-style +applications, which need to directly modify an image. See \pref{sec:inplace}. + +\end{description} + +The second form of constructor is shorthand for: + +\begin{verbatim} +VImage( "VImage:1", "p" ) +\end{verbatim} + +\noindent +It is used for representing intermediate results of computations. + +Two further constructors are handy for wrapping \verb+VImage+ around existing +images. + +\begin{verbatim} +VImage( void *buffer, + int width, int height, int bands, + TBandFmt format ); +VImage( void *image ); +\end{verbatim} + +\noindent +The first constructor makes a \verb+VImage+ from an area of memory (perhaps +from another image processing system), and the second makes a \verb+VImage+ +from an \verb+IMAGE+. + +In both these two cases, the VIPS C++ API does not assume responsibility +for the resouces: it's up to you to make sure the buffer is freed. + +\subsection{File conversion} + +VIPS can read and write a number of different file formats. Information about +file format conversion is taken from the filename. For example: + +\begin{verbatim} +VImage jim( "fred.jpg" ); +\end{verbatim} + +\noindent +This will decompress the file \verb+fred.jpg+ to a memory buffer, wrap a VIPS +image around the buffer and build a reference to it called \verb+jim+. + +Options are passed to the file format converters embedded in the filename. For +example: + +\begin{verbatim} +VImage out( "jen.tif:deflate", "w" ); +\end{verbatim} + +\noindent + +\noindent +Writing to the descriptor \verb+out+ will cause a TIFF image to be written to +disc with deflate compression. + +See the manual page for \verb+im_open(3)+ for details of all the file formats +and conversions available. + +\subsection{Projection functions} + +A set of member functions of \verb+VImage+ provide access to the fields in +the header: + +\begin{verbatim} +int Xsize(); +int Ysize(); +int Bands(); +TBandFmt BandFmt(); +TCoding Coding(); +TType Type(); +float Xres(); +float Yres(); +int Length(); +TCompression Compression(); +short Level(); +int Xoffset(); +int Yoffset(); +\end{verbatim} + +\noindent +Where \verb+TBandFmt+, \verb+TCoding+, \verb+TType+ and \verb+TCompression+ +are \verb+enum+s for the types in the VIPS file header. See section~\pref{sec:header} for an explanation of all of these fields. + +The \verb+image()+ member function provides access to the \verb+IMAGE+ +descriptor underlying the C++ API. See the \pref{sec:appl} for details. + +\begin{verbatim} +void *image(); +\end{verbatim} + +The \verb+data()+ member function returns a pointer to an array of pixel data +for the image. + +\begin{verbatim} +void *data() const; +\end{verbatim} + +\noindent +This can be very slow and use huge amounts of RAM. + +Finally, two projection functions give access to the filename and history +fields maintained by the VIPS IO system. + +\begin{verbatim} +char *filename(); +char *Hist(); +\end{verbatim} + +\subsection{Assignment} + +\verb+VImage+ defines copy and assignment, with reference-counted +pointer-style semantics. For example, if you write: + +\begin{verbatim} +VImage fred( "fred.v" ); +VImage jim( "jim.v" ); + +fred = jim; +\end{verbatim} + +This will automatically close the file \verb+fred.v+, and make the variable +\verb+fred+ point to the image \verb+jim.v+ instead. Both \verb+jim+ and +\verb+fred+ now point to the same underlying image object. + +Internally, a \verb+VImage+ object is just a pointer to a reference-counting +block, which in turn holds a pointer to the underlying VIPS \verb+IMAGE+ type. +You can therefore efficiently pass \verb+VImage+ objects to functions by +value, and return \verb+VImage+ objects as function results. + +\subsection{Computing with \texttt{VImage}s} +\label{sec:compute} + +All VIPS image processing operations are member functions of the \verb+VImage+ +class. For example: + +\begin{verbatim} +VImage fred( "fred.v" ); +VImage jim( "jim.v" ); + +VImage result = fred.cos() + jim; +\end{verbatim} + +Will apply \verb+im_costra()+ to \verb+fred.v+, making an image where each +pixel is the cosine of the corresponding pixel in \verb+fred.v+; then add that +image to \verb+jim.v+. Finally, the result will be held in \verb+result+. + +VIPS is a demand-driven image processing system: when it computes expressions +like this, no actual pixels are calculated (although you can use the +projection functions on images --- \verb+result.BandFmt()+ for example). When +you finally write the result to a file (or use some operation that needs pixel +values, such as \verb+min()+, find minimum value), VIPS evaluates all of the +operations you have called to that point in parallel. If you have more than one +CPU in your machine, the load is spread over the available processors. This +means that there is no limit to the size of the images you can process. + +\pref{sec:packages} lists all of the VIPS packages. These general +rules apply: + +\begin{itemize} + +\item +VIPS operation names become C++ member function names by dropping the +\verb+im_+ prefix, and if present, the \verb+tra+ postfix, the \verb+const+ +postfix and the \verb+_vec+ postfix. For example, the +VIPS operation \verb+im_extract()+ becomes \verb+extract()+, and +\verb+im_costra()+ becomes \verb+cos()+. + +\item +The \verb+VImage+ object to which you apply the member function is the first +input image, the member function returns the first output. If there is no +image input, the member is declared \verb+static+. + +\item +\verb+INTMASK+ and \verb+DOUBLEMASK+ types become \verb+VMask+ objects, +\verb+im_col_display+ types become \verb+VDisplay+ objects. + +\item +Several C API functions can map to the same C++ API member. For example, +\verb+im_andimage+, \verb+im_andimage_vec+ and \verb+im_andimageconst+ all map +to the member \verb+andimage+. The API relies on overloading to +discriminate between these functions. + +\end{itemize} + +This part of the C++ API is generated automatically from the VIPS function +database, so it should all be up-to-date. + +There are a set of arithmetic operators defined for your convenience. You can +generally write any arithmetic expression and include \verb+VImage+ in there. + +\begin{verbatim} +VImage fred( "fred.v" ); +VImage jim( "jim.v" ); + +Vimage v = int((fred + jim) / 2); +\end{verbatim} + +\subsection{Writing results} + +Once you have computed some result, you can write it to a file with the member +\verb+write()+. It takes the following forms: + +\begin{verbatim} +VImage write( const char *name ); +VImage write( VImage out ); +VImage write(); +\end{verbatim} + +The first form simply writes the image to the named file. The second form +writes the image to the specified \verb+VImage+ object, for example: + +\begin{verbatim} +VImage fred( "fred.v" ); +VImage jim( "jim buffer", "t" ); + +Vimage v = (fred + 42).write( jim ); +\end{verbatim} + +\noindent +This creates a temporary memory buffer called \verb+jim+, and fills it with +the result of adding 42 to every pixel in \verb+fred.v+. + +The final form of \verb+write()+ writes the image to a memory buffer, and +returns that. + +\subsection{Type conversions} + +Two type conversions are defined: you can cast \verb+VImage+ to a +\verb+VDMask+ and to a \verb+VIMask+. + +\begin{verbatim} +operator VDMask(); +operator VIMask(); +\end{verbatim} + +These operations are slow and need a lot of memory! Emergencies only. diff --git a/doc/src/vipsmanual.tex b/doc/src/vipsmanual.tex new file mode 100644 index 00000000..e1f5f78f --- /dev/null +++ b/doc/src/vipsmanual.tex @@ -0,0 +1,88 @@ +\documentclass[a4paper,twocolumn,dvips]{book} +\usepackage[dvips=false,pdftex=false,vtex=false]{geometry} +\usepackage{ifpdf} +\ifpdf + \usepackage[pdftex]{graphicx,color} +\else + \usepackage{graphicx,color} +\fi +\usepackage{times} +\usepackage{fancyhdr} +\usepackage{ifthen} + +\input{mydefs} + +\fancyhead{} % clear all fields +\fancyhead[LE,RO]{\leftmark} % left-even, right-odd +\fancyhead[RE,LO]{VIPS Manual} % right-even, left-odd +\fancyfoot[LE,RO]{\thepage} % left-even, right-odd +\fancyfoot[RE,LO]{January 2007} + +\begin{document} + +\pagenumbering{roman} + +\begin{titlepage} +\thispagestyle{empty} +\begin{center} +\huge +VIPS Manual\\ +\large Version 7.12\\ +\vspace{0.5in} +\large +John Cupitt, +Kirk Martinez\\ +\end{center} + +% hmm ... must be a better way to get the quote at the bottom of the page +\vspace{6in} + +This manual formatted \today +\setcounter{page}{1} +\end{titlepage} + +% \blankpage +\tableofcontents +\thispagestyle{plain} + +% \blankpage +\listoffigures +\thispagestyle{plain} + +% \blankpage +\listoftables +\thispagestyle{plain} + +% \blankpage +\pagenumbering{arabic} +\thispagestyle{plain} +\cfoot{} + +\chapter{VIPS from C++ and Python} + +\input{cppintro} +\input{fileformat} +\input{vimage} +\input{vmask} +\input{vdisplay} +\input{verror} + +\chapter{VIPS for C programmers} + +\input{applintro} +\input{iosys} +\input{func} + +\chapter{Writing VIPS operations} + +\input{operintro} +\input{wio} +\input{pio} +\input{ipio} + +\chapter{VIPS reference} + +\input{refintro} +\input{packages} + +\end{document} diff --git a/doc/src/vmask.tex b/doc/src/vmask.tex new file mode 100644 index 00000000..45817e7f --- /dev/null +++ b/doc/src/vmask.tex @@ -0,0 +1,151 @@ +\section{The \texttt{VMask} class} + +The \verb+VMask+ class is an abstraction over the VIPS \verb+DOUBLEMASK+ and +\verb+INTMASK+ types which gives convenient and safe representation of +matricies. + +\verb+VMask+ has two sub-classes, \verb+VIMask+ and \verb+VDMask+. These +represent matricies of integers and doubles respectively. + +\subsection{Constructors} + +There are three constructors for \verb+VIMask+ and \verb+VDMask+: + +\begin{verbatim} +VIMask( int xsize, int ysize ); +VIMask( int xsize, int ysize, + int scale, int offset, ... ); +VIMask( const char *name ); +VIMask(); +VDMask( int xsize, int ysize ); +VDMask( int xsize, int ysize, + double scale, double offset, ... ); +VDMask( const char *name ); +VDMask(); +\end{verbatim} + +The first form creates an empty matrix, with the specified dimensions; +the second form initialises a matrix from a varargs list; the third form +reads the matrix from the named file. The final form makes a mask object +with no contents yet. + +\subsection{Projection functions} + +A set of member functions of \verb+VIMask+ provide access to the fields in +the matrix: + +\begin{verbatim} +int xsize() const; +int ysize() const; +int scale() const; +int offset() const; +const char *filename() const; +\end{verbatim} + +\verb+VDMask+ is the same, except that the \verb+scale()+ and \verb+offset()+ +members return \verb+double+. \verb+VMask+ allows all operations that are +common to \verb+VIMask+ and \verb+VDMask+. + +\subsection{Assignment} + +\verb+VMask+ defines copy and assignment with pointer-style +semantics. You can write stuff like: + +\begin{verbatim} +VIMask fred( "mask" ); +VMask jim; + +jim = fred; +\end{verbatim} + +This reads the file \verb+mask+, noting a pointer to the mask in \verb+fred+. +It then makes \verb+jim+ also point to it, so \verb+jim+ and \verb+fred+ are +sharing the same underlying matrix values. + +Internally, a \verb+VMask+ object is just a pointer to a reference-counting +block, which in turn holds a pointer to the underlying VIPS \verb+MASK+ type. +You can therefore efficiently pass \verb+VMask+ objects to functions by +value, and return \verb+VMask+ objects as function results. + +\subsection{Computing with \texttt{VMask}} + +You can use \verb+[]+ to get at matrix elements, numbered left-to-right, +top-to-bottom. Alternatively, use \verb+()+ to address elements by $x,y$ +position. For example: + +\begin{verbatim} +VIMask fred( "mask" ); + +for( int i = 0; i < fred.xsize(); i++ ) + fred[i] = 12; +\end{verbatim} + +\noindent +will set the first line of the matrix to 12, and: + +\begin{verbatim} +VDMask fred( "mask" ); + +for( int x = 0; x < fred.xsize(); x++ ) + fred(x, x) = 12.0; +\end{verbatim} + +\noindent +will set the leading diagonal to 12. + +See the member functions below for other operations on \verb+VMask+. + +\subsection{\texttt{VIMask} operations} + +The following operations are defined for \verb+VIMask+: + +\begin{verbatim} +// Cast to VDMask and VImage +operator VDMask(); +operator VImage(); + +// Build gaussian and log masks +static VIMask gauss( double, double ); +static VIMask log( double, double ); + +// Rotate +VIMask rotate45(); +VIMask rotate90(); + +// Transpose, invert, join and multiply +VDMask trn() ; +VDMask inv(); +VDMask cat( VDMask ); +VDMask mul( VDMask ); +\end{verbatim} + +\subsection{\texttt{VDMask} operations} + +The following operations are defined for \verb+VDMask+: + +\begin{verbatim} +// Cast to VIMask and VImage +operator VIMask(); +operator VImage(); + +// Build gauss and log masks +static VDMask gauss( double, double ); +static VDMask log( double, double ); + +// Rotate +VDMask rotate45(); +VDMask rotate90(); + +// Scale to intmask +VIMask scalei(); + +// Transpose, invert, join and multiply +VDMask trn(); +VDMask inv(); +VDMask cat( VDMask ); +VDMask mul( VDMask ); +\end{verbatim} + +\subsection{Output of masks} + +You can output masks with the usual \verb+<<+ operator. diff --git a/doc/src/wio.tex b/doc/src/wio.tex new file mode 100644 index 00000000..6f8eb278 --- /dev/null +++ b/doc/src/wio.tex @@ -0,0 +1,363 @@ +\section{Programming WIO operations} + +WIO is the style for you if you want ease of programming, or if your +algorithm must have the whole of the input image available at the same +time. For example, a Fourier transform operation is unable to produce any +output until it has seen the whole of the input image. + +\subsection{Input from an image} + +In WIO input, the whole of the image data is made available to the program +via the \verb+data+ field of the descriptor. To make an image ready for reading +in this style, programs should call \verb+im_incheck()+: + +\begin{verbatim} +int im_incheck( IMAGE *im ) +\end{verbatim} + +\noindent +If it succeeds, it returns 0, if it fails, it returns non-zero and +sets \verb+im_error()+. On success, VIPS guarantees that all of the +user-accessible fields in the descriptor contain valid data, and that all +of the image data may be read by simply reading from the \verb+data+ field +(see below for an example). This will only work for images less than about +2GB in size. + +VIPS has some simple macros to help address calculations on images: + +\begin{verbatim} +int IM_IMAGE_SIZEOF_ELEMENT( IMAGE * ) +int IM_IMAGE_SIZEOF_PEL( IMAGE * ) +int IM_IMAGE_SIZEOF_LINE( IMAGE * ) +int IM_IMAGE_N_ELEMENTS( IMAGE * ) +char *IM_IMAGE_ADDR( IMAGE *, + int x, int y ) +\end{verbatim} + +\noindent +These macros calculate \verb+sizeof()+ a band element, a pel and a horizontal +line of pels. \verb+IM_IMAGE_N_ELEMENTS+ returns the number of band elements +across an image. \verb+IM_IMAGE_ADDR+ calculates the address of a pixel in an +image. If \verb+DEBUG+ is defined, it does bounds checking too. + +\begin{fig2} +\begin{verbatim} +#include +#include + +#include + +int +average( IMAGE *im, double *out ) +{ + int x, y; + long total; + + /* Prepare for reading. + */ + if( im_incheck( im ) ) + return( -1 ); + + /* Check that this is the kind of image we can process. + */ + if( im->BandFmt != IM_BANDFMT_UCHAR || + im->Coding != IM_CODING_NONE ) { + im_error( "average", "uncoded uchar images only" ); + return( -1 ); + } + + /* Loop over the image, summing pixels. + */ + total = 0; + for( y = 0; y < im->Ysize; y++ ) { + unsigned char *p = (unsigned char *) IM_IMAGE_ADDR( im, 0, y ); + + for( x = 0; x < IM_IMAGE_N_ELEMENTS( im ); x++ ) + total += p[x]; + } + + /* Calculate average. + */ + *out = (double) total / + (IM_IMAGE_N_ELEMENTS( im ) * im->Ysize)); + + /* Success! + */ + return( 0 ); +} +\end{verbatim} +\caption{Find average of image} +\label{fg:average} +\end{fig2} + +\fref{fg:average} is a simple WIO operation which calculates the +average of an unsigned char image. It will work for any size image, with any +number of bands. See~\pref{sec:poly} for techniques for making operations +which will work for any image type. This operation might be called from an +application with: + +\begin{verbatim} +#include +#include + +#include + +void +find_average( char *name ) +{ + IMAGE *im; + double avg; + + if( !(im = im_open( name, "r" )) || + average( im, &avg ) || + im_close( im ) ) + error_exit( "failure!" ); + + printf( "Average of \"%s\" is %G\n", + name, avg ); +} +\end{verbatim} + +\noindent +When you write an image processing operation, you can test it by writing +a VIPS function descriptor and calling it from the \vips{} universal +main program, or from the \nip{} interface. See \pref{sec:appl}. + +\subsection{Output to an image} + +Before attempting WIO output, programs should call \verb+im_outcheck()+. It +has type: + +\begin{verbatim} +int im_outcheck( IMAGE *im ) +\end{verbatim} + +\noindent +If \verb+im_outcheck()+ succeeds, VIPS guarantees that WIO output is sensible. + +Programs should then set fields in the output descriptor to describe +the sort of image they wish to write (size, type, and so on) and call +\verb+im_setupout()+. It has type: + +\begin{verbatim} +int im_setupout( IMAGE *im ) +\end{verbatim} + +\noindent +\verb+im_setupout()+ creates the output file or memory buffer, using the +size and type fields that were filled in by the program between the calls to +\verb+im_outcheck()+ and \verb+im_setupout()+, and gets it ready for writing. + +Pels are written with \verb+im_writeline()+. This takes a y position (pel +(0,0) is in the top-left-hand corner of the image), a descriptor and a +pointer to a line of pels. It has type: + +\begin{verbatim} +int im_writeline( int y, + IMAGE *im, unsigned char *pels ) +\end{verbatim} + +Two convenience functions are available to make this process slightly +easier. \verb+im_iocheck()+ is useful for programs which take one input +image and produce one image output. It simply calls \verb+im_incheck()+ +and \verb+im_outcheck()+. It has type: + +\begin{verbatim} +int im_iocheck( IMAGE *in, IMAGE *out ) +\end{verbatim} + +The second convenience function copies the fields describing size, type, +metadata and history from one image descriptor to another. It is useful when +the output +image will be similar in size and type to the input image. It has type: + +\begin{verbatim} +int im_cp_desc( IMAGE *out, IMAGE *in ) +\end{verbatim} + +\noindent +There's also \verb+im_cp_descv()+, see the man page. + +\begin{fig2} +\begin{verbatim} +#include +#include + +#include +#include + +int +invert( IMAGE *in, IMAGE *out ) +{ + int x, y; + unsigned char *buffer; + + /* Check images. + */ + if( im_iocheck( in, out ) ) + return( -1 ); + if( in->BandFmt != IM_BANDFMT_UCHAR || in->Coding != IM_CODING_NONE ) { + im_error( "invert", "uncoded uchar images only" ); + return( -1 ); + } + + /* Make output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Allocate a line buffer and make sure it will be freed correctly. + */ + if( !(buffer = IM_ARRAY( out, + IM_IMAGE_SIZEOF_LINE( in ), unsigned char )) ) + return( -1 ); + + /* Loop over the image! + */ + for( y = 0; y < in->Ysize; y++ ) { + unsigned char *p = (unsigned char *) IM_IMAGE_ADDR( in, 0, y ); + + for( x = 0; x < IM_IMAGE_N_ELEMENTS( in ); x++ ) + buffer[x] = 255 - p[x]; + if( im_writeline( y, out, buffer ) ) + return( -1 ); + } + + return( 0 ); +} +\end{verbatim} +\caption{Invert an image} +\label{fg:invert} +\end{fig2} + +\fref{fg:invert} is a WIO VIPS operation which finds the photographic +negative of an unsigned char image. See \pref{sec:malloc} for an explanation +of \verb+IM_ARRAY+. This operation might be called from an +application with: + +\begin{verbatim} +#include +#include + +#include + +void +find_negative( char *inn, char *outn ) +{ + IMAGE *in, *out; + + if( !(in = im_open( inn, "r" )) || + !(out = im_open( outn, "w" )) || + invert( in, out ) || + im_updatehist( out, "invert" ) || + im_close( in ) || + im_close( out ) ) + error_exit( "failure!" ); +} +\end{verbatim} + +See \pref{sec:history} for an explanation of the call to \verb+im_updatehist()+. + +\subsection{Polymorphism} +\label{sec:poly} + +Most image processing operations in the VIPS library can operate on +images of any type (\verb+IM_BANDFMT_UCHAR+, as in our examples above, +also \verb+IM_BANDFMT_UINT+ etc.). This is usually implemented with code +replication: the operation contains loops for processing every kind of image, +and when called, invokes the appropriate loop for the image it is given. + +As an example, figure~\ref{fg:exp} calculates \verb+exp()+ for every pel +in an image. If the input image is \verb+double+, we write \verb+double+ +output. If it is any other non-complex type, we write \verb+float+. If it +is complex, we flag an error (\verb+exp()+ of a complex number is fiddly). +The example uses an image type predicate, \verb+im_iscomplex()+. There are +a number of these predicate functions, see the manual page. + +\begin{fig2} +\begin{verbatim} +#include +#include +#include + +#include +#include + +/* Exponential transform. + */ +int +exptra( IMAGE *in, IMAGE *out ) +{ + int x, y; + unsigned char *buffer; + + /* Check descriptors. + */ + if( im_iocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_error( "exptra", "uncoded non-complex only" ); + return( -1 ); + } + + /* Make output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( in->BandFmt != IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_FLOAT; + if( im_setupout( out ) ) + return( -1 ); +\end{verbatim} +\caption{Calculate \texttt{exp()} for an image} +\label{fg:exp} +\end{fig2} + +\begin{fig2} +\begin{verbatim} + /* Allocate a line buffer. + */ + if( !(buffer = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( in ), unsigned char )) ) + return( -1 ); + +/* Our inner loop, parameterised for both the input and output + * types. Note the use of `\', since macros have to be all on + * one line. + */ +#define loop(IN, OUT) { \ + for( y = 0; y < in->Ysize; y++ ) { \ + IN *p = (IN *) IM_IMAGE_ADDR( in, 0, y ); \ + OUT *q = (OUT *) buffer; \ + \ + for( x = 0; x < IM_IMAGE_N_ELEMENTS( in ); x++ ) \ + q[x] = exp( p[x] ); \ + if( im_writeline( y, out, buffer ) ) \ + return( -1 ); \ + } \ +} + + /* Switch for all the types we can handle. + */ + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: loop( char, float ); break; + case IM_BANDFMT_USHORT:loop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: loop( short, float ); break; + case IM_BANDFMT_UINT: loop( unsigned int, float ); break; + case IM_BANDFMT_INT: loop( int, float ); break; + case IM_BANDFMT_FLOAT: loop( float, float ); break; + case IM_BANDFMT_DOUBLE:loop( double, double ); break; + default: + im_error( "exptra", "internal error" ); + return( -1 ); + } + + /* Success. + */ + return( 0 ); +} +\end{verbatim} +\caption{Calculate \texttt{exp()} for an image (cont)} +\end{fig2} diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 00000000..d3c74249 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = vips diff --git a/include/vips/Makefile.am b/include/vips/Makefile.am new file mode 100644 index 00000000..42edea2e --- /dev/null +++ b/include/vips/Makefile.am @@ -0,0 +1,34 @@ +pkginclude_HEADERS = \ + VDisplay.h \ + VError.h \ + VImage.h \ + VMask.h \ + vipscpp.h \ + colour.h \ + debug.h \ + dispatch.h \ + fmask.h \ + history.h \ + mosaic.h \ + proto.h \ + rect.h \ + region.h \ + r_access.h \ + struct.h \ + semaphore.h \ + threadgroup.h \ + thread.h \ + time.h \ + util.h \ + meta.h \ + version.h \ + vips.h \ + vips \ + intl.h \ + vbuf.h \ + vipsc++.h + +vipsc++.h: + vips --cpph all > vipsc++.h + +EXTRA_DIST = version.h.in internal.h diff --git a/include/vips/VDisplay.h b/include/vips/VDisplay.h new file mode 100644 index 00000000..5e027a65 --- /dev/null +++ b/include/vips/VDisplay.h @@ -0,0 +1,113 @@ +/* VIPS display class. + * + * Hide details of im_col_display API. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VDISPLAY_H +#define IM_VDISPLAY_H + +/* SWIG includes this file directly rather than going through vipscpp.h ... so + * we have to define these macros here as well. + */ +#ifdef SWIG +#define VIPS_NAMESPACE_START namespace vips { +#define VIPS_NAMESPACE_END } +#endif /*SWIG*/ + +/* Wrap pointers to these, but we don't want to import all the old C API. Just + * declare them. + */ +extern "C" { + struct im_col_display; + struct im_col_tab_disp; +} + +VIPS_NAMESPACE_START + +// Wrapper over im_col_display with ref counting +class VDisplay { + struct refblock { + im_col_display *disp; // im_col_display struct + im_col_tab_disp *luts; // luts built from this display + int priv; // disp is ours, or system + int nrefs; // Refs to us + + // Invalidate lut + void cleanlut(); + + // Break attached stuff + void cleanref(); + + // Get ready to write + void wready() throw( VError ); + + // Check that luts are up-to-date + void cluts() throw( VError ); + + refblock() : disp(0), luts(0), priv(0), nrefs(1) {} + ~refblock() { cleanref(); } + }; + + refblock *ref; + +public: + enum VDisplayType { + BARCO, // Does many corrections for us + DUMB // Needs many corrections + }; + + // Get named display + VDisplay( const char *name ) throw( VError ); + + // Get default display + VDisplay(); + + // Copy constructor + VDisplay( const VDisplay &a ) { ref = a.ref; ref->nrefs++; } + + // Assignment + VDisplay &operator=( const VDisplay &a ); + + // Destructor + virtual ~VDisplay(); + + // The matrix type we use + typedef float matrix[3][3]; + + // Extract display pointer + void *disp() const { return( ref->disp ); } + + // Extract luts pointer, rebuilding luts if necessary + im_col_tab_disp *luts() const throw( VError ) + { ref->cluts(); return( ref->luts ); } +}; + +VIPS_NAMESPACE_END + +#endif /*IM_VDISPLAY_H*/ diff --git a/include/vips/VError.h b/include/vips/VError.h new file mode 100644 index 00000000..69aa4b83 --- /dev/null +++ b/include/vips/VError.h @@ -0,0 +1,82 @@ +// Header for error type + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VERROR_H +#define IM_VERROR_H + +/* SWIG includes this file directly rather than going through vipscpp.h ... so + * we have to define these macros here as well. + */ +#ifdef SWIG +#define VIPS_NAMESPACE_START namespace vips { +#define VIPS_NAMESPACE_END } +#endif /*SWIG*/ + +/* Don't include these when parsing for SWIG. + */ +#ifndef SWIG +# include +# include +# include +#endif /*!SWIG*/ + +VIPS_NAMESPACE_START + +// Error type +class VError : public std::exception { + std::string _what; + +public: + VError( std::string what ) : _what( what ) {} + VError() {} + virtual ~VError() throw() {} + + // Print message and exit + void perror( const char * ); + void perror(); + + // Append some more text to the message + VError &app( std::string txt ); + VError &app( const int i ); + + // Extract string + virtual const char *what() const throw() { return _what.c_str(); } + void ostream_print( std::ostream & ) const; +}; + +inline std::ostream &operator<<( std::ostream &file, const VError &err ) +{ + err.ostream_print( file ); + return( file ); +} + +void verror( std::string str = "" ) throw( VError ); + +VIPS_NAMESPACE_END + +#endif /*IM_VERROR_H*/ diff --git a/include/vips/VImage.h b/include/vips/VImage.h new file mode 100644 index 00000000..3444f2fa --- /dev/null +++ b/include/vips/VImage.h @@ -0,0 +1,401 @@ +// VIPS image wrapper + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VIMAGE_H +#define IM_VIMAGE_H + +/* SWIG includes this file directly rather than going through vipscpp.h ... so + * we have to define these macros here as well. + */ +#ifdef SWIG +# define VIPS_NAMESPACE_START namespace vips { +# define VIPS_NAMESPACE_END } +#endif /*SWIG*/ + +/* Don't include these when parsing for SWIG. + */ +#ifndef SWIG +# include +# include +# include +#endif /*!SWIG*/ + +/* Wrap pointers to these, but we don't want to import all the old C API. Just + * declare them. + */ +extern "C" { + struct im__IMAGE; + + /* Needed by Vargv, see below. + */ + struct im__function; + typedef void *im__object; +} + +VIPS_NAMESPACE_START + +/* VIPS image class. + * + * Slightly tricky: we have two sorts of sharing. Several VImage can share one + * refblock (while results are being returned from functions, for example), + * and several other refblocks can have IMAGEs which depend upon this IMAGE + * for their result. + */ +class VImage { + /* We'd like this to be protected so that user subclasses can define + * their own member wrappers. But sadly C++ doesn't work like that: + * subclasses of VImage can only refer to protected members via + * this->, which isn't what we need. Just make it public and hope no + * one touches it. + */ +public: +/* Doesn't need to be wrapped. + */ +#ifndef SWIG + // Count ref etc. in one of these. One for each open VIPS image. + struct refblock { + im__IMAGE *im; // IMAGE pointer + int close_on_delete; // Set if we must im_close() + int nrefs; // Number of refs to us + std::list orefs; // Refs im makes + + // Construct/destruct + refblock(); + virtual ~refblock() throw( VError ); + + // Add a ref - this (output image) depends upon IMAGE in + void addref( refblock *in ) throw( VError ); + + // Remove a ref + void removeref() throw( VError ); + + // Debugging + void debug_print(); + + // Linked list needs "==" -- use address equivalence + friend int operator==( const refblock &left, + const refblock &right ) { return( &left == &right ); } + }; + + refblock *_ref; +#endif /*!SWIG*/ + +public: +#ifdef DEBUG + /* All the refblocks in the world. + */ + static std::list all_refblock; +#endif /*DEBUG*/ + + /* Print all refblocks ... debugging. Compile with DEBUG to enable + * this. + */ + static void print_all(); + + /* Typedefs and enums we need. + */ + + // Type type + enum TType { + MULTIBAND = 0, + B_W = 1, + LUMINACE = 2, + XRAY = 3, + IR = 4, + YUV = 5, + RED_ONLY = 6, + GREEN_ONLY = 7, + BLUE_ONLY = 8, + POWER_SPECTRUM = 9, + HISTOGRAM = 10, + LUT = 11, + XYZ = 12, + LAB = 13, + CMC = 14, + CMYK = 15, + LABQ = 16, + RGB = 17, + UCS = 18, + LCH = 19, + LABS = 21, + sRGB = 22, + YXY = 23, + FOURIER = 24, + RGB16 = 25, + GREY16 = 26 + }; + + // Format type + enum TBandFmt { + FMTNOTSET = -1, + FMTUCHAR = 0, + FMTCHAR = 1, + FMTUSHORT = 2, + FMTSHORT = 3, + FMTUINT = 4, + FMTINT = 5, + FMTFLOAT = 6, + FMTCOMPLEX = 7, + FMTDOUBLE = 8, + FMTDPCOMPLEX = 9 + }; + + // Coding type + enum TCoding { + NOCODING = 0, + COLQUANT = 1, + LABPACK = 2, + LABPACK_COMPRESSED = 3, + RGB_COMPRESSED = 4, + LUM_COMPRESSED = 5 + }; + + // Compression type + enum TCompression { + NO_COMPRESSION = 0, + TCSF_COMPRESSION = 1, + JPEG_COMPRESSION = 2 + }; + + /* Start of wrappers for iofuncs. + */ + + // Plain constructors + VImage( const char *name, const char *mode = "r" ) throw( VError ); + VImage( void *data, int width, int height, + int bands, TBandFmt format ) throw( VError ); + VImage( im__IMAGE *image ); + VImage() throw( VError ); + + // Copy constructor + VImage( const VImage &a ); + + // Assignment - delete old ref + VImage &operator=( const VImage &a ) throw( VError ); + + // Destructor + virtual ~VImage() throw( VError ) { _ref->removeref(); } + + // Extract underlying IMAGE* pointer + im__IMAGE *image() const { return( _ref->im ); } + + // Extract underlying data pointer + void *data() const throw( VError ); + + // Write this to another VImage, to a file, or to a mem buffer + VImage write( VImage out ) throw( VError ); + VImage write( const char *name ) throw( VError ); + VImage write() throw( VError ); + + // Debugging ... print header fields + void debug_print(); + + // Projection functions to get header fields + int Xsize(); + int Ysize(); + int Bands(); + TBandFmt BandFmt(); + TCoding Coding(); + TType Type(); + float Xres(); + float Yres(); + int Length(); + TCompression Compression(); + short Level(); + int Xoffset(); + int Yoffset(); + + // Derived fields + const char *filename(); + const char *Hist(); + + // Set header fields + void initdesc( int, int, int, TBandFmt, TCoding, TType, + float = 1.0, float = 1.0, int = 0, int = 0 ) throw( VError ); + + /* Insert automatically generated headers. + */ +#include "vipsc++.h" + +/* No point getting SWIG to wrap these ... we do this by hand later so we can + * handle things like "a + 12" correctly. + */ +#ifndef SWIG + // And some in-line operator equivalences done by hand + friend VImage operator+( VImage a, VImage b ) throw( VError ) + { return( a.add( b ) ); } + friend VImage operator+( double a, VImage b ) throw( VError ) + { return( b.lin( 1.0, a ) ); } + friend VImage operator+( VImage a, double b ) throw( VError ) + { return( a.lin( 1.0, b ) ); } + + friend VImage operator-( VImage a, VImage b ) throw( VError ) + { return( a.subtract( b ) ); } + friend VImage operator-( double a, VImage b ) throw( VError ) + { return( b.lin( -1.0, a ) ); } + friend VImage operator-( VImage a, double b ) throw( VError ) + { return( a.lin( 1.0, -b ) ); } + + friend VImage operator*( VImage a, VImage b ) throw( VError ) + { return( a.multiply( b ) ); } + friend VImage operator*( double a, VImage b ) throw( VError ) + { return( b.lin( a, 0.0 ) ); } + friend VImage operator*( VImage a, double b ) throw( VError ) + { return( a.lin( b, 0.0 ) ); } + + friend VImage operator/( VImage a, VImage b ) throw( VError ) + { return( a.divide( b ) ); } + friend VImage operator/( double a, VImage b ) throw( VError ) + { return( b.pow( -1.0 ).lin( a, 0.0 ) ); } + friend VImage operator/( VImage a, double b ) throw( VError ) + { return( a.lin( 1.0/b, 0.0 ) ); } + + friend VImage operator%( VImage a, VImage b ) throw( VError ) + { return( a.remainder( b ) ); } + friend VImage operator%( VImage a, double b ) throw( VError ) + { return( a.remainder( b ) ); } + + friend VImage operator<( VImage a, VImage b ) throw( VError ) + { return( a.less( b ) ); } + friend VImage operator<( double a, VImage b ) throw( VError ) + { return( b.more( a ) ); } + friend VImage operator<( VImage a, double b ) throw( VError ) + { return( a.less( b ) ); } + + friend VImage operator<=( VImage a, VImage b ) throw( VError ) + { return( a.lesseq( b ) ); } + friend VImage operator<=( double a, VImage b ) throw( VError ) + { return( b.moreeq( a ) ); } + friend VImage operator<=( VImage a, double b ) throw( VError ) + { return( a.lesseq( b ) ); } + + friend VImage operator>( VImage a, VImage b ) throw( VError ) + { return( a.more( b ) ); } + friend VImage operator>( double a, VImage b ) throw( VError ) + { return( b.less( a ) ); } + friend VImage operator>( VImage a, double b ) throw( VError ) + { return( a.more( b ) ); } + + friend VImage operator>=( VImage a, VImage b ) throw( VError ) + { return( a.moreeq( b ) ); } + friend VImage operator>=( double a, VImage b ) throw( VError ) + { return( b.lesseq( a ) ); } + friend VImage operator>=( VImage a, double b ) throw( VError ) + { return( a.moreeq( b ) ); } + + friend VImage operator==( VImage a, VImage b ) throw( VError ) + { return( a.equal( b ) ); } + friend VImage operator==( double a, VImage b ) throw( VError ) + { return( b.equal( a ) ); } + friend VImage operator==( VImage a, double b ) throw( VError ) + { return( a.equal( b ) ); } + + friend VImage operator!=( VImage a, VImage b ) throw( VError ) + { return( a.notequal( b ) ); } + friend VImage operator!=( double a, VImage b ) throw( VError ) + { return( b.notequal( a ) ); } + friend VImage operator!=( VImage a, double b ) throw( VError ) + { return( a.notequal( b ) ); } + + friend VImage operator&( VImage a, VImage b ) throw( VError ) + { return( a.andimage( b ) ); } + friend VImage operator&( int a, VImage b ) throw( VError ) + { return( b.andimage( a ) ); } + friend VImage operator&( VImage a, int b ) throw( VError ) + { return( a.andimage( b ) ); } + + friend VImage operator|( VImage a, VImage b ) throw( VError ) + { return( a.orimage( b ) ); } + friend VImage operator|( int a, VImage b ) throw( VError ) + { return( b.orimage( a ) ); } + friend VImage operator|( VImage a, int b ) throw( VError ) + { return( a.orimage( b ) ); } + + friend VImage operator^( VImage a, VImage b ) throw( VError ) + { return( a.eorimage( b ) ); } + friend VImage operator^( int a, VImage b ) throw( VError ) + { return( b.eorimage( a ) ); } + friend VImage operator^( VImage a, int b ) throw( VError ) + { return( a.eorimage( b ) ); } + + friend VImage operator<<( VImage a, int b ) throw( VError ) + { return( a.shiftleft( b ) ); } + friend VImage operator>>( VImage a, int b ) throw( VError ) + { return( a.shiftright( b ) ); } + + friend VImage operator-( VImage a ) throw( VError ) + { return( a * -1 ); } + + // Type conversion: VImage to VDMask and VIMask + operator VDMask() throw( VError ) + { return( this->vips2mask() ); } + operator VIMask() throw( VError ) + { return( VIMask( VDMask( *this ) ) ); } +#endif /*!SWIG*/ +}; + +/* Don't include these when parsing for SWIG. + */ +#ifndef SWIG + +/* Class wrapping up a vargv. Member function wrappers need this. It needs to + * be part of the public API in case people subclass VImage and add their own + * members. + */ +class Vargv { + // Function we are args to + im__function *fn; + + // Base of object vector + im__object *base; + +public: + Vargv( const char *name ); + ~Vargv(); + + // Reference to element of base + im__object &data( int i = 0 ) { return( base[i] ); }; + + // Invoke function + void call(); +}; + +#endif /*!SWIG*/ + +VIPS_NAMESPACE_END + +// Other VIPS protos +extern "C" { +extern int im_init_world( const char *argv0 ); +extern void im__print_all(); +extern void im_col_Lab2XYZ( + float, float, float, + float *, float *, float * ); +} + +#endif /*IM_VIMAGE_H*/ diff --git a/include/vips/VMask.h b/include/vips/VMask.h new file mode 100644 index 00000000..76a7e4ae --- /dev/null +++ b/include/vips/VMask.h @@ -0,0 +1,375 @@ +/* VIPS mask class. + * + * Just like VImage, but we don't need dependency stuff. Instead, have a base + * wrapper over *MASK, derive VMaskD and VMaskI from that, and then put + * refcounting over all of them. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VMASK_H +#define IM_VMASK_H + +/* SWIG includes this file directly rather than going through vipscpp.h ... so + * we have to define these macros here as well. + */ +#ifdef SWIG +# define VIPS_NAMESPACE_START namespace vips { +# define VIPS_NAMESPACE_END } +#endif /*SWIG*/ + +/* Don't include these when parsing for SWIG. + */ +#ifndef SWIG +# include +# include +#endif /*!SWIG*/ + +/* Wrap pointers to these, but we don't want to import all the old C API. Just + * declare them. + */ +extern "C" { + struct im__INTMASK; + struct im__DOUBLEMASK; +} + +VIPS_NAMESPACE_START + +/* This first section is private. Only expose the non-P versions of these + * classes later on. Don't need to wrap then in SWIG either. + */ +#ifndef SWIG +namespace _private_detail { + +union MASKUNION { + im__INTMASK *iptr; + im__DOUBLEMASK *dptr; +}; + +// Private wrapper over *MASK - user does not see this +class VPMask { + friend class VMask; + +public: + // Track type of mask with this + enum VMaskType { + UNASSIGNED, // Not yet set + INT, // mask points to INTMASK + DOUBLE // mask points to DOUBLEMASK + }; + + MASKUNION data; // Mask pointer - INT or DOUBLE + VMaskType type; // Track type too, for safety + + virtual ~VPMask() {}; + + // Duplicate + virtual VPMask *dup() const = 0; + + // Projection functions to get MASK fields + virtual int xsize() const = 0; + virtual int ysize() const = 0; + virtual const char *filename() const = 0; + + // Output + virtual void ostream_print( std::ostream & ) const = 0; +}; + +// Specialise for INTMASK +class VPIMask : public VPMask { +public: + VPIMask( int xsize, int ysize ) throw( VError ); + VPIMask( int xsize, int ysize, int scale, int offset, va_list ap ) + throw( VError ); + VPIMask( const char * ) + throw( VError ); + VPIMask( im__INTMASK * ); + VPIMask(); + virtual ~VPIMask(); + + VPMask *dup() const throw( VError ); + void embed( im__INTMASK * ) throw( VError ); + + int xsize() const throw( VError ); + int ysize() const throw( VError ); + int scale() const throw( VError ); + int offset() const throw( VError ); + const char *filename() const throw( VError ); + + // Output + virtual void ostream_print( std::ostream & ) const throw( VError ); + + // Extract start of array of ints + int *array() const; +}; + +// Specialise for DOUBLEMASK +class VPDMask : public VPMask { +public: + VPDMask( int xsize, int ysize ) throw( VError ); + VPDMask( int xsize, int ysize, + double scale, double offset, va_list ap ) throw( VError ); + VPDMask( const char * ) throw( VError ); + VPDMask( im__DOUBLEMASK * ); + VPDMask(); + virtual ~VPDMask(); + + VPMask *dup() const throw( VError ); + void embed( im__DOUBLEMASK * ) throw( VError ); + + int xsize() const throw( VError ); + int ysize() const throw( VError ); + double scale() const throw( VError ); + double offset() const throw( VError ); + const char *filename() const throw( VError ); + + // Output + virtual void ostream_print( std::ostream & ) const throw( VError ); + + // Extract start of array of doubles + double *array() const; +}; + +} // end of namespace _private_detail + +inline std::ostream &operator<<( std::ostream &file, + const _private_detail::VPMask &msk ) +{ + msk.ostream_print( file ); + return( file ); +} + +#endif /*!SWIG*/ + +// Wrapper over VP?Mask with ref counting +class VMask { +protected: + struct refblock { + _private_detail::VPMask *pmask; // Mask: double or int + int nrefs; // Refs to us + + refblock() : pmask(0), nrefs(1) {} + virtual ~refblock() { delete pmask; } + }; + + refblock *ref; + + // Make sure this is a private copy of pmask --- dup if nrefs != 1 + void make_private(); + +public: + // Constructor leaves msk uninitialised + VMask() { ref = new refblock; } + + // Copy constructor + VMask( const VMask &a ) { ref = a.ref; ref->nrefs++; } + + // Assignment + VMask &operator=( const VMask &a ); + + // Destructor + virtual ~VMask(); + + int xsize() const throw( VError ) + { return( ref->pmask->xsize() ); } + int ysize() const throw( VError ) + { return( ref->pmask->ysize() ); } + int size() const throw( VError ) + { return( xsize() * ysize() ); } + const char *filename() const throw( VError ) + { return( ref->pmask->filename() ); } + + // Extract underlying type + _private_detail::VPMask::VMaskType type() const + { return( ref->pmask->type ); } + + // Extract underlying VIPS pointer + _private_detail::MASKUNION mask() const { return( ref->pmask->data ); } + + void ostream_print( std::ostream & ) const; +}; + +inline std::ostream &operator<<( std::ostream &file, const VMask &msk ) +{ + msk.ostream_print( file ); + return( file ); +} + +// Need to forward ref these +class VDMask; +class VImage; + +// Wrapper over _private_detail::VPIMask with ref counting +class VIMask : public VMask { +public: + VIMask( int xsize, int ysize ) + { + ref->pmask = new _private_detail::VPIMask( xsize, ysize ); + } + + VIMask( int xsize, int ysize, int scale, int offset, ... ) + { + va_list ap; + + va_start( ap, offset ); + ref->pmask = new _private_detail::VPIMask( xsize, ysize, + scale, offset, ap ); + va_end( ap ); + } + + VIMask( const char *name ) + { + ref->pmask = new _private_detail::VPIMask( name ); + } + + // No mask there yet + VIMask() {} + + int scale() + { + return( ((_private_detail::VPIMask *)ref->pmask)->scale() ); + } + + int offset() + { + return( ((_private_detail::VPIMask *)ref->pmask)->offset() ); + } + + // Embed INTMASK in VIMask + void embed( im__INTMASK * ) throw( VError ); + + // Overload [] to get linear array subscript. + int &operator[]( int ) throw( VError ); + + // Overload () to get matrix subscript. + int &operator()( int x, int y ) throw( VError ) + { return( (*this)[x + y*xsize()] ); } + + // and as a function call that SWIG can wrap + int get( int i ) throw( VError ) + { return( (*this)[i] ); } + + // Type conversion: INTMASK->DOUBLEMASK + operator VDMask(); + + // Type conversion: INTMASK->image + operator VImage(); + + // VIMask build functions + static VIMask gauss( double, double ) throw( VError ); + static VIMask log( double, double ) throw( VError ); + + // VIMask manipulation + VIMask rotate45() throw( VError ); + VIMask rotate90() throw( VError ); + + // Arithmetic ... cast to double, and use VDMask funcs. For some + // reason, the compiler won't let us do casts to VDImage yet, so no + // inlines. + VDMask trn() throw( VError ); + VDMask inv() throw( VError ); + VDMask cat( VDMask ) throw( VError ); + VDMask mul( VDMask ) throw( VError ); +}; + +// Wrapper over _private_detail::VPDMask with ref counting +class VDMask : public VMask { +public: + VDMask( int xsize, int ysize ) + { + ref->pmask = new _private_detail::VPDMask( xsize, ysize ); + } + + VDMask( int xsize, int ysize, double scale, double offset, ... ) + { + va_list ap; + + va_start( ap, offset ); + ref->pmask = new _private_detail::VPDMask( xsize, ysize, + scale, offset, ap ); + va_end( ap ); + } + + VDMask( const char *name ) + { + ref->pmask = new _private_detail::VPDMask( name ); + } + + // No mask yet + VDMask() { } + + // Embed DOUBLEMASK in VDMask + void embed( im__DOUBLEMASK * ) throw( VError ); + + double scale() throw( VError ) + { + return( ((_private_detail::VPDMask *)ref->pmask)->scale() ); + } + + double offset() throw( VError ) + { + return( ((_private_detail::VPDMask *)ref->pmask)->offset() ); + } + + // Overload [] to get linear array subscript. + double &operator[]( int ) throw( VError ); + + // Overload () to get matrix subscript. + double &operator()( int x, int y ) throw( VError ) + { return( (*this)[x + y*xsize()] ); } + + // and as a function call that SWIG can wrap + double get( int i ) throw( VError ) + { return( (*this)[i] ); } + + // Type conversion: double->int + operator VIMask(); + + // Type conversion: DOUBLEMASK->image + operator VImage() throw( VError ); + + // VDMask build functions + static VDMask gauss( double, double ) throw( VError ); + static VDMask log( double, double ) throw( VError ); + + // VDMask manipulation + VDMask rotate45() throw( VError ); + VDMask rotate90() throw( VError ); + + // Scale to intmask + VIMask scalei() throw( VError ); + + // Simple arithmetic + VDMask trn() throw( VError ); + VDMask inv() throw( VError ); + VDMask cat( VDMask ) throw( VError ); + VDMask mul( VDMask ) throw( VError ); +}; + +VIPS_NAMESPACE_END + +#endif /*IM_VMASK_H*/ diff --git a/include/vips/colour.h b/include/vips/colour.h new file mode 100644 index 00000000..088fdebf --- /dev/null +++ b/include/vips/colour.h @@ -0,0 +1,234 @@ +/* Definitions for VIPS colour package. + * + * J.Cupitt, 8/4/93 + * 15/7/96 JC + * - C++ stuff added + * 20/2/98 JC + * - new display calibration added + * 26/9/05 + * - added IM_ prefix to colour temps + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_COLOUR_H +#define IM_COLOUR_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#include + +/* Convert degrees->rads and vice-versa. + */ +#define IM_RAD( r ) (((r) / 360.0) * 2.0 * IM_PI) +#define IM_DEG( a ) (((a) / (2.0 * IM_PI)) * 360.0) + +/* Areas under curves for Dxx. 2 degree observer. + */ +#define IM_D93_X0 (89.7400) +#define IM_D93_Y0 (100.0) +#define IM_D93_Z0 (130.7700) + +#define IM_D75_X0 (94.9682) +#define IM_D75_Y0 (100.0) +#define IM_D75_Z0 (122.5710) + +/* D65 temp 6504. + */ +#define IM_D65_X0 (95.0470) +#define IM_D65_Y0 (100.0) +#define IM_D65_Z0 (108.8827) + +#define IM_D55_X0 (95.6831) +#define IM_D55_Y0 (100.0) +#define IM_D55_Z0 (92.0871) + +#define IM_D50_X0 (96.4250) +#define IM_D50_Y0 (100.0) +#define IM_D50_Z0 (82.4680) + +/* A temp 2856k. + */ +#define IM_A_X0 (109.8503) +#define IM_A_Y0 (100.0) +#define IM_A_Z0 (35.5849) + +/* B temp 4874k. + */ +#define IM_B_X0 (99.0720) +#define IM_B_Y0 (100.0) +#define IM_B_Z0 (85.2230) + +/* C temp 6774k. + */ +#define IM_C_X0 (98.0700) +#define IM_C_Y0 (100.0) +#define IM_C_Z0 (118.2300) + +#define IM_E_X0 (100.0) +#define IM_E_Y0 (100.0) +#define IM_E_Z0 (100.0) + +#define IM_D3250_X0 (105.6590) +#define IM_D3250_Y0 (100.0) +#define IM_D3250_Z0 (45.8501) + +/* Two kinds of display. A DISP_BARCO does gamma correction etc etc for us and + * needs only a colour space transform, a DISP_DUMB is an ordinary display and + * needs a full range of corrections. + */ +enum im_col_disp_type { + DISP_BARCO = 0, + DISP_DUMB +}; + +/* Structure for holding information about a display device. See the BARCO + * papers for details on the fields. + */ +struct im_col_display { + char *d_name; /* Display name */ + enum im_col_disp_type d_type; /* Display type */ + float d_mat[3][3]; /* XYZ -> luminance matrix */ + float d_YCW; /* Luminosity of reference white */ + float d_xCW; /* x, y for reference white */ + float d_yCW; + float d_YCR; /* Light o/p for reference white */ + float d_YCG; + float d_YCB; + int d_Vrwr; /* Pixel values for ref. white */ + int d_Vrwg; + int d_Vrwb; + float d_Y0R; /* Residual light for black pixel */ + float d_Y0G; + float d_Y0B; + float d_gammaR; /* Gamma values for the three guns */ + float d_gammaG; + float d_gammaB; + float d_B; /* 'Background' (like brightness) */ + float d_P; /* 'Picture' (like contrast) */ +}; + +/* Structure for holding the lookup tables for XYZ<=>rgb conversion. + * Also holds the luminance to XYZ matrix and the inverse one. + */ +struct im_col_tab_disp { + float t_Yr2r[1501]; /* Conversion of Yr to r */ + float t_Yg2g[1501]; /* Conversion of Yg to g */ + float t_Yb2b[1501]; /* Conversion of Yb to b */ + float t_r2Yr[1501]; /* Conversion of r to Yr */ + float t_g2Yg[1501]; /* Conversion of g to Yg */ + float t_b2Yb[1501]; /* Conversion of b to Yb */ + float mat_XYZ2lum[3][3]; /* XYZ to Yr, Yg, Yb matrix */ + float mat_lum2XYZ[3][3]; /* Yr, Yg, Yb to XYZ matrix */ + float rstep, gstep, bstep; + float ristep, gistep, bistep; +}; + +/* Colour loading and conversion functions. + */ +void im_col_ab2Ch( float a, float b, float *C, float *h ); +void im_col_LCh2ab( float L, float C, float h, float *a, float *b ); +void im_col_XYZ2Lab( float X, float Y, float Z, float *L, float *a, float *b ); +void im_col_Lab2XYZ( float L, float a, float b, float *X, float *Y, float *Z ); +float im_col_pythagoras( float L1, float a1, float b1, + float L2, float a2, float b2 ); +struct im_col_tab_disp *im_col_make_tables_RGB( + IMAGE *im, + struct im_col_display *d ); +int im_col_rgb2XYZ( struct im_col_display *d, + struct im_col_tab_disp *table, + int r, int g, int b, + float *X, float *Y, float *Z ); +int im_col_XYZ2rgb( + struct im_col_display *d, struct im_col_tab_disp *table, + float X, float Y, float Z, + int *r_ret, int *g_ret, int *b_ret, + int *or_ret ); + +float im_col_L2Lucs( float L ); +float im_col_Lucs2L( float Lucs ); +float im_col_C2Cucs( float C ); +float im_col_Cucs2C( float Cucs ); +float im_col_Ch2hucs( float C, float h ); +float im_col_Chucs2h( float C, float hucs ); +double im_col_ab2h( double a, double b ); + +int im_ICC2display( char *filename, struct im_col_display *dpy ); +int im_XYZ2disp( IMAGE *, IMAGE *, struct im_col_display * ); +int im_Lab2disp( IMAGE *, IMAGE *, struct im_col_display * ); +int im_LabQ2disp( IMAGE *, IMAGE *, struct im_col_display * ); +int im_disp2XYZ( IMAGE *, IMAGE *, struct im_col_display * ); +int im_disp2Lab( IMAGE *, IMAGE *, struct im_col_display * ); + +void *im_LabQ2disp_build_table( IMAGE *out, struct im_col_display *d ); +int im_LabQ2disp_table( IMAGE *in, IMAGE *out, void *table ); + +int im_dE_fromdisp( IMAGE *, IMAGE *, IMAGE *, struct im_col_display * ); +int im_dECMC_fromdisp( IMAGE *, IMAGE *, IMAGE *, struct im_col_display * ); +int im_dE00_fromLab( IMAGE *, IMAGE *, IMAGE * ); + +/* Colour display values and arrays + &im_col_screen_white, index 0 + &im_col_SPARC_white, index 1 + &im_col_D65_white, index 2 + &im_col_barco_white, index 3 + &im_col_mitsubishi, index 4 + &im_col_relative, index 5 + &ultra2, index 6 + &srgb_profile, index 7 + */ +struct im_col_display *im_col_displays( int ); +struct im_col_display *im_col_display_name( const char * ); + +/* Render intents for icc wrappers. + */ +#define IM_INTENT_PERCEPTUAL (0) +#define IM_INTENT_RELATIVE_COLORIMETRIC (1) +#define IM_INTENT_SATURATION (2) +#define IM_INTENT_ABSOLUTE_COLORIMETRIC (3) + +int im_icc_present( void ); +int im_icc_transform( IMAGE *in, IMAGE *out, + const char *input_profile_filename, + const char *output_profile_filename, + int intent ); +int im_icc_import( IMAGE *in, IMAGE *out, + const char *input_profile_filename, int intent ); +int im_icc_import_embedded( IMAGE *in, IMAGE *out, int intent ); +int im_icc_export( IMAGE *in, IMAGE *out, + const char *output_profile_filename, int intent ); +int im_icc_export_depth( IMAGE *in, IMAGE *out, int depth, + const char *output_profile_filename, int intent ); +int im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_COLOUR_H*/ diff --git a/include/vips/debug.h b/include/vips/debug.h new file mode 100644 index 00000000..71a9732a --- /dev/null +++ b/include/vips/debug.h @@ -0,0 +1,50 @@ +/* Support for debug.c in iofuncs. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_DEBUG_H +#define IM_DEBUG_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* All open image descriptors ... see im_init() and im_close(). + */ +extern GSList *im__open_images; + +/* Print one line for each descriptor, complete dump for one descriptor. + */ +void im__print_one( int n ); +void im__print_all( void ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /* IM_DEBUG_H */ diff --git a/include/vips/dispatch.h b/include/vips/dispatch.h new file mode 100644 index 00000000..e9fb4359 --- /dev/null +++ b/include/vips/dispatch.h @@ -0,0 +1,269 @@ +/* VIPS function dispatch. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_DISPATCH_H +#define IM_DISPATCH_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#include +#include + +/* Type names. You may define your own, but if you use one of these, then + * you should use the built-in VIPS type converters. + */ +#define IM_TYPE_IMAGEVEC "imagevec" /* im_object is ptr to IMAGE[] */ +#define IM_TYPE_DOUBLEVEC "doublevec" /* im_object is ptr to double[] */ +#define IM_TYPE_INTVEC "intvec" /* im_object is ptr to int[] */ +#define IM_TYPE_DOUBLE "double" /* im_object is ptr to double */ +#define IM_TYPE_INT "integer" /* 32-bit integer */ +#define IM_TYPE_COMPLEX "complex" /* Pair of doubles */ +#define IM_TYPE_STRING "string" /* Zero-terminated char array */ +#define IM_TYPE_IMASK "intmask" /* Integer mask type */ +#define IM_TYPE_DMASK "doublemask" /* Double mask type */ +#define IM_TYPE_IMAGE "image" /* IMAGE descriptor */ +#define IM_TYPE_DISPLAY "display" /* Display descriptor */ +#define IM_TYPE_GVALUE "gvalue" /* GValue wrapper */ +typedef char *im_arg_type; /* Type of argument id */ + +/* Internal representation of an argument to an image processing function. + */ +typedef void *im_object; + +/* These bits are ored together to make the flags in a type descriptor. + * + * IM_TYPE_OUTPUT: set to indicate output, otherwise input. + * + * IM_TYPE_ARG: Two ways of making an im_object --- with and without a + * command-line string to help you along. Arguments with a string are thing + * like IMAGE descriptors, which require a filename to initialise. + * Arguments without are things like output numbers, where making the object + * simply involves allocating storage. + */ +typedef enum { + IM_TYPE_NONE = 0, /* No flags */ + IM_TYPE_OUTPUT = 0x1, /* Output/input object */ + IM_TYPE_ARG = 0x2 /* Uses a str arg in construction */ +} im_type_flags; + +/* Initialise and destroy objects. The "str" argument to the init function + * will not be supplied if this is not an ARG type. + */ +typedef int (*im_init_obj_fn)( im_object *obj, char *str ); +typedef int (*im_dest_obj_fn)( im_object obj ); + +/* Describe a VIPS type. + */ +typedef struct { + im_arg_type type; /* Type of argument */ + int size; /* sizeof( im_object repres. ) */ + im_type_flags flags; /* Flags */ + im_init_obj_fn init; /* Operation functions */ + im_dest_obj_fn dest; +} im_type_desc; + +/* Success on an argument. This is called if the image processing function + * succeeds and should be used to (for example) print output. + */ +typedef int (*im_print_obj_fn)( im_object obj ); + +/* Describe a VIPS command argument. + */ +typedef struct { + char *name; /* eg. "width" */ + im_type_desc *desc; /* Type description */ + im_print_obj_fn print; /* Print some output objects */ +} im_arg_desc; + +/* Type of VIPS dispatch funtion. + */ +typedef int (*im_dispatch_fn)( im_object *argv ); + +/* Maximum size of arg table. + */ +#define IM_MAX_ARGS (1000) + +/* Flags for functions. These are for information only, and more may be + * added. + */ +typedef enum { + IM_FN_NONE = 0, /* No flags set */ + IM_FN_PIO = 0x1, /* Is a partial function */ + IM_FN_TRANSFORM = 0x2, /* Performs coordinate transformations */ + IM_FN_PTOP = 0x4, /* Point-to-point ... can be done with a LUT */ + IM_FN_NOCACHE = 0x8 /* Result should not be cached */ +} im_fn_flags; + +/* Describe a VIPS function. + */ +typedef struct { + char *name; /* eg "im_invert" */ + char *desc; /* Description - eg "photographic negative" */ + im_fn_flags flags; /* Flags for this function */ + im_dispatch_fn disp; /* Dispatch */ + int argc; /* Number of args */ + im_arg_desc *argv; /* Arg table */ +} im_function; + +/* A set of VIPS functions forming a package. + */ +typedef struct { + char *name; /* Package name (eg "arithmetic") */ + int nfuncs; /* Number of functions in package */ + im_function **table; /* Array of function descriptors */ +} im_package; + +/* Externs for dispatch. + */ + +/* Struct for mask IO to a file. + */ +typedef struct { + char *name; /* Command-line name in */ + void *mask; /* Mask --- DOUBLE or INT */ +} im_mask_object; + +/* Struct for doublevec IO + */ +typedef struct { + int n; /* Vector length */ + double *vec; /* Vector */ +} im_doublevec_object; + +/* Struct for intvec IO + */ +typedef struct { + int n; /* Vector length */ + int *vec; /* Vector */ +} im_intvec_object; + +/* Struct for imagevec IO + */ +typedef struct { + int n; /* Vector length */ + IMAGE **vec; /* Vector */ +} im_imagevec_object; + +/* Built-in VIPS types. + */ +extern im_type_desc im__input_imagevec; +extern im_type_desc im__input_image; +extern im_type_desc im__output_image; +extern im_type_desc im__rw_image; +extern im_type_desc im__input_doublevec; +extern im_type_desc im__input_intvec; +extern im_type_desc im__input_double; +extern im_type_desc im__output_double; +extern im_type_desc im__input_int; +extern im_type_desc im__output_int; +extern im_type_desc im__input_string; +extern im_type_desc im__output_string; +extern im_type_desc im__output_complex; +extern im_type_desc im__input_dmask; +extern im_type_desc im__output_dmask; +extern im_type_desc im__output_dmask_screen; +extern im_type_desc im__input_display; +extern im_type_desc im__output_display; +extern im_type_desc im__input_imask; +extern im_type_desc im__output_imask; +extern im_type_desc im__input_gvalue; + +/* VIPS print functions. + */ +int im__iprint( im_object obj ); /* int */ +int im__dprint( im_object obj ); /* double */ +int im__cprint( im_object obj ); /* complex */ +int im__sprint( im_object obj ); /* string */ +int im__displayprint( im_object obj ); /* im_col_display */ +int im__dmsprint( im_object obj ); /* DOUBLEMASK as stats */ +int im__gprint( im_object obj ); /* GValue */ + +/* Macros for convenient creation. + */ +#define IM_INPUT_IMAGEVEC( S ) { S, &im__input_imagevec, NULL } +#define IM_INPUT_IMAGE( S ) { S, &im__input_image, NULL } +#define IM_OUTPUT_IMAGE( S ) { S, &im__output_image, NULL } +#define IM_RW_IMAGE( S ) { S, &im__rw_image, NULL } +#define IM_INPUT_DOUBLE( S ) { S, &im__input_double, NULL } +#define IM_INPUT_DOUBLEVEC( S ) { S, &im__input_doublevec, NULL } +#define IM_INPUT_INTVEC( S ) { S, &im__input_intvec, NULL } +#define IM_OUTPUT_DOUBLE( S ) { S, &im__output_double, im__dprint } +#define IM_INPUT_INT( S ) { S, &im__input_int, NULL } +#define IM_OUTPUT_INT( S ) { S, &im__output_int, im__iprint } +#define IM_INPUT_STRING( S ) { S, &im__input_string, NULL } +#define IM_OUTPUT_STRING( S ) { S, &im__output_string, im__sprint } +#define IM_INPUT_DISPLAY( S ) { S, &im__input_display, NULL } +#define IM_OUTPUT_DISPLAY( S ) { S, &im__output_display, im__displayprint } +#define IM_OUTPUT_COMPLEX( S ) { S, &im__output_complex, im__cprint } +#define IM_INPUT_DMASK( S ) { S, &im__input_dmask, NULL } +#define IM_OUTPUT_DMASK( S ) { S, &im__output_dmask, NULL } +#define IM_OUTPUT_DMASK_STATS( S ) { S, &im__output_dmask_screen, im__dmsprint } +#define IM_INPUT_IMASK( S ) { S, &im__input_imask, NULL } +#define IM_OUTPUT_IMASK( S ) { S, &im__output_imask, NULL } +#define IM_INPUT_GVALUE( S ) { S, &im__input_gvalue, NULL } +#define IM_OUTPUT_GVALUE( S ) { S, &im__output_gvalue, im__gprint } + +/* Add a plug-in package. + */ +im_package *im_load_plugin( const char *name ); +int im_load_plugins( const char *fmt, ... ) + __attribute__((format(printf, 1, 2))); + +/* Close all plug-ins. + */ +int im_close_plugins( void ); + +/* Loop over all loaded packages. + */ +void *im_map_packages( VSListMap2Fn fn, void *a ); + +/* Convenience functions for finding packages, functions, etc. + */ +im_function *im_find_function( const char *name ); +im_package *im_find_package( const char *name ); +im_package *im_package_of_function( const char *name ); + +/* Allocate space for, and free im_object argument lists. + */ +int im_free_vargv( im_function *fn, im_object *vargv ); +int im_allocate_vargv( im_function *fn, im_object *vargv ); + +/* Run a VIPS command by name. + */ +int im_run_command( char *name, int argc, char **argv ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_DISPATCH_H*/ diff --git a/include/vips/fmask.h b/include/vips/fmask.h new file mode 100644 index 00000000..49e1ebd5 --- /dev/null +++ b/include/vips/fmask.h @@ -0,0 +1,86 @@ +/* @(#) Typical filter function + * va_list is filter parameters + * lowpass highpass filters + * flag = 0 -> idealhpf, parameters: frequency cutoff + * flag = 1 -> ideallpf, parameters: frequency cutoff + * flag = 2 -> buthpf, parameters: order, frequency cutoff, amplitude cutoff + * flag = 3 -> butlpf, parameters: order, frequency cutoff, amplitude cutoff + * flag = 4 -> gaussianlpf, parameters: frequency cutoff, amplitude cutoff + * flag = 5 -> gaussianhpf, parameters: frequency cutoff, amplitude cutoff + * ring pass ring reject filters + * flag = 6 -> idealrpf, parameters: frequency cutoff, width + * flag = 7 -> idealrrf, parameters: frequency cutoff, width + * flag = 8 -> butrpf, parameters: order, freq cutoff, width, ampl cutoff + * flag = 9 -> butrrf, parameters: order, freq cutoff, width, ampl cutoff + * flag = 10 -> gaussianrpf, parameters: frequency cutoff, width, ampl cutoff + * flag = 11 -> gaussianrrf, parameters: frequency cutoff, width, ampl cutoff + * bandpass bandreject filters + * flag = 12 -> idealbpf, parameters: center frequency, 2*radius + * flag = 13 -> idealbrf, parameters: centre frequency, 2*radius + * flag = 14 -> butbpf, parameters: order, frequency, 2*radius, ampl cutoff + * flag = 15 -> butbrf, parameters: order, frequency, 2*radius, ampl cutoff + * flag = 16 -> gaussianbpf, parameters: frequency cutoff, width, ampl cutoff + * flag = 17 -> gaussianbrf, parameters: frequency cutoff, width, ampl cutoff + * fractal filters (for filtering gaussian noises only) + * flag = 18 -> fractal, parameters: fractal dimension + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_FMASK_H +#define IM_FMASK_H + +typedef enum mask_type { + MASK_IDEAL_HIGHPASS = 0, + MASK_IDEAL_LOWPASS = 1, + MASK_BUTTERWORTH_HIGHPASS = 2, + MASK_BUTTERWORTH_LOWPASS = 3, + MASK_GAUSS_HIGHPASS = 4, + MASK_GAUSS_LOWPASS = 5, + + MASK_IDEAL_RINGPASS = 6, + MASK_IDEAL_RINGREJECT = 7, + MASK_BUTTERWORTH_RINGPASS = 8, + MASK_BUTTERWORTH_RINGREJECT = 9, + MASK_GAUSS_RINGPASS = 10, + MASK_GAUSS_RINGREJECT = 11, + + MASK_IDEAL_BANDPASS = 12, + MASK_IDEAL_BANDREJECT = 13, + MASK_BUTTERWORTH_BANDPASS = 14, + MASK_BUTTERWORTH_BANDREJECT = 15, + MASK_GAUSS_BANDPASS = 16, + MASK_GAUSS_BANDREJECT = 17, + + MASK_FRACTAL_FLT = 18 +} MaskType; + +int im_flt_image_freq( IMAGE *in, IMAGE *out, MaskType flag, ... ); +int im_create_fmask( IMAGE *out, int xsize, int ysize, MaskType flag, ... ); +int im__fmaskcir( IMAGE *out, MaskType flag, va_list ap ); + +#endif /*IM_FMASK_H*/ diff --git a/include/vips/history.h b/include/vips/history.h new file mode 100644 index 00000000..5af9d69b --- /dev/null +++ b/include/vips/history.h @@ -0,0 +1,59 @@ +/* @(#) Useful macros for appending one line in the History field of the + * @(#) output image descriptor when a function is called + * @(#) The main program should use im_updatehist() + * @(#) The added line corresponds to the command relevant to the function + * @(#) for instance + * @(#) for the function: im_add(in1, in2, out) the following lines of code can + * @(#) be used to add a line of history in the Hist member + * @(#) of the out image descriptor + * @(#) .... + * @(#) IMAGE *in1, *in2, *out; + * @(#) .... + * @(#) if ( im_add(in1, in2, out) == -1 ) return(-1); + * @(#) if ( IM_ADD(in1, in2, out) == -1 ) return(-1); + * @(#) .... + * @(#) + * @(#) The first function will add the two images in1 and in2, + * @(#) whereas the second call will append + * @(#) at the history descriptor of out the line: + * @(#) add infile outfile # date + * @(#) where infile is in.filename and outfile is out.filename + * @(#) The history line has been prepared in such a way that the first + * @(#) argument is the UNIX command which corresponds to the function + * @(#) As a general rule, all functions in im_funcs directory which + * @(#) have a correponding command in src directory are listed here + * @(#) + * @(#) Since the macros presented in this file correspond to the function + * @(#) im_histlin() the returned value is 0 on success and -1 on error. + * @(#) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Made obsolete by the function database stuff ... just here in case anyone + * still includes it. + */ diff --git a/include/vips/internal.h b/include/vips/internal.h new file mode 100644 index 00000000..185a6686 --- /dev/null +++ b/include/vips/internal.h @@ -0,0 +1,121 @@ +/* Prototypes for internal VIPS functions. + * + * 11/9/06 + * - cut from proto.h + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_INTERNAL_H +#define IM_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +typedef int (*im__fftproc_fn)( IMAGE *, IMAGE *, IMAGE * ); + +/* iofuncs + */ +void im__read_4byte( int msb_first, unsigned char *to, unsigned char **from ); +void im__read_2byte( int msb_first, unsigned char *to, unsigned char **from ); +void im__write_4byte( unsigned char **to, unsigned char *from ); +void im__write_2byte( unsigned char **to, unsigned char *from ); + +int im__read_header_bytes( IMAGE *im, unsigned char *from ); +int im__write_header_bytes( IMAGE *im, unsigned char *to ); +int im__has_extension_block( IMAGE *im ); +void *im__read_extension_block( IMAGE *im, int *size ); +int im__readhist( IMAGE *image ); +int im__write_extension_block( IMAGE *im, void *buf, int size ); +int im__writehist( IMAGE *image ); + +extern int im__read_test; +extern int im__mmap_limit; +extern GMutex *im__global_lock; + +IMAGE *im__convert_saveable( IMAGE *in, gboolean allow_alpha ); + +void im__link_make( IMAGE *parent, IMAGE *child ); +void im__link_break_all( IMAGE *im ); +void *im__link_map( IMAGE *im, VSListMap2Fn fn, void *a, void *b ); + +GValue *im__gvalue_ref_string_new( const char *text ); +void im__gslist_gvalue_free( GSList *list ); +GSList *im__gslist_gvalue_copy( const GSList *list ); +GSList *im__gslist_gvalue_merge( GSList *a, const GSList *b ); +char *im__gslist_gvalue_get( const GSList *list ); + +void im__buffer_init( void ); + +int im__cast_and_call(); +int im__read_header( IMAGE *image ); +int im__test_kill( IMAGE *im ); +void *im__mmap( int fd, int writeable, size_t length, gint64 offset ); +int im__munmap( void *start, size_t length ); +int im__write( int, const void *, size_t ); +int im__open_image_file( const char *filename ); +gint64 im__image_pixel_length( IMAGE *im ); +void im__change_suffix( const char *name, char *out, int mx, + const char *new_suff, const char **olds, int nolds ); +void im__print_all( void ); +void im__print_one( int ); +int im__trigger_callbacks( GSList *cblist ); +int im__close( IMAGE * ); +int im__handle_eval( IMAGE *im, int w, int h ); +int im__create_int_luts( int *, int, int **, int **, int * ); +int im__create_double_luts( double *, int, double **, double **, int * ); +int im__fft_sp( float *rvec, float *ivec, int logrows, int logcols ); +int im__fftproc( IMAGE *dummy, IMAGE *in, IMAGE *out, im__fftproc_fn fn ); +int im__mean_std_double_buffer( double *buffer, int size, + double *pmean, double *pstd ); +int im__mean_std_int_buffer( int *buffer, int size, + double *pmean, double *pstd ); +int im__find_lroverlap( IMAGE *ref_in, IMAGE *sec_in, IMAGE *out, + int bandno_in, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int *dx0, int *dy0, + double *scale1, double *angle1, double *dx1, double *dy1 ); +int im__find_tboverlap( IMAGE *ref_in, IMAGE *sec_in, IMAGE *out, + int bandno_in, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int *dx0, int *dy0, + double *scale1, double *angle1, double *dx1, double *dy1 ); +int im__find_best_contrast( IMAGE *image, + int xpos, int ypos, int xsize, int ysize, + int xarray[], int yarray[], int cont[], + int nbest, int hcorsize ); +int im__balance( IMAGE *ref, IMAGE *sec, IMAGE *out, + IMAGE **ref_out, IMAGE **sec_out, int dx, int dy, int balancetype ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_INTERNAL_H*/ diff --git a/include/vips/intl.h b/include/vips/intl.h new file mode 100644 index 00000000..3974b9dc --- /dev/null +++ b/include/vips/intl.h @@ -0,0 +1,44 @@ +/* i18n stuff for vips. + */ + +#ifndef IM_VIPS_INTL_H +#define IM_VIPS_INTL_H + +const char *im__gettext( const char *msgid ); +const char *im__ngettext( const char *msgid, + const char *plural, unsigned long int n ); + +#ifdef ENABLE_NLS + +#include +#define _(String) im__gettext(String) +/* ngettext may be defined as a macro if we're optimised. + */ +#ifdef ngettext +#undef ngettext +#endif /*ngettext*/ +#define ngettext(String,Plural,number) im__ngettext(String,Plural,number) +#ifdef gettext_noop +#define N_(String) gettext_noop(String) +#else +#define N_(String) (String) +#endif + +#else /*!ENABLE_NLS*/ + +#define _(String) (String) +#define N_(String) (String) +#define textdomain(String) (String) +#define gettext(String) (String) +#define dgettext(Domain,String) (String) +#define dcgettext(Domain,String,Type) (String) +#define bindtextdomain(Domain,Directory) (Domain) +#define bind_textdomain_codeset(Domain,Codeset) (Codeset) +#define ngettext(S, P, N) ((N) == 1 ? (S) : (P)) +#define dngettext(D, S, P, N) ngettext(S, P, N) + +#endif /* ENABLE_NLS */ + +#endif /* IM_VIPS_INTL_H */ + + diff --git a/include/vips/meta.h b/include/vips/meta.h new file mode 100644 index 00000000..a300609a --- /dev/null +++ b/include/vips/meta.h @@ -0,0 +1,107 @@ +/* Metadata API. + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_META_H +#define IM_META_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Reserved header names. + */ +#define IM_META_EXIF_NAME "exif-data" +#define IM_META_ICC_NAME "icc-profile-data" +#define IM_META_XML "xml-header" + +/* Types we add for meta fields. + */ +#define IM_TYPE_SAVE_STRING (im_save_string_get_type()) +GType im_save_string_get_type( void ); +const char *im_save_string_get( const GValue *value ); +void im_save_string_set( GValue *value, const char *str ); +void im_save_string_setf( GValue *value, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); + +#define IM_TYPE_AREA (im_area_get_type()) +GType im_area_get_type( void ); + +#define IM_TYPE_REF_STRING (im_ref_string_get_type()) +GType im_ref_string_get_type( void ); +int im_ref_string_set( GValue *value, const char *str ); +const char *im_ref_string_get( const GValue *value ); +size_t im_ref_string_get_length( const GValue *value ); + +#define IM_TYPE_BLOB (im_blob_get_type()) +GType im_blob_get_type( void ); +void *im_blob_get( const GValue *value, size_t *data_length ); +int im_blob_set( GValue *value, im_callback_fn free_fn, + void *data, size_t length ); + +/* What we store in the Meta hash table. We can't just use GHashTable's + * key/value pairs, since we need to iterate over meta in Meta_traverse order. + * + * We don't refcount at this level ... large meta values are refcounted by + * their GValue implementation, see eg. MetaArea below. + */ +typedef struct _Meta { + IMAGE *im; + + char *field; /* strdup() of field name */ + GValue value; /* copy of value */ +} Meta; + +int im_meta_set( IMAGE *, const char *field, GValue * ); +int im_meta_get( IMAGE *, const char *field, GValue * ); +GType im_meta_get_type( IMAGE *im, const char *field ); + +int im_meta_set_int( IMAGE *, const char *field, int i ); +int im_meta_get_int( IMAGE *, const char *field, int *i ); +int im_meta_set_double( IMAGE *, const char *field, double d ); +int im_meta_get_double( IMAGE *, const char *field, double *d ); +int im_meta_set_area( IMAGE *, const char *field, im_callback_fn, void * ); +int im_meta_get_area( IMAGE *, const char *field, void **data ); +int im_meta_set_string( IMAGE *, const char *field, const char *str ); +int im_meta_get_string( IMAGE *, const char *field, char **str ); +int im_meta_set_blob( IMAGE *im, const char *field, + im_callback_fn free_fn, void *blob, size_t blob_length ); +int im_meta_get_blob( IMAGE *im, const char *field, + void **blob, size_t *blob_length ); + +/* Internal. + */ +void im__meta_init_types( void ); +void im__meta_destroy( IMAGE *im ); +int im__meta_cp( IMAGE *, const IMAGE * ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*!IM_META_H*/ diff --git a/include/vips/mosaic.h b/include/vips/mosaic.h new file mode 100644 index 00000000..dc2d26a8 --- /dev/null +++ b/include/vips/mosaic.h @@ -0,0 +1,86 @@ +/* @(#) Local definitions used by the mosaicing program + * @(#) If MAXPOINTS change please ensure that it is still a multiple of + * @(#) AREAS or else AREAS must change as well. Initial setup is for + * @(#) MAXPOINTS = 60, AREAS = 3. + * @(#) + * Copyright: 1990, 1991 N. Dessipris + * Author: Nicos Dessipris + * Written on: 07/11/1989 + * Modified on : 29/11/1989 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_MOSAIC_H +#define IM_MOSAIC_H + +#define MAXPOINTS 60 /* MAXPOINTS % AREAS (in im_calcon) must be zero */ + +typedef struct { + char *reference; /* filename of reference */ + char *secondary; /* filename of secondary */ + int deltax; /* initial estimate of displacement */ + int deltay; /* initial estimate of displacement */ + int nopoints; /* must be multiple of AREAS and <= MAXPOINTS */ + int halfcorsize; /* recommended 5 */ + int halfareasize; /* recommended 8 */ + + /* x, y_reference and contrast found by im_calcon() + */ + int x_reference[MAXPOINTS], y_reference[MAXPOINTS]; + int contrast[MAXPOINTS]; + + /* x, y_secondary and correlation set by im_chkpair() + */ + int x_secondary[MAXPOINTS], y_secondary[MAXPOINTS]; + + /* returns the corrected best correlation + * as detected in 2*halfareasize+1 + * centered at point (x2, y2) and using + * correlation area 2*halfareasize+1 + */ + double correlation[MAXPOINTS]; + + /* Coefficients calculated by im_clinear() + */ + double l_scale, l_angle, l_deltax, l_deltay; + + /* used by im_clinear() + */ + double dx[MAXPOINTS], dy[MAXPOINTS]; + double deviation[MAXPOINTS]; +} TIE_POINTS; + +int im_clinear( TIE_POINTS *points ); +int im__chkpair( IMAGE *, IMAGE *, TIE_POINTS *point ); +int im__initialize( TIE_POINTS *points ); +int im__improve( TIE_POINTS *inpoints, TIE_POINTS *outpoints ); +int im__avgdxdy( TIE_POINTS *points, int *dx, int *dy ); +int im__lrcalcon( IMAGE *ref, TIE_POINTS *points ); +int im__tbcalcon( IMAGE *ref, TIE_POINTS *points ); + +#endif /*IM_MOSAIC_H*/ diff --git a/include/vips/proto.h b/include/vips/proto.h new file mode 100644 index 00000000..2f1060b0 --- /dev/null +++ b/include/vips/proto.h @@ -0,0 +1,749 @@ +/* @(#) Header file for Birkbeck/VIPS Image Processing Library + * Authors: N. Dessipris, K. Martinez, Birkbeck College, London. + * and J. Cupitt The National Gallery, London. + * + * Sept 94 + * + * 15/7/96 JC + * - now does C++ extern stuff + * - many more protos + * 15/4/97 JC + * - protos split out here, more of them + * - still not complete tho' ... + * 8/4/99 JC + * - lots of consts added to please C++ + * - and more protos added + * 11/9/06 + * - internal protos cut out to help SWIG + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_PROTO_H +#define IM_PROTO_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Need these for some protos. + */ +#include +#include +#include +#include + +/* If we're being parsed by SWIG, remove gcc attributes. + */ +#ifdef SWIG +# ifndef __attribute__ +# define __attribute__(x) /*NOTHING*/ +# endif +#endif /*SWIG*/ + +typedef int (*im_callback_fn)( void *, void * ); +typedef void *(*im_construct_fn)( void *, void *, void * ); +typedef void *(*im_header_map_fn)( IMAGE *, const char *, GValue *, void * ); + +/* iofuncs + */ +int im_init_world( const char *argv0 ); +GOptionGroup *im_get_option_group( void ); + +const char *im_error_buffer( void ); +int im_debugim( IMAGE * ); +int im_printlines( IMAGE * ); + +int im_header_int( IMAGE *im, const char *field, int *out ); +int im_header_double( IMAGE *im, const char *field, double *out ); +int im_header_string( IMAGE *im, const char *field, char **out ); +GType im_header_get_type( IMAGE *im, const char *field ); +int im_header_get( IMAGE *im, const char *field, GValue *value_copy ); +void *im_header_map( IMAGE *im, im_header_map_fn fn, void *a ); + +const char *im_version_string( void ); +int im_version( int flag ); +const char *im_guess_prefix( const char *, const char * ); +IMAGE *im_init( const char * ); +IMAGE *im_openout( const char * ); +int im_openin( IMAGE *image ); +int im_openinrw( IMAGE *image ); +IMAGE *im_setbuf( const char * ); +IMAGE *im_partial( const char * ); +IMAGE *im_binfile( const char *, int, int, int, int ); +IMAGE *im_image( void *, int, int, int, int ); + +int im_mapfile( IMAGE * ); +int im_mapfilerw( IMAGE * ); +int im_remapfilerw( IMAGE *image ); + +IMAGE *im_open( const char *, const char * ); +IMAGE *im_open_header( const char * ); +int im_image_sanity( IMAGE * ); + +void *im_malloc( IMAGE *im, size_t sz ); +int im_free( void * ); + +int im_close( IMAGE * ); +int im_rwcheck( IMAGE * ); +int im_iocheck( IMAGE *, IMAGE * ); +int im_incheck( IMAGE * ); +int im_outcheck( IMAGE * ); +int im_piocheck( IMAGE *, IMAGE * ); +int im_pincheck( IMAGE * ); +int im_poutcheck( IMAGE * ); +int im_cp_desc( IMAGE *, IMAGE * ); +int im_cp_descv( IMAGE *out, IMAGE *in1, ... ) + __attribute__((sentinel)); +int im_cp_desc_array( IMAGE *out, IMAGE *in[] ); +int im_setupout( IMAGE * ); +int im_writeline( int, IMAGE *, PEL * ); + +int im_isuint( IMAGE * ); +int im_isint( IMAGE * ); +int im_isfloat( IMAGE * ); +int im_isscalar( IMAGE * ); +int im_iscomplex( IMAGE * ); +int im_isfile( IMAGE * ); +int im_ispartial( IMAGE * ); +int im_isMSBfirst( IMAGE * ); +int im_amiMSBfirst( void ); + +int im_ispoweroftwo( int ); + +int im_existsf( const char *name, ... ) + __attribute__((format(printf, 1, 2))); +int im_istiff( const char * ); +int im_istifftiled( const char * ); +int im_istiffpyramid( const char * ); +int im_isjpeg( const char * ); +int im_isvips( const char * ); +int im_isexr( const char * ); +int im_isppm( const char * ); +int im_ispng( const char * ); +int im_ismagick( const char * ); +int im_isanalyze( const char *filename ); + +int im_add_close_callback( IMAGE *, im_callback_fn, void *, void * ); +int im_add_eval_callback( IMAGE *, im_callback_fn, void *, void * ); +int im_add_evalend_callback( IMAGE *, im_callback_fn, void *, void * ); + +void error_exit( const char *, ... ) + __attribute__((noreturn, format(printf, 1, 2))); +void im_error_clear( void ); +void im_verror( const char *domain, const char *fmt, va_list ap ); +void im_error( const char *domain, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +void im_error_system( int err, const char *domain, const char *fmt, ... ) + __attribute__((format(printf, 3, 4))); +void im_warn( const char *domain, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +void im_diag( const char *domain, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); + +int im_bits_of_fmt( int ); +const char *im_Type2char( int ); +const char *im_BandFmt2char( int ); +const char *im_Coding2char( int ); +const char *im_Compression2char( int ); +const char *im_dhint2char( im_demand_type ); +const char *im_dtype2char( im_desc_type ); +int im_char2Type( const char * ); +int im_char2BandFmt( const char * ); +int im_char2Coding( const char * ); +int im_char2Compression( const char * ); + +int im_unmapfile( IMAGE * ); +void im_printdesc( IMAGE * ); +void im_initdesc( IMAGE *, + int, int, int, int, int, int, int, float, float, + int, int ); +int im_histlin( IMAGE *image, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +int im_updatehist( IMAGE *out, const char *name, int argc, char *argv[] ); +const char *im_history_get( IMAGE *im ); + +int im_render( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, + void (*notify)( IMAGE *, Rect *, void * ), void *client ); +int im_render_fade( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, + int fps, int steps, + int priority, + void (*notify)( IMAGE *, Rect *, void * ), void *client ); +int im_cache( IMAGE *in, IMAGE *out, int width, int height, int max ); + +/* morphology + */ +int im_dilate( IMAGE *in, IMAGE *out, INTMASK *m ); +int im_dilate_raw( IMAGE *in, IMAGE *out, INTMASK *m ); +int im_erode( IMAGE *in, IMAGE *out, INTMASK *m ); +int im_erode_raw( IMAGE *in, IMAGE *out, INTMASK *m ); +int im_cntlines( IMAGE *im, double *nolines, int flag ); +int im_profile( IMAGE *in, IMAGE *out, int dir ); + +/* convolution + */ +void im_copy_dmask_matrix( DOUBLEMASK *mask, double **matrix ); +void im_copy_matrix_dmask( double **matrix, DOUBLEMASK *mask ); +INTMASK *im_create_imask( const char *, int, int ); +INTMASK *im_create_imaskv( const char *, int, int, ... ); +DOUBLEMASK *im_create_dmask( const char *, int, int ); +DOUBLEMASK *im_create_dmaskv( const char *, int, int, ... ); +INTMASK *im_dup_imask( INTMASK *, const char * ); +DOUBLEMASK *im_dup_dmask( DOUBLEMASK *, const char * ); +int im_free_imask( INTMASK * ); +int im_free_dmask( DOUBLEMASK * ); +INTMASK *im_read_imask( const char * ); +DOUBLEMASK *im_read_dmask( const char * ); +void im_print_imask( INTMASK * ); +void im_print_dmask( DOUBLEMASK * ); +int im_write_imask( INTMASK * ); +int im_write_dmask( DOUBLEMASK * ); +int im_write_imask_name( INTMASK *, const char * ); +int im_write_dmask_name( DOUBLEMASK *, const char * ); +INTMASK *im_scale_dmask( DOUBLEMASK *, const char * ); +void im_norm_dmask( DOUBLEMASK *mask ); +int *im_offsets45( int ); +int *im_offsets90( int ); +INTMASK *im_rotate_imask90( INTMASK *, const char * ); +INTMASK *im_rotate_imask45( INTMASK *, const char * ); +DOUBLEMASK *im_rotate_dmask90( DOUBLEMASK *, const char * ); +DOUBLEMASK *im_rotate_dmask45( DOUBLEMASK *, const char * ); +INTMASK *im_log_imask( const char *, double, double ); +DOUBLEMASK *im_log_dmask( const char *, double, double ); +INTMASK *im_gauss_imask( const char *, double, double ); +DOUBLEMASK *im_gauss_dmask( const char *, double, double ); + +int im_rank( IMAGE *, IMAGE *, int, int, int ); +int im_sharpen( IMAGE *, IMAGE *, int, double, double, double, double, double ); +int im_addgnoise( IMAGE *, IMAGE *, double ); +int im_gaussnoise( IMAGE *, int, int, double, double ); + +int im_zerox( IMAGE *, IMAGE *, int ); + +int im_maxvalue( IMAGE **in, IMAGE *out, int n ); +int im_rank_image( IMAGE **in, IMAGE *out, int n, int index ); +int im_compass( IMAGE *, IMAGE *, INTMASK * ); +int im_gradient( IMAGE *, IMAGE *, INTMASK * ); +int im_lindetect( IMAGE *, IMAGE *, INTMASK * ); +int im_conv( IMAGE *, IMAGE *, INTMASK * ); +int im_conv_raw( IMAGE *, IMAGE *, INTMASK * ); +int im_convf( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_convf_raw( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_convsep( IMAGE *, IMAGE *, INTMASK * ); +int im_convsep_raw( IMAGE *, IMAGE *, INTMASK * ); +int im_convsepf( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_convsepf_raw( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_convsub( IMAGE *, IMAGE *, INTMASK *, int, int ); + +int im_grad_x( IMAGE *in, IMAGE *out ); +int im_grad_y( IMAGE *in, IMAGE *out ); + +int im_fastcor( IMAGE *, IMAGE *, IMAGE * ); +int im_fastcor_raw( IMAGE *, IMAGE *, IMAGE * ); +int im_spcor( IMAGE *, IMAGE *, IMAGE * ); +int im_spcor_raw( IMAGE *, IMAGE *, IMAGE * ); +int im_spcor2( IMAGE *, IMAGE *, IMAGE * ); +int im_spcor2_raw( IMAGE *, IMAGE *, IMAGE * ); +int im_gradcor( IMAGE *, IMAGE *, IMAGE * ); +int im_gradcor_raw( IMAGE *, IMAGE *, IMAGE * ); +int im_contrast_surface( IMAGE *, IMAGE *, int, int ); +int im_contrast_surface_raw( IMAGE *, IMAGE *, int, int ); + +int im_resize_linear( IMAGE *, IMAGE *, int, int ); +int im_mpercent( IMAGE *, double, int * ); +int im_shrink( IMAGE *, IMAGE *, double, double ); +int im_embed( IMAGE *, IMAGE *, int, int, int, int, int ); + +int im_stretch3( IMAGE *in, IMAGE *out, double dx, double dy ); +int im_rank_raw( IMAGE *in, IMAGE *out, int xsize, int ysize, int n ); + +/* freq_filt + */ +int im_fractsurf( IMAGE *out, int size, double frd ); +int im_freqflt( IMAGE *, IMAGE *, IMAGE * ); +int im_disp_ps( IMAGE *, IMAGE * ); +int im_rotquad( IMAGE *, IMAGE * ); +int im_fwfft( IMAGE *, IMAGE * ); +int im_invfft( IMAGE *, IMAGE * ); +int im_invfftr( IMAGE *, IMAGE * ); + +/* arithmetic + */ +DOUBLEMASK *im_measure( IMAGE *, IMAGE_BOX *, + int, int, int *, int, const char * ); +DOUBLEMASK *im_stats( IMAGE * ); +int im_abs( IMAGE *in, IMAGE *out ); +int im_max( IMAGE *in, double *out ); +int im_min( IMAGE *in, double *out ); +int im_avg( IMAGE *in, double *out ); +int im_deviate( IMAGE *in, double *out ); +int im_maxpos( IMAGE *in, int *xpos, int *ypos, double *out ); +int im_minpos( IMAGE *in, int *xpos, int *ypos, double *out ); +int im_maxpos_avg( IMAGE *im, double *xpos, double *ypos, double *out ); +int im_maxpos_vec( IMAGE *im, int *xpos, int *ypos, double *maxima, int n ); +int im_minpos_vec( IMAGE *im, int *xpos, int *ypos, double *minima, int n ); +int im_add( IMAGE *, IMAGE *, IMAGE * ); +int im_subtract( IMAGE *, IMAGE *, IMAGE * ); +int im_invert( IMAGE *, IMAGE * ); +int im_linreg( IMAGE **ins, IMAGE *out, double *xs ); +int im_lintra( double, IMAGE *, double, IMAGE * ); +int im_lintra_vec( int n, double *a, IMAGE *in, double *b, IMAGE *out ); +int im_multiply( IMAGE *, IMAGE *, IMAGE * ); +int im_divide( IMAGE *, IMAGE *, IMAGE * ); +int im_point_bilinear( IMAGE *im, double x, double y, int band, double *val ); +int im_powtra( IMAGE *, IMAGE *, double ); +int im_powtra_vec( IMAGE *in, IMAGE *out, int n, double *e ); +int im_exptra( IMAGE *, IMAGE * ); +int im_exp10tra( IMAGE *, IMAGE * ); +int im_expntra( IMAGE *, IMAGE *, double ); +int im_expntra_vec( IMAGE *in, IMAGE *out, int n, double *e ); +int im_logtra( IMAGE *, IMAGE * ); +int im_log10tra( IMAGE *, IMAGE * ); +int im_remainder( IMAGE *, IMAGE *, IMAGE * ); +int im_remainderconst( IMAGE *, IMAGE *, double ); +int im_remainderconst_vec( IMAGE *, IMAGE *, int, double * ); +int im_floor( IMAGE *, IMAGE * ); +int im_rint( IMAGE *, IMAGE * ); +int im_ceil( IMAGE *, IMAGE * ); +int im_sintra( IMAGE *, IMAGE * ); +int im_sign( IMAGE *in, IMAGE *out ); +int im_costra( IMAGE *, IMAGE * ); +int im_tantra( IMAGE *, IMAGE * ); +int im_asintra( IMAGE *, IMAGE * ); +int im_acostra( IMAGE *, IMAGE * ); +int im_atantra( IMAGE *, IMAGE * ); +int im_cmulnorm( IMAGE *, IMAGE *, IMAGE * ); +int im_fav4( IMAGE **, IMAGE * ); +int im_gadd( double, IMAGE *, double, IMAGE *, double, IMAGE *); +int im_litecor( IMAGE *, IMAGE *, IMAGE *, int, double ); +int im_bandmean( IMAGE *in, IMAGE *out ); + +/* boolean + */ +int im_andimage( IMAGE *, IMAGE *, IMAGE * ); +int im_andconst( IMAGE *, IMAGE *, double ); +int im_and_vec( IMAGE *, IMAGE *, int, double * ); +int im_orimage( IMAGE *, IMAGE *, IMAGE * ); +int im_orconst( IMAGE *, IMAGE *, double ); +int im_or_vec( IMAGE *, IMAGE *, int, double * ); +int im_eorimage( IMAGE *, IMAGE *, IMAGE * ); +int im_eorconst( IMAGE *, IMAGE *, double ); +int im_eor_vec( IMAGE *, IMAGE *, int, double * ); +int im_shiftleft( IMAGE *, IMAGE *, int ); +int im_shiftright( IMAGE *, IMAGE *, int ); + +/* histogram + */ +int im_maplut( IMAGE *, IMAGE *, IMAGE * ); +int im_gammacorrect( IMAGE *, IMAGE *, double ); +int im_heq( IMAGE *in, IMAGE *out, int bandno ); +int im_hist( IMAGE *in, IMAGE *out, int bandno ); +int im_histeq( IMAGE *in, IMAGE *out ); +int im_histnorm( IMAGE *in, IMAGE *out ); +int im_histcum( IMAGE *in, IMAGE *out ); +int im_histgr( IMAGE *in, IMAGE *out, int bandno ); +int im_histnD( IMAGE *in, IMAGE *out, int bins ); +int im_histplot( IMAGE *hist, IMAGE *histplot ); +int im_histspec( IMAGE *hin, IMAGE *href, IMAGE *lut ); +int im_hsp( IMAGE *in, IMAGE *ref, IMAGE *out ); +int im_identity( IMAGE *lut, int bands ); +int im_identity_ushort( IMAGE *lut, int bands, int sz ); +int im_lhisteq( IMAGE *in, IMAGE *out, int xwin, int ywin ); +int im_lhisteq_raw( IMAGE *in, IMAGE *out, int xwin, int ywin ); +int im_invertlut( DOUBLEMASK *input, IMAGE *output, int lut_size ); +int im_buildlut( DOUBLEMASK *input, IMAGE *output ); +int im_stdif( IMAGE *in, IMAGE *out, + double a, double m0, double b, double s0, int xwin, int ywin ); +int im_stdif_raw( IMAGE *in, IMAGE *out, + double a, double m0, double b, double s0, int xwin, int ywin ); +int im_tone_build_range( IMAGE *out, + int in_max, int out_max, + double Lb, double Lw, double Ps, double Pm, double Ph, + double S, double M, double H ); +int im_tone_build( IMAGE *out, + double Lb, double Lw, double Ps, double Pm, double Ph, + double S, double M, double H ); +int im_tone_analyse( IMAGE *in, IMAGE *lut, + double Ps, double Pm, double Ph, double S, double M, double H ); +int im_ismonotonic( IMAGE *lut, int *out ); +int im_tone_map( IMAGE *in, IMAGE *out, IMAGE *lut ); +int im_project( IMAGE *in, IMAGE *hout, IMAGE *vout ); + +/* conversion + */ + +/* Copy and swap types. + */ +typedef enum { + IM_ARCH_NATIVE, + IM_ARCH_BYTE_SWAPPED, + IM_ARCH_LSB_FIRST, + IM_ARCH_MSB_FIRST +} im_arch_type; + +DOUBLEMASK *im_vips2mask( IMAGE *, const char * ); +int im_mask2vips( DOUBLEMASK *, IMAGE * ); +int im_copy_set( IMAGE *, IMAGE *, int, float, float, int, int ); +int im_copy_set_meta( IMAGE *in, IMAGE *out, const char *field, GValue *meta ); +int im_copy_morph( IMAGE *, IMAGE *, int, int, int ); +int im_copy( IMAGE *, IMAGE * ); +int im_copy_swap( IMAGE *in, IMAGE *out ); +int im_copy_from( IMAGE *in, IMAGE *out, im_arch_type architecture ); +int im_extract( IMAGE *, IMAGE *, IMAGE_BOX * ); +int im_extract_band( IMAGE *in, IMAGE *out, int band ); +int im_extract_bands( IMAGE *in, IMAGE *out, int band, int nbands ); +int im_extract_area( IMAGE *in, IMAGE *out, int x, int y, int w, int h ); +int im_extract_areabands( IMAGE *in, IMAGE *out, + int left, int top, int width, int height, int band, int nbands ); +int im_subsample( IMAGE *, IMAGE *, int, int ); +int im_zoom( IMAGE *, IMAGE *, int, int ); +int im_bandjoin( IMAGE *, IMAGE *, IMAGE * ); +int im_gbandjoin( IMAGE **, IMAGE *, int ); +int im_black( IMAGE *, int, int, int ); +int im_text( IMAGE *out, const char *text, const char *font, + int width, int alignment, int dpi ); +int im_c2amph( IMAGE *, IMAGE * ); +int im_c2rect( IMAGE *, IMAGE * ); +int im_clip2fmt( IMAGE *in, IMAGE *out, int ofmt ); +int im_clip2dcm( IMAGE *, IMAGE * ); +int im_clip2cm( IMAGE *, IMAGE * ); +int im_clip2us( IMAGE *, IMAGE * ); +int im_clip2ui( IMAGE *, IMAGE * ); +int im_clip2s( IMAGE *, IMAGE * ); +int im_clip2i( IMAGE *, IMAGE * ); +int im_clip2d( IMAGE *, IMAGE * ); +int im_clip2f( IMAGE *, IMAGE * ); +int im_clip2c( IMAGE *, IMAGE * ); +int im_clip( IMAGE *, IMAGE * ); +int im_ri2c( IMAGE *, IMAGE *, IMAGE * ); +int im_c2imag( IMAGE *, IMAGE * ); +int im_c2real( IMAGE *, IMAGE * ); +int im_c2ps( IMAGE *, IMAGE * ); +int im_fliphor( IMAGE *, IMAGE * ); +int im_flipver( IMAGE *, IMAGE * ); +int im_falsecolour( IMAGE *, IMAGE * ); +int im_recomb( IMAGE *, IMAGE *, DOUBLEMASK * ); +int im_insert( IMAGE *, IMAGE *, IMAGE *, int, int ); +int im_insert_noexpand( IMAGE *, IMAGE *, IMAGE *, int, int ); +int im_rot90( IMAGE *, IMAGE * ); +int im_rot180( IMAGE *, IMAGE * ); +int im_rot270( IMAGE *, IMAGE * ); +int im_lrjoin( IMAGE *, IMAGE *, IMAGE * ); +int im_tbjoin( IMAGE *, IMAGE *, IMAGE * ); +int im_scale( IMAGE *, IMAGE * ); +int im_scaleps( IMAGE *, IMAGE * ); +int im_slice( IMAGE *, IMAGE *, double, double ); +int im_system( IMAGE *im, const char *cmd, char **out ); +int im_print( const char *message ); +int im_thresh( IMAGE *, IMAGE *, double ); +int im_jpeg2vips( const char *, IMAGE * ); +int im_jpeg2vips_header( const char *, IMAGE * ); +int im_vips2jpeg( IMAGE *, const char * ); +int im_vips2mimejpeg( IMAGE *, int ); +int im_vips2bufjpeg( IMAGE *, IMAGE *, int, char **, int * ); +int im_vips2tiff( IMAGE *, const char * ); +int im_bernd( const char *, int, int, int, int ); +int im_tiff2vips( const char *, IMAGE * ); +int im_tiff2vips_header( const char *, IMAGE * ); +int im_tile_cache( IMAGE *, IMAGE *, int, int, int ); +int im_magick2vips( const char *, IMAGE * ); +int im_magick2vips_header( const char *, IMAGE * ); +int im_png2vips( const char *, IMAGE * ); +int im_png2vips_header( const char *, IMAGE * ); +int im_exr2vips( const char *, IMAGE * ); +int im_exr2vips_header( const char *, IMAGE * ); +int im_ppm2vips( const char *, IMAGE * ); +int im_ppm2vips_header( const char *, IMAGE * ); +int im_vips2ppm( IMAGE *, const char * ); +int im_analyze2vips( const char *filename, IMAGE *out ); +int im_analyze2vips_header( const char *filename, IMAGE *out ); +int im_vips2csv( IMAGE *in, const char *filename ); +int im_csv2vips( const char *filename, IMAGE *out ); +int im_csv2vips_header( const char *filename, IMAGE *out ); +int im_vips2png( IMAGE *, const char * ); +int im_raw2vips( const char *filename, IMAGE *out, + int width, int height, int bpp, int offset ); +int im_replicate( IMAGE *in, IMAGE *out, int across, int down ); +int im_grid( IMAGE *in, IMAGE *out, int tile_height, int across, int down ); +int im_msb ( IMAGE * in, IMAGE * out ); +int im_msb_band ( IMAGE * in, IMAGE * out, int band ); + +/* colour + */ +int im_Lab2LCh( IMAGE *, IMAGE * ); +int im_LCh2Lab( IMAGE *, IMAGE * ); +int im_LabQ2XYZ( IMAGE *, IMAGE * ); +int im_LCh2UCS( IMAGE *, IMAGE * ); +int im_Lab2LCh( IMAGE *, IMAGE * ); +int im_Lab2LabQ( IMAGE *, IMAGE * ); +int im_Lab2LabS( IMAGE *, IMAGE * ); +int im_Lab2XYZ( IMAGE *, IMAGE * ); +int im_Lab2XYZ_temp( IMAGE *, IMAGE *, double X0, double Y0, double Z0 ); +int im_Lab2UCS( IMAGE *, IMAGE * ); +int im_LabQ2Lab( IMAGE *, IMAGE * ); +int im_LabQ2LabS( IMAGE *, IMAGE * ); +int im_LabS2LabQ( IMAGE *, IMAGE * ); +int im_LabS2Lab( IMAGE *, IMAGE * ); +int im_UCS2XYZ( IMAGE *, IMAGE * ); +int im_UCS2LCh( IMAGE *, IMAGE * ); +int im_UCS2Lab( IMAGE *, IMAGE * ); +int im_XYZ2Lab( IMAGE *, IMAGE * ); +int im_XYZ2Lab_temp( IMAGE *, IMAGE *, double X0, double Y0, double Z0 ); +int im_XYZ2UCS( IMAGE *, IMAGE * ); +int im_sRGB2XYZ( IMAGE *, IMAGE * ); +int im_XYZ2sRGB( IMAGE *, IMAGE * ); +int im_Yxy2XYZ( IMAGE *, IMAGE * ); +int im_XYZ2Yxy( IMAGE *, IMAGE * ); + +int im_dECMC_fromLab( IMAGE *, IMAGE *, IMAGE * ); +int im_dE_fromXYZ( IMAGE *, IMAGE *, IMAGE * ); +int im_dE_fromLab( IMAGE *, IMAGE *, IMAGE * ); + +void imb_Lab2LCh( float *, float *, int ); +void imb_LCh2Lab( float *, float *, int ); +void imb_XYZ2Lab_tables( void ); +void imb_XYZ2Lab( float *, float *, int, im_colour_temperature * ); +void imb_Lab2XYZ( float *, float *, int, im_colour_temperature * ); +void imb_LabQ2Lab( PEL *, float *, int ); +void imb_Lab2LabQ( float *, PEL *, int ); +void imb_LabS2Lab( signed short *, float *, int ); +void imb_Lab2LabS( float *, signed short *, int n ); + +void im_col_make_tables_UCS( void ); + +float im_col_dECMC( float, float, float, float, float, float ); +float im_col_dE00( float, float, float, float, float, float ); + +int im_lab_morph( IMAGE *in, IMAGE *out, + DOUBLEMASK *mask, + double L_offset, double L_scale, + double a_scale, double b_scale ); + +/* other + */ +int im_feye( IMAGE *image, + const int xsize, const int ysize, const double factor ); +int im_eye( IMAGE *image, + const int xsize, const int ysize, const double factor ); +int im_zone( IMAGE *im, int size ); +int im_fzone( IMAGE *im, int size ); +int im_grey( IMAGE *im, const int xsize, const int ysize ); +int im_fgrey( IMAGE *im, const int xsize, const int ysize ); +int im_make_xy( IMAGE *out, const int xsize, const int ysize ); +int im_benchmarkn( IMAGE *in, IMAGE *out, int n ); +int im_benchmark2( IMAGE *in, double *out ); + +int im_cooc_matrix( IMAGE *im, IMAGE *m, + int xp, int yp, int xs, int ys, int dx, int dy, int flag ); +int im_cooc_asm( IMAGE *m, double *asmoment ); +int im_cooc_contrast( IMAGE *m, double *contrast ); +int im_cooc_correlation( IMAGE *m, double *correlation ); +int im_cooc_entropy( IMAGE *m, double *entropy ); + +int im_glds_matrix( IMAGE *im, IMAGE *m, + int xpos, int ypos, int xsize, int ysize, int dx, int dy ); +int im_glds_asm( IMAGE *m, double *asmoment ); +int im_glds_contrast( IMAGE *m, double *contrast ); +int im_glds_entropy( IMAGE *m, double *entropy ); +int im_glds_mean( IMAGE *m, double *mean ); + +int im_simcontr( IMAGE *image, int xs, int ys ); +int im_sines( IMAGE *image, + int xsize, int ysize, double horfreq, double verfreq ); +int im_spatres( IMAGE *in, IMAGE *out, int step ); + +int im_rightshift_size( IMAGE *in, IMAGE *out, int xshift, int yshift, int band_fmt ); + +/* mosaicing + */ +int im_lrmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, + int dx, int dy, int mwidth ); +int im_tbmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, + int dx, int dy, int mwidth ); + +int im_lrmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ); +int im_tbmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ); + +int im_lrmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ); +int im_tbmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ); + +int im_lrmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ); +int im_tbmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ); + +int im_global_balance( IMAGE *in, IMAGE *out, double gamma ); +int im_global_balancef( IMAGE *in, IMAGE *out, double gamma ); + +int im_match_linear( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2 ); +int im_match_linear_search( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int hwindowsize, int hsearchsize ); + +int im_affine( IMAGE *in, IMAGE *out, + double a, double b, double c, double d, double dx, double dy, + int ox, int oy, int ow, int oh ); +int im_similarity( IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy ); +int im_similarity_area( IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy, + int ox, int oy, int ow, int oh ); +int im_correl( IMAGE *ref, IMAGE *sec, + int xref, int yref, int xsec, int ysec, + int hwindowsize, int hsearchsize, + double *correlation, int *x, int *y ); +int im_remosaic( IMAGE *in, IMAGE *out, + const char *old_str, const char *new_str ); + +/* inplace + */ +int im_plotmask( IMAGE *, int, int, PEL *, PEL *, Rect * ); +int im_smear( IMAGE *, int, int, Rect * ); +int im_smudge( IMAGE *, int, int, Rect * ); +int im_paintrect( IMAGE *, Rect *, PEL * ); +int im_circle( IMAGE *, int, int, int, int ); +int im_insertplace( IMAGE *, IMAGE *, int, int ); +int im_line( IMAGE *, int, int, int, int, int ); +int im_fastlineuser(); +int im_readpoint( IMAGE *, int, int, PEL * ); +int im_flood( IMAGE *, int, int, PEL *, Rect * ); +int im_flood_blob( IMAGE *, int, int, PEL *, Rect * ); +int im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink ); +int im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink, + int n, int *x1v, int *y1v, int *x2v, int *y2v ); + +/* relational + */ +int im_equal( IMAGE *, IMAGE *, IMAGE * ); +int im_equalconst( IMAGE *, IMAGE *, double ); +int im_equal_vec( IMAGE *, IMAGE *, int, double * ); +int im_notequal( IMAGE *, IMAGE *, IMAGE * ); +int im_notequalconst( IMAGE *, IMAGE *, double ); +int im_notequal_vec( IMAGE *, IMAGE *, int, double * ); +int im_more( IMAGE *, IMAGE *, IMAGE * ); +int im_moreconst( IMAGE *, IMAGE *, double ); +int im_more_vec( IMAGE *, IMAGE *, int, double * ); +int im_less( IMAGE *, IMAGE *, IMAGE * ); +int im_lessconst( IMAGE *, IMAGE *, double ); +int im_less_vec( IMAGE *, IMAGE *, int, double * ); +int im_moreeq( IMAGE *, IMAGE *, IMAGE * ); +int im_moreeqconst( IMAGE *, IMAGE *, double ); +int im_moreeq_vec( IMAGE *, IMAGE *, int, double * ); +int im_lesseq( IMAGE *, IMAGE *, IMAGE * ); +int im_lesseqconst( IMAGE *, IMAGE *, double ); +int im_lesseq_vec( IMAGE *, IMAGE *, int, double * ); +int im_ifthenelse( IMAGE *, IMAGE *, IMAGE *, IMAGE * ); +int im_blend( IMAGE *, IMAGE *, IMAGE *, IMAGE * ); + +/* matrix + */ +DOUBLEMASK *im_mattrn( DOUBLEMASK *, const char * ); +DOUBLEMASK *im_matcat( DOUBLEMASK *, DOUBLEMASK *, const char * ); +DOUBLEMASK *im_matmul( DOUBLEMASK *, DOUBLEMASK *, const char * ); + +DOUBLEMASK *im_lu_decomp( const DOUBLEMASK *mat, const char *name ); +int im_lu_solve( const DOUBLEMASK *lu, double *vec ); +DOUBLEMASK *im_matinv( const DOUBLEMASK *mat, const char *name ); +int im_matinv_inplace( DOUBLEMASK *mat ); + + +int *im_ivector(); +float *im_fvector(); +double *im_dvector(); +void im_free_ivector(); +void im_free_fvector(); +void im_free_dvector(); + +int **im_imat_alloc(); +float **im_fmat_alloc(); +double **im_dmat_alloc(); +void im_free_imat(); +void im_free_fmat(); +void im_free_dmat(); + +int im_invmat( double **, int ); + +/* video + */ +int im_video_v4l1( IMAGE *im, const char *device, + int channel, int brightness, int colour, int contrast, int hue, + int ngrabs ); +int im_video_test( IMAGE *im, int brightness, int error ); + +/* Backwards compatibility macros. + */ +#define im_clear_error_string() im_error_clear() +#define im_errorstring() im_error_buffer() + +/* Deprecated API. + */ +void im_errormsg( const char *fmt, ... ) + __attribute__((format(printf, 1, 2))); +void im_verrormsg( const char *fmt, va_list ap ); +void im_errormsg_system( int err, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +void im_diagnostics( const char *fmt, ... ) + __attribute__((format(printf, 1, 2))); +void im_warning( const char *fmt, ... ) + __attribute__((format(printf, 1, 2))); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_PROTO_H*/ diff --git a/include/vips/r_access.h b/include/vips/r_access.h new file mode 100644 index 00000000..1a1cebc7 --- /dev/null +++ b/include/vips/r_access.h @@ -0,0 +1,85 @@ +/* r_access.h + * + * 2006-09-21 tcv + * random access to images and regions + */ + +#ifndef IM_R_ACCESS_H +#define IM_R_ACCESS_H + +#include + + +/** ARRAY MEMBER MACROS **/ +/* these are local */ + +#define IM__TYPE_FROM_ARRAY(type,vptr,i) ( ((type*) (vptr))[i] ) + +#define IM__CHAR_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( gint8, (vptr), (i) ) +#define IM__UCHAR_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( guint8, (vptr), (i) ) +#define IM__SHORT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( gint16, (vptr), (i) ) +#define IM__USHORT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( guint16, (vptr), (i) ) +#define IM__INT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( gint32, (vptr), (i) ) +#define IM__UINT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( guint32, (vptr), (i) ) +#define IM__FLOAT_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( float, (vptr), (i) ) +#define IM__DOUBLE_FROM_ARRAY(vptr,i) IM__TYPE_FROM_ARRAY( double, (vptr), (i) ) + +#define IM__VALUE_FROM_ARRAY(band_fmt,vptr,i) ( \ + ( IM_BANDFMT_DOUBLE == (band_fmt) ) ? IM__DOUBLE_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_FLOAT == (band_fmt) ) ? IM__FLOAT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_INT == (band_fmt) ) ? IM__INT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_UINT == (band_fmt) ) ? IM__UINT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_SHORT == (band_fmt) ) ? IM__SHORT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_USHORT == (band_fmt) ) ? IM__USHORT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_CHAR == (band_fmt) ) ? IM__CHAR_FROM_ARRAY( (vptr), (i) ) \ + : IM__UCHAR_FROM_ARRAY( (vptr), (i) ) ) + +#define IM__ARRAY_ASSIGNMENT(band_fmt,vptr,i,val) ( \ + ( IM_BANDFMT_DOUBLE == (band_fmt) ) ? ( IM__DOUBLE_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_FLOAT == (band_fmt) ) ? ( IM__FLOAT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_INT == (band_fmt) ) ? ( IM__INT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_UINT == (band_fmt) ) ? ( IM__UINT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_SHORT == (band_fmt) ) ? ( IM__SHORT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_USHORT == (band_fmt) ) ? ( IM__USHORT_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM_BANDFMT_CHAR == (band_fmt) ) ? ( IM__CHAR_FROM_ARRAY( (vptr), (i) )= (val) ) \ + : ( IM__UCHAR_FROM_ARRAY( (vptr), (i) )= (val) ) ) + +#define IM__ARRAY_INCREMENT(band_fmt,vptr,i,val) ( \ + ( IM_BANDFMT_DOUBLE == (band_fmt) ) ? ( IM__DOUBLE_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_FLOAT == (band_fmt) ) ? ( IM__FLOAT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_INT == (band_fmt) ) ? ( IM__INT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_UINT == (band_fmt) ) ? ( IM__UINT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_SHORT == (band_fmt) ) ? ( IM__SHORT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_USHORT == (band_fmt) ) ? ( IM__USHORT_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM_BANDFMT_CHAR == (band_fmt) ) ? ( IM__CHAR_FROM_ARRAY( (vptr), (i) )+= (val) ) \ + : ( IM__UCHAR_FROM_ARRAY( (vptr), (i) )+= (val) ) ) + + +/** IMAGE MEMBER MACROS **/ +/* export these */ + +#define IM_IMAGE_VALUE(im,x,y,band) IM__VALUE_FROM_ARRAY( (im)-> BandFmt, \ + IM_IMAGE_ADDR( (im), (x), (y) ), (band) ) + +#define IM_IMAGE_ASSIGNMENT(im,x,y,band,val) IM__ARRAY_ASSIGNMENT( (im)-> BandFmt, \ + IM_IMAGE_ADDR( (im), (x), (y) ), (band), (val) ) + +#define IM_IMAGE_INCREMENT(im,x,y,band,val) IM__ARRAY_INCREMENT( (im)-> BandFmt, \ + IM_IMAGE_ADDR( (im), (x), (y) ), (band), (val) ) + + +/** REGION MEMBER MACROS **/ +/* export these */ + +#define IM_REGION_VALUE(reg,x,y,band) IM__VALUE_FROM_ARRAY( (reg)-> im-> BandFmt, \ + IM_REGION_ADDR( (reg), (x), (y) ), (band) ) + +#define IM_REGION_ASSIGNMENT(reg,x,y,band,val) IM__ARRAY_ASSIGNMENT( (reg)-> im-> BandFmt, \ + IM_REGION_ADDR( (reg), (x), (y) ), (band), (val) ) + +#define IM_REGION_INCREMENT(reg,x,y,band,val) IM__ARRAY_INCREMENT( (reg)-> im-> BandFmt, \ + IM_REGION_ADDR( (reg), (x), (y) ), (band), (val) ) + + +#endif /* IM_R_ACCESS_H */ + diff --git a/include/vips/rect.h b/include/vips/rect.h new file mode 100644 index 00000000..8baebbb7 --- /dev/null +++ b/include/vips/rect.h @@ -0,0 +1,75 @@ +/* Simple rectangle algebra. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_RECT_H +#define IM_RECT_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* A rectangle. + */ +typedef struct im_rect_struct { + int left, top, width, height; +} Rect; + +/* Only define old, broken names if asked. + */ +#ifdef IM_ENABLE_DEPRECATED + +/* Useful macros. Compatibility ... see below for new names. + */ +#define right(R) ((R)->left + (R)->width) +#define bottom(R) ((R)->top + (R)->height) + +#endif /*IM_ENABLE_DEPRECATED*/ + +#define IM_RECT_RIGHT(R) ((R)->left + (R)->width) +#define IM_RECT_BOTTOM(R) ((R)->top + (R)->height) +#define IM_RECT_HCENTRE(R) ((R)->left + (R)->width / 2) +#define IM_RECT_VCENTRE(R) ((R)->top + (R)->height / 2) + +/* Rectangle algebra functions. + */ +void im_rect_marginadjust( Rect *r, int n ); +int im_rect_includespoint( Rect *r, int x, int y ); +int im_rect_includesrect( Rect *r1, Rect *r2 ); +void im_rect_intersectrect( Rect *r1, Rect *r2, Rect *r3 ); +int im_rect_isempty( Rect *r ); +void im_rect_unionrect( Rect *r1, Rect *r2, Rect *r3 ); +int im_rect_equalsrect( Rect *r1, Rect *r2 ); +Rect *im_rect_dup( Rect *r ); +void im_rect_normalise( Rect *r ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_RECT_H*/ diff --git a/include/vips/region.h b/include/vips/region.h new file mode 100644 index 00000000..a9897e40 --- /dev/null +++ b/include/vips/region.h @@ -0,0 +1,293 @@ +/* Definitions for partial image regions. + * + * J.Cupitt, 8/4/93 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_REGION_H +#define IM_REGION_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#ifdef TIME_THREAD +#include +#endif /*TIME_THREAD*/ + +#include "rect.h" + +/* Per-thread buffer cache. Held in a GPrivate. + */ +typedef struct im__buffer_cache_t { + GHashTable *hash; /* Hash to im_buffer_cache_list_t* */ + GThread *thread; /* Just for sanity checking */ +} im_buffer_cache_t; + +/* Per-image buffer cache. Hash to this from im_buffer_cache_t. + * We can't store the GSList directly in the hash table, as GHashTable lacks an + * update operation and we'd need to _remove() and _insert() on every list + * operation. + */ +typedef struct im__buffer_cache_list_t { + GSList *buffers; /* GSList of im_buffer_t* */ + GThread *thread; /* Just for sanity checking */ + IMAGE *im; + im_buffer_cache_t *cache; +} im_buffer_cache_list_t; + +/* What we track for each pixel buffer. + */ +typedef struct { + int ref_count; /* # of regions referencing us */ + IMAGE *im; /* IMAGE we are attached to */ + + Rect area; /* Area this pixel buffer covers */ + gboolean done; /* Calculated and in cache */ + im_buffer_cache_t *cache; + gboolean invalid; /* Needs to be recalculated */ + char *buf; /* Private malloc() area */ + size_t bsize; /* Size of private malloc() */ +} im_buffer_t; + +/* Region types. + */ +typedef enum region_type { + IM_REGION_NONE, + IM_REGION_BUFFER, /* a pixel buffer */ + IM_REGION_OTHER_REGION, /* memory on another region */ + IM_REGION_OTHER_IMAGE, /* memory on another image */ + IM_REGION_WINDOW /* mmap() buffer on fd on another image */ +} RegionType; + +/* Sub-area of image. + */ +typedef struct region_struct { + /* Users may read these two fields. + */ + IMAGE *im; /* Link back to parent image */ + Rect valid; /* Area of parent we can see */ + + /* The rest of REGION is private. + */ + RegionType type; /* What kind of attachment */ + char *data; /* Off here to get data */ + int bpl; /* Bytes-per-line for data */ + void *seq; /* Sequence we are using to fill region */ + + /* The thread that made this region. Used to assert() test that + * regions are not being shared between threads. + */ + GThread *thread; + + /* Ref to the window we use for this region, if any. + */ + im_window_t *window; + + /* Ref to the buffer we use for this region, if any. + */ + im_buffer_t *buffer; +} REGION; + +/* Private to iofuncs: the size of the `tiles' requested by im_generate() + * when acting as a data sink. + */ +#define IM__TILE_WIDTH (64) +#define IM__TILE_HEIGHT (64) + +/* The height of the strips for the other two request styles. + */ +#define IM__THINSTRIP_HEIGHT (1) +#define IM__FATSTRIP_HEIGHT (16) + +/* Functions on regions. + */ +void im__region_take_ownership( REGION *reg ); +void im__region_check_ownership( REGION *reg ); +void im__region_no_ownership( REGION *reg ); + +REGION *im_region_create( IMAGE *im ); +void im_region_free( REGION *reg ); +int im_region_buffer( REGION *reg, Rect *r ); +int im_region_image( REGION *reg, Rect *r ); +int im_region_region( REGION *reg, REGION *to, Rect *r, int x, int y ); +int im_region_equalsregion( REGION *reg1, REGION *reg2 ); +int im_region_position( REGION *reg1, int x, int y ); +typedef int (*im_region_fill_fn)( REGION *, void * ); +int im_region_fill( REGION *reg, Rect *r, im_region_fill_fn fn, void *a ); + +/* IMAGE functions which use regions. We do not strictly type the function + * arguments to avoid hassle. + */ +int im_prepare( REGION *reg, Rect *r ); +int im_prepare_many( REGION **reg, Rect *r ); +int im_prepare_to( REGION *reg, REGION *dest, Rect *r, int x, int y ); +int im_generate( IMAGE *im, + void *(*start_fn)(), int (*gen_fn)(), int (*stop_fn)(), + void *a, void *b +); +int im_iterate( IMAGE *im, + void *(*start_fn)(), int (*scan_fn)(), int (*stop_fn)(), + void *a, void *b +); +void im__copy_region( REGION *reg, REGION *dest, Rect *r, int x, int y ); + +/* Convenience functions for im_generate()/im_iterate(). + */ +void *im_start_one( IMAGE *out, IMAGE *in, void *dummy ); +int im_stop_one( REGION *reg, void *dummy1, void *dummy2 ); +void *im_start_many( IMAGE *out, IMAGE **in, void *dummy ); +int im_stop_many( REGION **out, void *dummy1, void *dummy2 ); +IMAGE **im_allocate_input_array( IMAGE *out, ... ); +int im_demand_hint( IMAGE *im, im_demand_type hint, ... ) + __attribute__((sentinel)); +int im_demand_hint_array( IMAGE *im, im_demand_type hint, IMAGE **in ); +void im_free_region_array( REGION **regs ); +REGION **im_allocate_region_array( IMAGE *im, int count ); +void im__find_demand_size( IMAGE *im, int *pw, int *ph ); + +/* Buffer processing. + */ +typedef void (*im_wrapmany_fn)( void **in, void *out, int width, + void *a, void *b ); +int im_wrapmany( IMAGE **in, IMAGE *out, + im_wrapmany_fn fn, void *a, void *b ); +typedef void (*im_wrapone_fn)( void *in, void *out, int width, + void *a, void *b ); +int im_wrapone( IMAGE *in, IMAGE *out, + im_wrapone_fn fn, void *a, void *b ); + +/* Internal VIPS functions shared by partials. + */ +int im__call_start( REGION *reg ); +void im__call_stop( REGION *reg ); + +/* window manager. + */ +im_window_t *im_window_ref( IMAGE *im, int top, int height ); +int im_window_unref( im_window_t *window ); +void im_window_print( im_window_t *window ); + +/* buffer manager. + */ +void im_buffer_done( im_buffer_t *buffer ); +void im_buffer_undone( im_buffer_t *buffer ); +void im_buffer_unref( im_buffer_t *buffer ); +im_buffer_t *im_buffer_ref( IMAGE *im, Rect *area ); +im_buffer_t *im_buffer_unref_ref( im_buffer_t *buffer, IMAGE *im, Rect *area ); +void im_buffer_print( im_buffer_t *buffer ); +void im_invalidate( IMAGE *im ); + +/* Only define if IM_ENABLE_DEPRECATED is set. + */ +#ifdef IM_ENABLE_DEPRECATED + +/* Compatibilty macros ... delete soon. See below for the new names. + */ + +/* Macros on REGIONs. + * lskip() add to move down line + * nele() number of elements across region + * rsize() sizeof width of region + * addr() address of pixel in region + */ +#define lskip(B) ((B)->bpl) +#define nele(B) ((B)->valid.width*(B)->im->Bands) +#define rsize(B) ((B)->valid.width*psize((B)->im)) + +/* addr() is special: if DEBUG is defined, make an addr() with bounds checking. + */ +#ifdef DEBUG +#define addr(B,X,Y) \ + ( (im_rect_includespoint( &(B)->valid, (X), (Y) ))? \ + ((B)->data + ((Y) - (B)->valid.top)*lskip(B) + \ + ((X) - (B)->valid.left)*psize((B)->im)): \ + (fprintf( stderr, \ + "addr: point out of bounds, file \"%s\", line %d\n" \ + "(point x=%d, y=%d\n" \ + " should have been within Rect left=%d, top=%d, " \ + "width=%d, height=%d)\n", \ + __FILE__, __LINE__, \ + (X), (Y), \ + (B)->valid.left, \ + (B)->valid.top, \ + (B)->valid.width, \ + (B)->valid.height ), abort(), (char *) NULL) \ + ) +#else /*DEBUG*/ +#define addr(B,X,Y) ((B)->data + ((Y)-(B)->valid.top)*lskip(B) + \ + ((X)-(B)->valid.left)*psize((B)->im)) +#endif /*DEBUG*/ + +#endif /*IM_ENABLE_DEPRECATED*/ + +/* Macros on REGIONs. + * IM_REGION_LSKIP() add to move down line + * IM_REGION_N_ELEMENTS() number of elements across region + * IM_REGION_SIZEOF_LINE() sizeof width of region + * IM_REGION_ADDR() address of pixel in region + */ +#define IM_REGION_LSKIP(R) ((R)->bpl) +#define IM_REGION_N_ELEMENTS(R) ((R)->valid.width*(R)->im->Bands) +#define IM_REGION_SIZEOF_LINE(R) \ + ((R)->valid.width * IM_IMAGE_SIZEOF_PEL((R)->im)) + +/* If DEBUG is defined, add bounds checking. + */ +#ifdef DEBUG +#define IM_REGION_ADDR(B,X,Y) \ + ( (im_rect_includespoint( &(B)->valid, (X), (Y) ))? \ + ((B)->data + ((Y) - (B)->valid.top)*IM_REGION_LSKIP(B) + \ + ((X) - (B)->valid.left)*IM_IMAGE_SIZEOF_PEL((B)->im)): \ + (fprintf( stderr, \ + "IM_REGION_ADDR: point out of bounds, " \ + "file \"%s\", line %d\n" \ + "(point x=%d, y=%d\n" \ + " should have been within Rect left=%d, top=%d, " \ + "width=%d, height=%d)\n", \ + __FILE__, __LINE__, \ + (X), (Y), \ + (B)->valid.left, \ + (B)->valid.top, \ + (B)->valid.width, \ + (B)->valid.height ), abort(), (char *) NULL) \ + ) +#else /*DEBUG*/ +#define IM_REGION_ADDR(B,X,Y) \ + ((B)->data + \ + ((Y)-(B)->valid.top) * IM_REGION_LSKIP(B) + \ + ((X)-(B)->valid.left) * IM_IMAGE_SIZEOF_PEL((B)->im)) +#endif /*DEBUG*/ + +#define IM_REGION_ADDR_TOPLEFT(B) ( (B)->data ) + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_REGION_H*/ diff --git a/include/vips/semaphore.h b/include/vips/semaphore.h new file mode 100644 index 00000000..112cd8dd --- /dev/null +++ b/include/vips/semaphore.h @@ -0,0 +1,64 @@ +/* Definitions for thread support. + * + * JC, 9/5/94 + * 30/7/99 RP, JC + * - reworked for posix/solaris threads + * 28/9/99 JC + * - restructured, made part of public API + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_SEMAPHORE_H +#define IM_SEMAPHORE_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Implement our own semaphores. + */ +typedef struct { + char *name; + int v; + + GMutex *mutex; + GCond *cond; +} im_semaphore_t; + +int im_semaphore_up( im_semaphore_t *s ); +int im_semaphore_down( im_semaphore_t *s ); +int im_semaphore_upn( im_semaphore_t *s, int n ); +int im_semaphore_downn( im_semaphore_t *s, int n ); +void im_semaphore_destroy( im_semaphore_t *s ); +void im_semaphore_init( im_semaphore_t *s, int v, char *name ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_SEMAPHORE_H*/ diff --git a/include/vips/struct.h b/include/vips/struct.h new file mode 100644 index 00000000..99a8bdf9 --- /dev/null +++ b/include/vips/struct.h @@ -0,0 +1,44 @@ +/* Header file for structures using the mosaicing programs */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_STRUCT_H +#define IM_STRUCT_H + +typedef struct { + char *reference, *secondary; + int nopoints; + float *xref, *yref, *xsec, *ysec; +} CNTRL_POINTS; + +typedef struct { + char *reference, *secondary; + float scale, angle, deltax, deltay; + float Xcoef[6], Ycoef[6]; +} MERGE_PARAM; + +#endif /*IM_STRUCT_H*/ diff --git a/include/vips/thread.h b/include/vips/thread.h new file mode 100644 index 00000000..4d1b78f7 --- /dev/null +++ b/include/vips/thread.h @@ -0,0 +1,77 @@ +/* Private include file ... if we've been configured without gthread, we need + * to point the g_thread_*() and g_mutex_*() functions at our own stubs. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_THREAD_H +#define IM_THREAD_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#ifndef HAVE_THREADS +#undef g_thread_supported +#define g_thread_supported() (0) + +#define g_thread_init im__g_thread_init +#define g_thread_join im__g_thread_join +#define g_thread_self im__g_thread_self +#define g_thread_create_full im__g_thread_create_full + +/* We don't need a shadow imlementation of g_thread_create(), even though we + * use it, because it's just a macro over g_thread_create_full(). + */ + +void im__g_thread_init( GThreadFunctions *vtable ); +gpointer im__g_thread_join( GThread * ); +gpointer im__g_thread_self( void ); +GThread *im__g_thread_create_full( GThreadFunc, + gpointer, gulong, gboolean, gboolean, GThreadPriority, GError ** ); + +#undef g_mutex_new +#undef g_mutex_free +#undef g_mutex_lock +#undef g_mutex_unlock + +#define g_mutex_new im__g_mutex_new +#define g_mutex_free im__g_mutex_free +#define g_mutex_lock im__g_mutex_lock +#define g_mutex_unlock im__g_mutex_unlock + +GMutex *im__g_mutex_new( void ); +void im__g_mutex_free( GMutex * ); +void im__g_mutex_lock( GMutex * ); +void im__g_mutex_unlock( GMutex * ); +#endif /*!HAVE_THREADS*/ + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_THREAD_H*/ diff --git a/include/vips/threadgroup.h b/include/vips/threadgroup.h new file mode 100644 index 00000000..f55022fc --- /dev/null +++ b/include/vips/threadgroup.h @@ -0,0 +1,134 @@ +/* Thread eval for VIPS. + * + * 29/9/99 JC + * - from thread.h + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_THREADGROUP_H +#define IM_THREADGROUP_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#include + +/* Stack size for each thread. Need to set this explicitly because some + * systems have a very low default. + + FIXME ... should have an environment variable for this? + + */ +#define IM__DEFAULT_STACK_SIZE (2 * 1024 * 1024) + +/* Default tile geometry. + */ +extern int im__tile_width; +extern int im__tile_height; +extern int im__fatstrip_height; +extern int im__thinstrip_height; + +/* Default n threads. + */ +extern int im__concurrency; + +/* A work function. + */ +typedef int (*im__work_fn)( REGION *, void *, void *, void * ); + +/* What we track for each thread. + */ +typedef struct { + REGION *reg; /* Region this thread operates on */ + struct im__threadgroup_t *tg; /* Thread group we are part of */ + + GThread *thread; /* Thread for this region */ + im_semaphore_t go; /* Thread waits here to start work */ + int kill; /* Set this to make thread exit */ + int error; /* Set by thread if work fn fails */ + + REGION *oreg; /* If part of an inplace threadgroup, */ + Rect pos; /* where this thread should write */ + int x, y; /* it's result */ + + void *a, *b, *c; /* User arguments to work fns */ + +#ifdef TIME_THREAD + hrtime_t *btime, *etime; + int tpos; +#endif /*TIME_THREAD*/ +} im_thread_t; + +/* What we track for a group of threads working together. + */ +typedef struct im__threadgroup_t { + int zombie; /* Set if has been freed */ + + IMAGE *im; /* Image we are calculating */ + int pw, ph; /* Tile size */ + int nlines; /* Scanlines-at-once we prefer for iteration */ + + im__work_fn work; /* Work fn for this threadgroup */ + int inplace; /* Regions should be contiguous */ + + int nthr; /* Number of threads in group */ + im_thread_t **thr; /* Threads */ + + im_semaphore_t idle_sem;/* The number of idle threads */ + GSList *idle; /* All the idle threads */ + GMutex *idle_lock; +#ifdef DEBUG_HIGHWATER + int nidle; /* Number of idles */ + int min_idle; /* How short idle got */ +#endif /*DEBUG_HIGHWATER*/ + + int kill; /* Set this to stop threadgroup early */ +} im_threadgroup_t; + +void im_concurrency_set( int concurrency ); +int im_concurrency_get( void ); + +/* Thread group functions. + */ +im_threadgroup_t *im_threadgroup_create( IMAGE *im ); +int im_threadgroup_free( im_threadgroup_t *tg ); +im_thread_t *im_threadgroup_get( im_threadgroup_t *tg ); +void im_threadgroup_wait( im_threadgroup_t *tg ); +int im_threadgroup_iserror( im_threadgroup_t *tg ); +void im_threadgroup_trigger( im_thread_t *thr ); + +/* Threaded im_prepare() + */ +int im_prepare_thread( im_threadgroup_t *tg, REGION *oreg, Rect *r ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_THREADGROUP_H*/ diff --git a/include/vips/time.h b/include/vips/time.h new file mode 100644 index 00000000..5d073323 --- /dev/null +++ b/include/vips/time.h @@ -0,0 +1,63 @@ +/* Definitions for time struct. + * + * J.Cupitt, 8/4/93 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_TIME_H +#define IM_TIME_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif /*HAVE_SYS_TIME_H*/ + +/* Struct we keep a record of execution time in. Passed to eval callback, so + * it can assess progress. + */ +struct time_info { + IMAGE *im; /* Image we are part of */ + time_t start; /* Start time, in seconds */ + int run; /* Time we have been running */ + int eta; /* Estimated seconds of computation left */ + gint64 tpels; /* Number of pels we expect to calculate */ + gint64 npels; /* Number of pels calculated so far */ + int percent; /* Percent complete */ +}; + +extern int im__handle_eval( IMAGE *im, int w, int h ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_TIME_H*/ diff --git a/include/vips/util.h b/include/vips/util.h new file mode 100644 index 00000000..2cd58a2a --- /dev/null +++ b/include/vips/util.h @@ -0,0 +1,324 @@ +/* Various useful definitions. + * + * J.Cupitt, 8/4/93 + * 15/7/96 JC + * - C++ stuff added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_UTIL_H +#define IM_UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Some platforms don't have M_PI in math.h :-( + */ +#define IM_PI (3.14159265358979323846) + +/* Only define if IM_ENABLE_DEPRECATED is set. + */ +#ifdef IM_ENABLE_DEPRECATED + +#ifndef MAX +#define MAX(A,B) ((A)>(B)?(A):(B)) +#define MIN(A,B) ((A)<(B)?(A):(B)) +#endif /*MAX*/ + +#define CLIP(A,V,B) MAX( (A), MIN( (B), (V) ) ) +#define NEW(IM,A) ((A *)im_malloc((IM),sizeof(A))) +#define NUMBER(R) (sizeof(R)/sizeof(R[0])) +#define ARRAY(IM,N,T) ((T *)im_malloc((IM),(N) * sizeof(T))) + +/* Duff's device. Do OPERation N times in a 16-way unrolled loop. + */ +#define UNROLL( N, OPER ) { \ + if( (N) ) { \ + int duff_count = ((N) + 15) / 16; \ + \ + switch( (N) % 16 ) { \ + case 0: do { OPER; \ + case 15: OPER; \ + case 14: OPER; \ + case 13: OPER; \ + case 12: OPER; \ + case 11: OPER; \ + case 10: OPER; \ + case 9: OPER; \ + case 8: OPER; \ + case 7: OPER; \ + case 6: OPER; \ + case 5: OPER; \ + case 4: OPER; \ + case 3: OPER; \ + case 2: OPER; \ + case 1: OPER; \ + } while( --duff_count > 0 ); \ + } \ + } \ +} + +/* Round a float to the nearest integer. This should give an identical result + * to the math.h rint() function (and the old SunOS nint() function), but be + * much faster. Beware: it evaluates its argument more than once, so don't use + * ++! + */ +#define RINT( R ) ((int)((R)>0?((R)+0.5):((R)-0.5))) + +/* Various integer range clips. Record over/under flows. + */ +#define CLIP_UCHAR( V, SEQ ) { \ + if( (V) & (UCHAR_MAX ^ -1) ) { \ + if( (V) < 0 ) { \ + (SEQ)->underflow++; \ + (V) = 0; \ + } \ + if( (V) > UCHAR_MAX ) { \ + (SEQ)->overflow++; \ + (V) = UCHAR_MAX; \ + } \ + } \ +} + +#define CLIP_USHORT( V, SEQ ) { \ + if( (V) & (USHRT_MAX ^ -1) ) { \ + if( (V) < 0 ) { \ + (SEQ)->underflow++; \ + (V) = 0; \ + } \ + if( (V) > USHRT_MAX ) { \ + (SEQ)->overflow++; \ + (V) = USHRT_MAX; \ + } \ + } \ +} + +#define CLIP_CHAR( V, SEQ ) { \ + if( (V) < SCHAR_MIN ) { \ + (SEQ)->underflow++; \ + (V) = SCHAR_MIN; \ + } \ + if( (V) > SCHAR_MAX ) { \ + (SEQ)->overflow++; \ + (V) = SCHAR_MAX; \ + } \ +} + +#define CLIP_SHORT( V, SEQ ) { \ + if( (V) < SHRT_MIN ) { \ + (SEQ)->underflow++; \ + (V) = SHRT_MIN; \ + } \ + if( (V) > SHRT_MAX ) { \ + (SEQ)->overflow++; \ + (V) = SHRT_MAX; \ + } \ +} + +#define CLIP_NONE( V, SEQ ) {} + +#endif /*IM_ENABLE_DEPRECATED*/ + +#define IM_MAX(A,B) ((A)>(B)?(A):(B)) +#define IM_MIN(A,B) ((A)<(B)?(A):(B)) +#define IM_ABS(x) (((x) >= 0) ? (x) : -(x)) + +#define IM_CLIP(A,V,B) IM_MAX( (A), IM_MIN( (B), (V) ) ) +#define IM_NEW(IM,A) ((A *)im_malloc((IM),sizeof(A))) +#define IM_NUMBER(R) ((int)(sizeof(R)/sizeof(R[0]))) +#define IM_ARRAY(IM,N,T) ((T *)im_malloc((IM),(N) * sizeof(T))) +#define IM_FREEF( F, S ) do { \ + if( S ) { \ + (void) F( (S) ); \ + (S) = 0; \ + } \ +} while( 0 ) +#define IM_FREE( A ) IM_FREEF( im_free, A ) +#define IM_SETSTR( S, V ) do { \ + if( (S) != (V) ) { \ + if( !(S) || !(V) || strcmp( (S), (V) ) != 0 ) { \ + const char *sst = (V); \ + \ + IM_FREE( S ); \ + if( sst ) \ + (S) = im_strdup( NULL, sst ); \ + } \ + } \ +} while( 0 ) + +/* Duff's device. Do OPERation N times in a 16-way unrolled loop. + */ +#define IM_UNROLL( N, OPER ) { \ + if( (N) ) { \ + int duff_count = ((N) + 15) / 16; \ + \ + switch( (N) % 16 ) { \ + case 0: do { OPER; \ + case 15: OPER; \ + case 14: OPER; \ + case 13: OPER; \ + case 12: OPER; \ + case 11: OPER; \ + case 10: OPER; \ + case 9: OPER; \ + case 8: OPER; \ + case 7: OPER; \ + case 6: OPER; \ + case 5: OPER; \ + case 4: OPER; \ + case 3: OPER; \ + case 2: OPER; \ + case 1: OPER; \ + } while( --duff_count > 0 ); \ + } \ + } \ +} + +/* Round a float to the nearest integer. This should give an identical result + * to the math.h rint() function (and the old SunOS nint() function), but be + * much faster. Beware: it evaluates its argument more than once, so don't use + * ++! + */ +#define IM_RINT( R ) ((int)((R)>0?((R)+0.5):((R)-0.5))) + +/* Various integer range clips. Record over/under flows. + */ +#define IM_CLIP_UCHAR( V, SEQ ) { \ + if( (V) < 0 ) { \ + (SEQ)->underflow++; \ + (V) = 0; \ + } \ + else if( (V) > UCHAR_MAX ) { \ + (SEQ)->overflow++; \ + (V) = UCHAR_MAX; \ + } \ +} + +#define IM_CLIP_USHORT( V, SEQ ) { \ + if( (V) < 0 ) { \ + (SEQ)->underflow++; \ + (V) = 0; \ + } \ + else if( (V) > USHRT_MAX ) { \ + (SEQ)->overflow++; \ + (V) = USHRT_MAX; \ + } \ +} + +#define IM_CLIP_CHAR( V, SEQ ) { \ + if( (V) < SCHAR_MIN ) { \ + (SEQ)->underflow++; \ + (V) = SCHAR_MIN; \ + } \ + else if( (V) > SCHAR_MAX ) { \ + (SEQ)->overflow++; \ + (V) = SCHAR_MAX; \ + } \ +} + +#define IM_CLIP_SHORT( V, SEQ ) { \ + if( (V) < SHRT_MIN ) { \ + (SEQ)->underflow++; \ + (V) = SHRT_MIN; \ + } \ + else if( (V) > SHRT_MAX ) { \ + (SEQ)->overflow++; \ + (V) = SHRT_MAX; \ + } \ +} + +#define IM_CLIP_NONE( V, SEQ ) {} + +/* Now implemented as macros. + */ +#define im_open_local( IM, NAME, MODE ) \ + ((IMAGE *) im_local( (IM), \ + (im_construct_fn) im_open, (im_callback_fn) im_close, \ + (void *) (NAME), (void *) (MODE), NULL )) + +/* Strange double cast stops bogus warnings from gcc 4.1 + */ +#define im_open_local_array( IM, OUT, N, NAME, MODE ) \ + (im_local_array( (IM), (void **)((void*)(OUT)), (N),\ + (im_construct_fn) im_open, (im_callback_fn) im_close, \ + (void *) (NAME), (void *) (MODE), NULL )) + +/* strtok replacement. + */ +char *im__break_token( char *str, char *brk ); + +/* Like GFunc, but return a value. + */ +typedef void *(*VSListMap2Fn)( void *, void *, void * ); +typedef void *(*VSListMap4Fn)( void *, void *, void *, void *, void * ); +typedef void *(*VSListFold2Fn)( void *, void *, void *, void * ); + +gboolean im_slist_equal( GSList *l1, GSList *l2 ); +void *im_slist_map2( GSList *list, VSListMap2Fn fn, void *a, void *b ); +void *im_slist_map2_rev( GSList *list, VSListMap2Fn fn, void *a, void *b ); +void *im_slist_map4( GSList *list, + VSListMap4Fn fn, void *a, void *b, void *c, void *d ); +void *im_slist_fold2( GSList *list, void *start, + VSListFold2Fn fn, void *a, void *b ); +GSList *im_slist_filter( GSList *list, VSListMap2Fn fn, void *a, void *b ); +void im_slist_free_all( GSList *list ); + +void *im_map_equal( void *a, void *b ); + +char *im_strncpy( char *dest, const char *src, int n ); +char *im_strrstr( const char *haystack, const char *needle ); +char *im_strdup( IMAGE *im, const char *str ); +gboolean im_ispostfix( const char *a, const char *b ); +gboolean im_isprefix( const char *a, const char *b ); +int im_vsnprintf( char *str, size_t size, const char *format, va_list ap ); +int im_snprintf( char *str, size_t size, const char *format, ... ) + __attribute__((format(printf, 3, 4))); +char *im_break_token( char *str, const char *brk ); + +const char *im_skip_dir( const char *filename ); +void im_filename_split( const char *path, char *name, char *mode ); +void im_filename_suffix( const char *path, char *suffix ); +int im_filename_suffix_match( const char *path, const char *suffixes[] ); +char *im_getnextoption( char **in ); +char *im_getsuboption( const char *buf ); + +void *im_local( IMAGE *im, + im_construct_fn cons, im_callback_fn dest, void *a, void *b, void *c ); +int im_local_array( IMAGE *im, void **out, int n, + im_construct_fn cons, im_callback_fn dest, void *a, void *b, void *c ); + +gint64 im_file_length( int fd ); + +char *im__file_read( FILE *fp, const char *name, unsigned int *length_out ); +char *im__file_read_name( const char *name, unsigned int *length_out ); +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_UTIL_H*/ diff --git a/include/vips/vbuf.h b/include/vips/vbuf.h new file mode 100644 index 00000000..45180fe2 --- /dev/null +++ b/include/vips/vbuf.h @@ -0,0 +1,84 @@ +/* A static string buffer, with overflow protection. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VBUF_H +#define IM_VBUF_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* A string in the process of being written to ... multiple calls to + * buf_append add to it, on overflow append "..." and block further writes. + */ +typedef struct { + char *base; /* String base */ + int mx; /* Maximum length */ + int i; /* Current write point */ + gboolean full; /* String has filled, block writes */ + int lasti; /* For read-recent */ + gboolean dynamic; /* We own the string with malloc() */ +} VBuf; + +/* Static init of one of these. + */ +#define IM_BUF_STATIC( TEXT, MAX ) \ + { &TEXT[0], MAX, 0, FALSE, 0, FALSE } + +void im_buf_rewind( VBuf *buf ); +void im_buf_destroy( VBuf *buf ); +void im_buf_init( VBuf *buf ); +void im_buf_set_static( VBuf *buf, char *base, int mx ); +void im_buf_set_dynamic( VBuf *buf, int mx ); +void im_buf_init_static( VBuf *buf, char *base, int mx ); +void im_buf_init_dynamic( VBuf *buf, int mx ); +gboolean im_buf_appendns( VBuf *buf, const char *str, int sz ); +gboolean im_buf_appends( VBuf *buf, const char *str ); +gboolean im_buf_appendline( VBuf *buf, const char *str ); +gboolean im_buf_appendf( VBuf *buf, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +gboolean im_buf_vappendf( VBuf *buf, const char *fmt, va_list ap ); +gboolean im_buf_appendc( VBuf *buf, char ch ); +gboolean im_buf_appendg( VBuf *buf, double g ); +gboolean im_buf_appendsc( VBuf *buf, const char *str ); +gboolean im_buf_removec( VBuf *buf, char ch ); +gboolean im_buf_change( VBuf *buf, const char *old, const char * ); +gboolean im_buf_isempty( VBuf *buf ); +gboolean im_buf_isfull( VBuf *buf ); +const char *im_buf_all( VBuf *buf ); +gboolean im_buf_appendd( VBuf *buf, int d ); +int im_buf_len( VBuf *buf ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_VBUF_H*/ + + diff --git a/include/vips/version.h.in b/include/vips/version.h.in new file mode 100644 index 00000000..2529a6da --- /dev/null +++ b/include/vips/version.h.in @@ -0,0 +1,15 @@ +/* Macros for the header version. + */ + +#ifndef IM_VERSION_H +#define IM_VERSION_H + +#define IM_VERSION "@IM_VERSION@" +#define IM_VERSION_STRING "@IM_VERSION_STRING@" +#define IM_MAJOR_VERSION (@IM_MAJOR_VERSION@) +#define IM_MINOR_VERSION (@IM_MINOR_VERSION@) +#define IM_MICRO_VERSION (@IM_MICRO_VERSION@) +#define IM_INTERFACE_AGE (@IM_INTERFACE_AGE@) +#define IM_BINARY_AGE (@IM_BINARY_AGE@) + +#endif /*IM_VERSION_H*/ diff --git a/include/vips/vips b/include/vips/vips new file mode 100644 index 00000000..06490d14 --- /dev/null +++ b/include/vips/vips @@ -0,0 +1,106 @@ +// Include file to get all VIPS C++ bindings + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VIPS +#define IM_VIPS + +#include + +// If we have already #included the C vips headers, we have to undef a load of +// stuff to stop vips's stupid macros messing up our enums +#ifdef IM_VIPS_H +#ifdef IM_ENABLE_DEPRECATED + +#undef MULTIBAND +#undef B_W +#undef LUMINACE +#undef XRAY +#undef IR +#undef YUV +#undef RED_ONLY +#undef GREEN_ONLY +#undef BLUE_ONLY +#undef POWER_SPECTRUM +#undef HISTOGRAM + +#undef LUT +#undef XYZ +#undef LAB +#undef CMC +#undef CMYK +#undef LABQ +#undef RGB +#undef UCS +#undef LCH +#undef LABS +#undef sRGB + +#undef FMTNOTSET +#undef FMTUCHAR +#undef FMTCHAR +#undef FMTUSHORT +#undef FMTSHORT +#undef FMTUINT +#undef FMTINT +#undef FMTFLOAT +#undef FMTCOMPLEX +#undef FMTDOUBLE +#undef FMTDPCOMPLEX + +#undef NOCODING +#undef COLQUANT +#undef LABPACK +#undef LABPACK_COMPRESSED +#undef RGB_COMPRESSED +#undef LUM_COMPRESSED + +#undef NO_COMPRESSION +#undef TCSF_COMPRESSION +#undef JPEG_COMPRESSION + +#endif /*IM_ENABLE_DEPRECATED*/ +#endif /*IM_VIPS_H*/ + +#ifdef IM_RECT_H +#ifdef IM_ENABLE_DEPRECATED + +#undef right +#undef bottom + +#endif /*IM_ENABLE_DEPRECATED*/ +#endif /*IM_RECT_H*/ + +#define VIPS_NAMESPACE_START namespace vips { +#define VIPS_NAMESPACE_END } + +#include +#include +#include +#include + +#endif /*IM_VIPS*/ diff --git a/include/vips/vips.h b/include/vips/vips.h new file mode 100644 index 00000000..9305d55e --- /dev/null +++ b/include/vips/vips.h @@ -0,0 +1,463 @@ +/* @(#) Header file for Birkbeck/VIPS Image Processing Library + * Authors: N. Dessipris, K. Martinez, Birkbeck College, London. + * Sept 94 + * + * 15/7/96 JC + * - now does C++ extern stuff + * - many more protos + * 15/4/97 JC + * - protos split out + * 4/3/98 JC + * - IM_ANY added + * - sRGB colourspace added + * 28/10/98 JC + * - VASARI_MAGIC_INTEL and VASARI_MAGIC_SPARC added + * 29/9/99 JC + * - new locks for threading, no more threadgroup stuff in IMAGE + * 30/11/00 JC + * - override RGB/CMYK macros on cygwin + * 21/9/02 JC + * - new Xoffset/Yoffset fields + * - rationalized macro names + * 6/6/05 Markus Wollgarten + * - added Meta header field + * 31/7/05 + * - added meta.h for new metadata API + * 22/8/05 + * - scrapped stupid VAS_HD + * 30/9/05 + * - added sizeof_header field for mmap window read of RAW files + * 4/10/05 + * - now you have to define IM_ENABLE_DEPRECATED to get broken #defined + * 5/10/05 + * - added GNUC attributes + * 8/5/06 + * - added RGB16, GREY16 + * 30/10/06 + * - added im_window_t + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef IM_VIPS_H +#define IM_VIPS_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* If we're not using GNU C, elide __attribute__ + */ +#ifndef __GNUC__ +# ifndef __attribute__ +# define __attribute__(x) /*NOTHING*/ +# endif +#endif + +#include +#include + +#include +#include + +#define IM_SPARE (8) + +/* If you read MSB first, you get these two values. + * intel order: byte 0 = b6 + * SPARC order: byte 0 = 08 + */ +#define IM_MAGIC_INTEL (0xb6a6f208) +#define IM_MAGIC_SPARC (0x08f2a6b6) + +/* Private to iofuncs: the image size above which we switch from + * mmap()-whole-image behaviour to mmap()-window, plus window margins. + */ +#define IM__MMAP_LIMIT (1024*1024*30) +#define IM__WINDOW_MARGIN (128) + +/* sizeof() a VIPS header on disc. + */ +#define IM_SIZEOF_HEADER (64) + +typedef unsigned char PEL; /* useful datum */ + +/* All these #defines are here for backwards compatibility ... delete them + * soon. See the bottom of this file for the new names. + */ + +/* Only define old, broken names if asked. + */ +#ifdef IM_ENABLE_DEPRECATED + +/* On win32, need to override the wingdi defs for these. Yuk! + */ +#ifdef HAVE_WINDOWS_H +#ifdef RGB +#undef RGB +#endif +#ifdef CMYK +#undef CMYK +#endif +#endif /*HAVE_WINDOWS_H*/ + +/* Bits per Band */ +#define BBBYTE 8 +#define BBSHORT 16 +#define BBINT 32 +#define BBFLOAT 32 +#define BBCOMPLEX 64 /* complex consisting of two floats */ +#define BBDOUBLE 64 +#define BBDPCOMPLEX 128 /* complex consisting of two doubles */ + +/* picture Type */ +#define MULTIBAND 0 +#define B_W 1 +#define LUMINACE 2 +#define XRAY 3 +#define IR 4 +#define YUV 5 +#define RED_ONLY 6 /* red channel only */ +#define GREEN_ONLY 7 /* green channel only */ +#define BLUE_ONLY 8 /* blue channel only */ +#define POWER_SPECTRUM 9 +#define HISTOGRAM 10 +#define FOURIER 24 + +/* Colour spaces. + */ +#define LUT 11 +#define XYZ 12 +#define LAB 13 +#define CMC 14 +#define CMYK 15 +#define LABQ 16 +#define RGB 17 +#define UCS 18 +#define LCH 19 +#define LABS 21 +#define sRGB 22 +#define YXY 23 + +/* BandFmt + */ +#define FMTNOTSET -1 +#define FMTUCHAR 0 /* pels interpreted as unsigned chars */ +#define FMTCHAR 1 /* pels interpreted as signed chars */ +#define FMTUSHORT 2 /* pels interpreted as unsigned shorts */ +#define FMTSHORT 3 /* pels interpreted as signed shorts */ +#define FMTUINT 4 /* pels interpreted as unsigned ints */ +#define FMTINT 5 /* pels interpreted as signed ints */ +#define FMTFLOAT 6 /* pels interpreted as floats */ +#define FMTCOMPLEX 7 /* pels interpreted as complex (2 float each) */ +#define FMTDOUBLE 8 /* pels interpreted as unsigned double */ +#define FMTDPCOMPLEX 9 /* pels interpreted as complex (2 double each)*/ + +/* Coding type + */ +#define NOCODING 0 +#define COLQUANT 1 +#define LABPACK 2 +#define LABPACK_COMPRESSED 3 +#define RGB_COMPRESSED 4 +#define LUM_COMPRESSED 5 + +/* Compression type + */ +#define NO_COMPRESSION 0 +#define TCSF_COMPRESSION 1 +#define JPEG_COMPRESSION 2 + +#endif /*IM_ENABLE_DEPRECATED*/ + +/* Types of image descriptor we may have. The type field is advisory only: it + * does not imply that any fields in IMAGE have valid data. + */ +typedef enum { + IM_NONE, /* no type set */ + IM_SETBUF, /* malloced memory array */ + IM_SETBUF_FOREIGN, /* memory array, don't free on close */ + IM_OPENIN, /* input from fd */ + IM_MMAPIN, /* memory mapped input file */ + IM_MMAPINRW, /* memory mapped read/write file */ + IM_OPENOUT, /* output to fd */ + IM_PARTIAL /* partial image */ +} im_desc_type; + +/* Demand style from im_generate(). See im_demand_hint(). + */ +typedef enum { + IM_SMALLTILE, + IM_FATSTRIP, + IM_THINSTRIP, + IM_ANY /* Not from a disc file, any geometry */ +} im_demand_type; + +/* What we track for each mmap window. Have a list of these on an openin + * IMAGE. + */ +typedef struct { + int ref_count; /* # of regions referencing us */ + struct im__IMAGE *im; /* IMAGE we are attached to */ + + int top; /* Area of image we have mapped, in pixels */ + int height; + char *data; /* First pixel of line 'top' */ + + PEL *baseaddr; /* Base of window */ + size_t length; /* Size of window */ +} im_window_t; + +/* Image descriptor for subroutine i/o args + */ +typedef struct im__IMAGE { + /* Fields from file header. + */ + int Xsize; + int Ysize; + int Bands; + int Bbits; + int BandFmt; + int Coding; + int Type; + float Xres; + float Yres; + int Length; + short Compression; + short Level; + int Xoffset; + int Yoffset; + + /* Derived fields that user can fiddle with. + */ + char *Hist; /* don't use ... call im_history_get() */ + char *filename; /* pointer to copy of filename */ + char *data; /* start of image data for WIO */ + struct time_info *time; /* time struct for eval callback */ + int kill; /* set to non-zero to block partial eval */ + + /* Private fields. + */ + im_desc_type dtype; /* descriptor type */ + int fd; /* file descriptor */ + char *baseaddr; /* pointer to the start of an mmap file */ + size_t length; /* size of mmap area */ + GSList *closefns; /* list of close callbacks */ + GSList *evalfns; /* list of eval callbacks */ + GSList *evalendfns; /* list of eval end callbacks */ + int closing; /* true for this descriptor is closing */ + int close_pending; /* true for this descriptor is a zombie */ + guint32 magic; /* magic from header, endian-ness of image */ + + /* Partial image stuff. All private! All these fields are initialised + * to NULL and ignored unless set by im_generate() or im_partial(). + */ + void *(*start)(); /* user-supplied start function */ + int (*generate)(); /* user-supplied generate function */ + int (*stop)(); /* user-supplied stop function */ + void *client1; /* user arguments */ + void *client2; + GMutex *sslock; /* start-stop lock */ + GSList *regions; /* list of regions current for this image */ + im_demand_type dhint; /* demand style hint */ + + /* Extra user-defined fields ... see im_meta_get_int() etc. + */ + GHashTable *Meta; /* GhashTable of GValue */ + GSList *Meta_traverse; /* Traverse order for Meta */ + + /* Part of mmap() read ... the sizeof() the header we skip from the + * file start. Usually IM_SIZEOF_HEADER, but can be soomething else + * for binary file read. + */ + int sizeof_header; + + /* If this is a large disc image, don't map the whole thing, instead + * have a set of windows shared between the regions active on the + * image. List of im_window_t. + */ + GSList *windows; + + /* Parent/child relationships, built from args to im_demand_hint(). + * We use these to invalidate pixel buffers on im_invalidate(). Use + * 'serial' to spot circular dependencies. + */ + GSList *parents; + GSList *children; + int serial; + + /* Keep a list of recounted GValue strings so we can share hist + * efficiently. + */ + GSList *history_list; +} IMAGE; + +/* Only define if IM_ENABLE_DEPRECATED is set. + */ +#ifdef IM_ENABLE_DEPRECATED + +/* Macros on IMAGEs. + * esize() sizeof band element + * psize() sizeof pel + * lsize() sizeof scan line + * niele() number of elements in scan line + */ +#define esize(I) ((I)->Bbits >> 3) +#define psize(I) (esize(I)*(I)->Bands) +#define lsize(I) (psize(I)*(I)->Xsize) +#define niele(I) ((I)->Bands*(I)->Xsize) + +#endif /*IM_ENABLE_DEPRECATED*/ + +/* Used to define a region of interest for im_extract() etc. + */ +typedef struct { + int xstart; + int ystart; + int xsize; + int ysize; + int chsel; /* 1 2 3 or 0, for r g b or all respectively + *(channel select) */ +} IMAGE_BOX; + +/* @(#) Definition for structure to hold integer or double masks + */ + +typedef struct im__INTMASK { + int xsize; + int ysize; + int scale; + int offset; + int *coeff; + char *filename; +} INTMASK ; + +typedef struct im__DOUBLEMASK { + int xsize; + int ysize; + double scale; + double offset; + double *coeff; + char *filename; +} DOUBLEMASK ; + +/* A colour temperature. + */ +typedef struct { + double X0, Y0, Z0; +} im_colour_temperature; + +/* Sensible names for our #defines. Only bother with + * the ones we actually use. Switch over to defining only these ones at some + * point (vips8?). + */ + +#define IM_BBITS_BYTE (8) +#define IM_BBITS_SHORT (16) +#define IM_BBITS_INT (32) +#define IM_BBITS_FLOAT (32) +#define IM_BBITS_COMPLEX (64) +#define IM_BBITS_DOUBLE (64) +#define IM_BBITS_DPCOMPLEX (128) + +#define IM_TYPE_MULTIBAND (0) +#define IM_TYPE_B_W (1) +#define IM_TYPE_HISTOGRAM (10) +#define IM_TYPE_FOURIER (24) +#define IM_TYPE_XYZ (12) +#define IM_TYPE_LAB (13) +#define IM_TYPE_CMYK (15) +#define IM_TYPE_LABQ (16) +#define IM_TYPE_RGB (17) +#define IM_TYPE_UCS (18) +#define IM_TYPE_LCH (19) +#define IM_TYPE_LABS (21) +#define IM_TYPE_sRGB (22) +#define IM_TYPE_YXY (23) +#define IM_TYPE_RGB16 (25) +#define IM_TYPE_GREY16 (26) + +#define IM_BANDFMT_NOTSET (-1) +#define IM_BANDFMT_UCHAR (0) +#define IM_BANDFMT_CHAR (1) +#define IM_BANDFMT_USHORT (2) +#define IM_BANDFMT_SHORT (3) +#define IM_BANDFMT_UINT (4) +#define IM_BANDFMT_INT (5) +#define IM_BANDFMT_FLOAT (6) +#define IM_BANDFMT_COMPLEX (7) +#define IM_BANDFMT_DOUBLE (8) +#define IM_BANDFMT_DPCOMPLEX (9) + +#define IM_CODING_NONE (0) +#define IM_CODING_LABQ (2) + +#define IM_IMAGE_SIZEOF_ELEMENT(I) ((I)->Bbits >> 3) +#define IM_IMAGE_SIZEOF_PEL(I) \ + (IM_IMAGE_SIZEOF_ELEMENT(I) * (I)->Bands) +#define IM_IMAGE_SIZEOF_LINE(I) (IM_IMAGE_SIZEOF_PEL(I) * (I)->Xsize) +#define IM_IMAGE_N_ELEMENTS(I) ((I)->Bands * (I)->Xsize) + +/* If DEBUG is defined, add bounds checking. + */ +#ifdef DEBUG +#define IM_IMAGE_ADDR(I,X,Y) \ + ( ((X) >= 0 && (X) < (I)->Xsize && \ + (Y) >= 0 && (Y) < (I)->Ysize) ? \ + ((I)->data + (Y) * IM_IMAGE_SIZEOF_LINE(I) + \ + (X) * IM_IMAGE_SIZEOF_PEL(I)) : \ + (fprintf( stderr, \ + "IM_IMAGE_ADDR: point out of bounds, " \ + "file \"%s\", line %d\n" \ + "(point x=%d, y=%d\n" \ + " should have been within Rect left=%d, top=%d, " \ + "width=%d, height=%d)\n", \ + __FILE__, __LINE__, \ + (X), (Y), \ + 0, 0, \ + (I)->Xsize, \ + (I)->Ysize ), abort(), (char *) NULL) \ + ) +#else /*DEBUG*/ +#define IM_IMAGE_ADDR(I,X,Y) \ + ((I)->data + \ + (Y) * IM_IMAGE_SIZEOF_LINE(I) + \ + (X) * IM_IMAGE_SIZEOF_PEL(I)) +#endif /*DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_VIPS_H*/ diff --git a/include/vips/vipsc++.h b/include/vips/vipsc++.h new file mode 100644 index 00000000..3541bff3 --- /dev/null +++ b/include/vips/vipsc++.h @@ -0,0 +1,308 @@ +// this file automatically generated from +// VIPS library 7.12.2-Tue Jul 17 23:36:09 BST 2007 +VImage abs() throw( VError ); +VImage acos() throw( VError ); +VImage add( VImage ) throw( VError ); +VImage asin() throw( VError ); +VImage atan() throw( VError ); +double avg() throw( VError ); +double point_bilinear( double, double, int ) throw( VError ); +VImage bandmean() throw( VError ); +VImage ceil() throw( VError ); +VImage cmulnorm( VImage ) throw( VError ); +VImage cos() throw( VError ); +double deviate() throw( VError ); +VImage divide( VImage ) throw( VError ); +VImage exp10() throw( VError ); +VImage expn( double ) throw( VError ); +VImage expn( std::vector ) throw( VError ); +VImage exp() throw( VError ); +VImage fav4( VImage, VImage, VImage ) throw( VError ); +VImage floor() throw( VError ); +VImage gadd( double, double, VImage, double ) throw( VError ); +VImage invert() throw( VError ); +VImage lin( double, double ) throw( VError ); +static VImage linreg( std::vector, std::vector ) throw( VError ); +VImage lin( std::vector, std::vector ) throw( VError ); +VImage litecor( VImage, int, double ) throw( VError ); +VImage log10() throw( VError ); +VImage log() throw( VError ); +double max() throw( VError ); +std::complex maxpos() throw( VError ); +double maxpos_avg( double&, double& ) throw( VError ); +VDMask measure( int, int, int, int, int, int ) throw( VError ); +double min() throw( VError ); +std::complex minpos() throw( VError ); +VImage multiply( VImage ) throw( VError ); +VImage pow( double ) throw( VError ); +VImage pow( std::vector ) throw( VError ); +VImage remainder( VImage ) throw( VError ); +VImage remainder( double ) throw( VError ); +VImage remainder( std::vector ) throw( VError ); +VImage rint() throw( VError ); +VImage sign() throw( VError ); +VImage sin() throw( VError ); +VDMask stats() throw( VError ); +VImage subtract( VImage ) throw( VError ); +VImage tan() throw( VError ); +VImage andimage( VImage ) throw( VError ); +VImage andimage( int ) throw( VError ); +VImage andimage( std::vector ) throw( VError ); +VImage orimage( VImage ) throw( VError ); +VImage orimage( int ) throw( VError ); +VImage orimage( std::vector ) throw( VError ); +VImage eorimage( VImage ) throw( VError ); +VImage eorimage( int ) throw( VError ); +VImage eorimage( std::vector ) throw( VError ); +VImage shiftleft( int ) throw( VError ); +VImage shiftright( int ) throw( VError ); +VImage LCh2Lab() throw( VError ); +VImage LCh2UCS() throw( VError ); +VImage Lab2LCh() throw( VError ); +VImage Lab2LabQ() throw( VError ); +VImage Lab2LabS() throw( VError ); +VImage Lab2UCS() throw( VError ); +VImage Lab2XYZ() throw( VError ); +VImage Lab2XYZ_temp( double, double, double ) throw( VError ); +VImage Lab2disp( VDisplay ) throw( VError ); +VImage LabQ2LabS() throw( VError ); +VImage LabQ2Lab() throw( VError ); +VImage LabQ2XYZ() throw( VError ); +VImage LabQ2disp( VDisplay ) throw( VError ); +VImage LabS2LabQ() throw( VError ); +VImage LabS2Lab() throw( VError ); +VImage UCS2LCh() throw( VError ); +VImage UCS2Lab() throw( VError ); +VImage UCS2XYZ() throw( VError ); +VImage XYZ2Lab() throw( VError ); +VImage XYZ2Lab_temp( double, double, double ) throw( VError ); +VImage XYZ2UCS() throw( VError ); +VImage XYZ2Yxy() throw( VError ); +VImage XYZ2disp( VDisplay ) throw( VError ); +VImage XYZ2sRGB() throw( VError ); +VImage Yxy2XYZ() throw( VError ); +VImage dE00_fromLab( VImage ) throw( VError ); +VImage dECMC_fromLab( VImage ) throw( VError ); +VImage dECMC_fromdisp( VImage, VDisplay ) throw( VError ); +VImage dE_fromLab( VImage ) throw( VError ); +VImage dE_fromXYZ( VImage ) throw( VError ); +VImage dE_fromdisp( VImage, VDisplay ) throw( VError ); +VImage disp2Lab( VDisplay ) throw( VError ); +VImage disp2XYZ( VDisplay ) throw( VError ); +VImage icc_ac2rc( char* ) throw( VError ); +VImage icc_export( char*, int ) throw( VError ); +VImage icc_export_depth( int, char*, int ) throw( VError ); +VImage icc_import( char*, int ) throw( VError ); +VImage icc_import_embedded( int ) throw( VError ); +VImage icc_transform( char*, char*, int ) throw( VError ); +VImage lab_morph( VDMask, double, double, double, double ) throw( VError ); +VImage sRGB2XYZ() throw( VError ); +VImage bandjoin( VImage ) throw( VError ); +static VImage black( int, int, int ) throw( VError ); +VImage c2amph() throw( VError ); +VImage c2imag() throw( VError ); +VImage c2ps() throw( VError ); +VImage c2real() throw( VError ); +VImage c2rect() throw( VError ); +VImage clip2c() throw( VError ); +VImage clip2cm() throw( VError ); +VImage clip2d() throw( VError ); +VImage clip2dcm() throw( VError ); +VImage clip2f() throw( VError ); +VImage clip2fmt( int ) throw( VError ); +VImage clip2i() throw( VError ); +VImage clip2s() throw( VError ); +VImage clip2ui() throw( VError ); +VImage clip2us() throw( VError ); +VImage clip() throw( VError ); +VImage copy() throw( VError ); +VImage copy_morph( int, int, int ) throw( VError ); +VImage copy_swap() throw( VError ); +VImage copy_set( int, double, double, int, int ) throw( VError ); +static VImage csv2vips( char* ) throw( VError ); +VImage extract_area( int, int, int, int ) throw( VError ); +VImage extract_areabands( int, int, int, int, int, int ) throw( VError ); +VImage extract_band( int ) throw( VError ); +VImage extract_bands( int, int ) throw( VError ); +VImage extract( int, int, int, int, int ) throw( VError ); +VImage falsecolour() throw( VError ); +VImage fliphor() throw( VError ); +VImage flipver() throw( VError ); +static VImage gbandjoin( std::vector ) throw( VError ); +VImage grid( int, int, int ) throw( VError ); +VImage insert( VImage, int, int ) throw( VError ); +VImage insert_noexpand( VImage, int, int ) throw( VError ); +static VImage jpeg2vips( char* ) throw( VError ); +VImage lrjoin( VImage ) throw( VError ); +static VImage magick2vips( char* ) throw( VError ); +static VImage mask2vips( VDMask ) throw( VError ); +VImage msb() throw( VError ); +VImage msb_band( int ) throw( VError ); +static VImage png2vips( char* ) throw( VError ); +static VImage exr2vips( char* ) throw( VError ); +static VImage ppm2vips( char* ) throw( VError ); +static VImage analyze2vips( char* ) throw( VError ); +VImage recomb( VDMask ) throw( VError ); +VImage replicate( int, int ) throw( VError ); +VImage ri2c( VImage ) throw( VError ); +VImage rot180() throw( VError ); +VImage rot270() throw( VError ); +VImage rot90() throw( VError ); +VImage scale() throw( VError ); +VImage scaleps() throw( VError ); +VImage rightshift_size( int, int, int ) throw( VError ); +VImage slice( double, double ) throw( VError ); +VImage subsample( int, int ) throw( VError ); +char* system( char* ) throw( VError ); +VImage tbjoin( VImage ) throw( VError ); +static VImage text( char*, char*, int, int, int ) throw( VError ); +VImage thresh( double ) throw( VError ); +static VImage tiff2vips( char* ) throw( VError ); +void vips2csv( char* ) throw( VError ); +void vips2jpeg( char* ) throw( VError ); +VDMask vips2mask() throw( VError ); +void vips2mimejpeg( int ) throw( VError ); +void vips2png( char* ) throw( VError ); +void vips2ppm( char* ) throw( VError ); +void vips2tiff( char* ) throw( VError ); +VImage zoom( int, int ) throw( VError ); +VImage addgnoise( double ) throw( VError ); +VImage compass( VIMask ) throw( VError ); +VImage contrast_surface( int, int ) throw( VError ); +VImage contrast_surface_raw( int, int ) throw( VError ); +VImage conv( VIMask ) throw( VError ); +VImage conv_raw( VIMask ) throw( VError ); +VImage convf( VDMask ) throw( VError ); +VImage convf_raw( VDMask ) throw( VError ); +VImage convsep( VIMask ) throw( VError ); +VImage convsep_raw( VIMask ) throw( VError ); +VImage convsepf( VDMask ) throw( VError ); +VImage convsepf_raw( VDMask ) throw( VError ); +VImage convsub( VIMask, int, int ) throw( VError ); +VImage embed( int, int, int, int, int ) throw( VError ); +VImage fastcor( VImage ) throw( VError ); +VImage fastcor_raw( VImage ) throw( VError ); +static VImage gaussnoise( int, int, double, double ) throw( VError ); +VImage grad_x() throw( VError ); +VImage grad_y() throw( VError ); +VImage gradcor( VImage ) throw( VError ); +VImage gradcor_raw( VImage ) throw( VError ); +VImage gradient( VIMask ) throw( VError ); +static VImage rank_image( std::vector, int ) throw( VError ); +VImage lindetect( VIMask ) throw( VError ); +static VImage maxvalue( std::vector ) throw( VError ); +int mpercent( double ) throw( VError ); +VImage rank( int, int, int ) throw( VError ); +VImage rank_raw( int, int, int ) throw( VError ); +VImage resize_linear( int, int ) throw( VError ); +VImage sharpen( int, double, double, double, double, double ) throw( VError ); +VImage shrink( double, double ) throw( VError ); +VImage spcor( VImage ) throw( VError ); +VImage spcor_raw( VImage ) throw( VError ); +VImage spcor2( VImage ) throw( VError ); +VImage spcor2_raw( VImage ) throw( VError ); +VImage stretch3( double, double ) throw( VError ); +VImage zerox( int ) throw( VError ); +static VImage create_fmask( int, int, int, double, double, double, double, double ) throw( VError ); +VImage disp_ps() throw( VError ); +VImage flt_image_freq( int, double, double, double, double, double ) throw( VError ); +static VImage fractsurf( int, double ) throw( VError ); +VImage freqflt( VImage ) throw( VError ); +VImage fwfft() throw( VError ); +VImage rotquad() throw( VError ); +VImage invfft() throw( VError ); +VImage invfftr() throw( VError ); +VImage gammacorrect( double ) throw( VError ); +VImage heq( int ) throw( VError ); +VImage hist( int ) throw( VError ); +VImage histcum() throw( VError ); +VImage histeq() throw( VError ); +VImage histgr( int ) throw( VError ); +VImage histnD( int ) throw( VError ); +VImage histnorm() throw( VError ); +VImage histplot() throw( VError ); +VImage histspec( VImage ) throw( VError ); +VImage hsp( VImage ) throw( VError ); +static VImage identity( int ) throw( VError ); +static VImage identity_ushort( int, int ) throw( VError ); +int ismonotonic() throw( VError ); +VImage lhisteq( int, int ) throw( VError ); +VImage lhisteq_raw( int, int ) throw( VError ); +static VImage invertlut( VDMask, int ) throw( VError ); +static VImage buildlut( VDMask ) throw( VError ); +VImage maplut( VImage ) throw( VError ); +VImage project( VImage& ) throw( VError ); +VImage stdif( double, double, double, double, int, int ) throw( VError ); +VImage stdif_raw( double, double, double, double, int, int ) throw( VError ); +VImage tone_analyse( double, double, double, double, double, double ) throw( VError ); +static VImage tone_build( double, double, double, double, double, double, double, double ) throw( VError ); +static VImage tone_build_range( int, int, double, double, double, double, double, double, double, double ) throw( VError ); +VImage tone_map( VImage ) throw( VError ); +void circle( int, int, int, int ) throw( VError ); +VImage flood_blob_copy( int, int, std::vector ) throw( VError ); +void insertplace( VImage, int, int ) throw( VError ); +void line( int, int, int, int, int ) throw( VError ); +VImage lineset( VImage, VImage, std::vector, std::vector, std::vector, std::vector ) throw( VError ); +static VImage binfile( char*, int, int, int, int ) throw( VError ); +VImage cache( int, int, int ) throw( VError ); +int header_get_type( char* ) throw( VError ); +int header_int( char* ) throw( VError ); +double header_double( char* ) throw( VError ); +char* header_string( char* ) throw( VError ); +double cntlines( int ) throw( VError ); +VImage dilate( VIMask ) throw( VError ); +VImage dilate_raw( VIMask ) throw( VError ); +VImage erode( VIMask ) throw( VError ); +VImage erode_raw( VIMask ) throw( VError ); +VImage profile( int ) throw( VError ); +VImage affine( double, double, double, double, double, double, int, int, int, int ) throw( VError ); +double correl( VImage, int, int, int, int, int, int, int&, int& ) throw( VError ); +int _find_lroverlap( VImage, int, int, int, int, int, int, int, int&, double&, double&, double&, double& ) throw( VError ); +int _find_tboverlap( VImage, int, int, int, int, int, int, int, int&, double&, double&, double&, double& ) throw( VError ); +VImage global_balance( double ) throw( VError ); +VImage global_balancef( double ) throw( VError ); +VImage lrmerge( VImage, int, int, int ) throw( VError ); +VImage lrmerge1( VImage, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage lrmosaic( VImage, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage lrmosaic1( VImage, int, int, int, int, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage match_linear( VImage, int, int, int, int, int, int, int, int ) throw( VError ); +VImage match_linear_search( VImage, int, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage remosaic( char*, char* ) throw( VError ); +VImage similarity_area( double, double, double, double, int, int, int, int ) throw( VError ); +VImage similarity( double, double, double, double ) throw( VError ); +VImage tbmerge( VImage, int, int, int ) throw( VError ); +VImage tbmerge1( VImage, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage tbmosaic( VImage, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage tbmosaic1( VImage, int, int, int, int, int, int, int, int, int, int, int, int, int ) throw( VError ); +VImage benchmark() throw( VError ); +double benchmark2() throw( VError ); +VImage benchmarkn( int ) throw( VError ); +static VImage eye( int, int, double ) throw( VError ); +static VImage grey( int, int ) throw( VError ); +static VImage feye( int, int, double ) throw( VError ); +static VImage fgrey( int, int ) throw( VError ); +static VImage fzone( int ) throw( VError ); +static VImage make_xy( int, int ) throw( VError ); +static VImage zone( int ) throw( VError ); +VImage blend( VImage, VImage ) throw( VError ); +VImage equal( VImage ) throw( VError ); +VImage equal( std::vector ) throw( VError ); +VImage equal( double ) throw( VError ); +VImage ifthenelse( VImage, VImage ) throw( VError ); +VImage less( VImage ) throw( VError ); +VImage less( std::vector ) throw( VError ); +VImage less( double ) throw( VError ); +VImage lesseq( VImage ) throw( VError ); +VImage lesseq( std::vector ) throw( VError ); +VImage lesseq( double ) throw( VError ); +VImage more( VImage ) throw( VError ); +VImage more( std::vector ) throw( VError ); +VImage more( double ) throw( VError ); +VImage moreeq( VImage ) throw( VError ); +VImage moreeq( std::vector ) throw( VError ); +VImage moreeq( double ) throw( VError ); +VImage notequal( VImage ) throw( VError ); +VImage notequal( std::vector ) throw( VError ); +VImage notequal( double ) throw( VError ); +static VImage video_test( int, int ) throw( VError ); +static VImage video_v4l1( char*, int, int, int, int, int, int ) throw( VError ); diff --git a/include/vips/vipscpp.h b/include/vips/vipscpp.h new file mode 100644 index 00000000..ec263c70 --- /dev/null +++ b/include/vips/vipscpp.h @@ -0,0 +1,39 @@ +// Include file to get all VIPS C++ bindings + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* This header is just for compatibility with the pre-namespace C++ bindings. + */ + +#ifndef IM_VIPSCPP_H +#define IM_VIPSCPP_H + +#include + +using namespace vips; + +#endif /*IM_VIPSCPP_H*/ diff --git a/libsrc/Makefile.am b/libsrc/Makefile.am new file mode 100644 index 00000000..6d956bbd --- /dev/null +++ b/libsrc/Makefile.am @@ -0,0 +1,58 @@ + +SUBDIRS = \ + acquire \ + arithmetic \ + boolean \ + colour \ + conversion \ + convolution \ + freq_filt \ + histograms_lut \ + inplace \ + iofuncs \ + matrix \ + morphology \ + mosaicing \ + other \ + relational \ + video \ + . + +lib_LTLIBRARIES = libvips.la + +libvips_la_SOURCES = dummy.c + +# DLLs need dependant libs there too ... put @VIPS_LIBS@ at the end +libvips_la_LIBADD = \ + acquire/libacquire.la \ + arithmetic/libarithmetic.la \ + boolean/libboolean.la \ + colour/libcolour.la \ + conversion/libconversion.la \ + convolution/libconvolution.la \ + freq_filt/libfreq_filt.la \ + histograms_lut/libhistograms_lut.la \ + inplace/libinplace.la \ + iofuncs/libiofuncs.la \ + matrix/libmatrix.la \ + morphology/libmorphology.la \ + mosaicing/libmosaicing.la \ + other/libother.la \ + relational/librelational.la \ + video/libvideo.la \ + @VIPS_LIBS@ + +# use "-no-undefined" because we need to be able to generate dlls as well + +# commented out +# -no-undefined +# because I can't get libtool DLL builds working :-( + +libvips_la_LDFLAGS = \ + -no-undefined \ + -version-info @LIBRARY_CURRENT@:@LIBRARY_REVISION@:@LIBRARY_AGE@ + +vips.def: + ./makedef.pl $(prefix) > vips.def + +EXTRA_DIST = vips.def makedef.pl diff --git a/libsrc/acquire/Makefile.am b/libsrc/acquire/Makefile.am new file mode 100644 index 00000000..733c4645 --- /dev/null +++ b/libsrc/acquire/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libacquire.la + +libacquire_la_SOURCES = \ + im_clamp.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ + diff --git a/libsrc/acquire/im_clamp.c b/libsrc/acquire/im_clamp.c new file mode 100644 index 00000000..df072687 --- /dev/null +++ b/libsrc/acquire/im_clamp.c @@ -0,0 +1,127 @@ +/* @(#) Function to perform black level correction given black image + * @(#) designed for PAD camera single field black to apply in blocks + * @(#) as each is reused for higher resolution pels (eg: 6 8 for Progres) + * @(#) IM_BANDFMT_UCHAR images only. Always writes UCHAR. + * @(#) int im_clamp(in, w, out, hstep, vstep) + * @(#) IMAGE *in, *w, *out; int hstep, vstep; + * @(#) - Compute clip(image - (black)) ie subtract black no negatives + * @(#) scales for low res Progres images to replicate black value + * @(#) Returns 0 on success and -1 on error + * fiddle at your peril - nasty code + * Copyright: 1993 KM + * 20/8/93 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_clamp( in, out,black, hstep, vstep ) +IMAGE *in, *black, *out; +int hstep, vstep; +{ PEL *p, *blk, *bline, *bexp; +PEL *q, *outbuf; +int rep; +int x, y, bnd; +int temp, blacky, newblacky; + +if( im_iocheck( in, out ) ) + return( -1 ); +if( in->Bbits != 8 || + in->Coding != IM_CODING_NONE || in->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_clamp", _( "bad input format" ) ); + return( -1 ); +} +if( black->Bbits != 8 || + black->Coding != IM_CODING_NONE || black->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_clamp", _( "bad black format" ) ); + return( -1 ); +} + +/* Set up the output header. + */ +if( im_cp_desc( out, in ) ) + return( -1 ); +if( im_setupout( out ) ) + return( -1 ); + +/* Make buffer for expanded black line + */ +if( !(bline = (PEL *) im_malloc( out, black->Bands * hstep * in->Xsize )) ) + return( -1 ); +/* Make buffer we write to. + */ +if( !(outbuf = (PEL *) im_malloc( out, out->Bands * out->Xsize )) ) + return( -1 ); +blacky = -1; +p = (PEL *) in->data; + +for( y = 0; y < in->Ysize; y++ ) { + /* calc corresponding black line - get new one if different */ + newblacky = (vstep * black->Ysize - in->Ysize + y)/vstep; + if( newblacky != blacky){ + blacky = newblacky; + /* time to expand a new black line */ + blk = (PEL *) (black->data + + black->Xsize * black->Bands * blacky); + for(bexp = bline, x = 0; x < black->Xsize; x++){ + for(rep = 0; rep < hstep; rep++) + for(q=blk, bnd = 0; bnd < in->Bands; bnd++) + *bexp++ = *q++; + blk += black->Bands; + } + } + + /* correct a line of image */ + bexp = bline; + q = outbuf; + for( x = 0; x < (out->Bands * out->Xsize); x++ ) { + temp = ((int) *p++ - *bexp++); + if( temp < 0 ) temp = 0; + *q++ = (PEL)temp; + } + + if( im_writeline( y, out, outbuf ) ) + return( -1 ); +} /* end of a line */ + +return( 0 ); +} diff --git a/libsrc/acquire/man3/Makefile.am b/libsrc/acquire/man3/Makefile.am new file mode 100644 index 00000000..27aac5d4 --- /dev/null +++ b/libsrc/acquire/man3/Makefile.am @@ -0,0 +1 @@ +man_MANS = diff --git a/libsrc/arithmetic/Makefile.am b/libsrc/arithmetic/Makefile.am new file mode 100644 index 00000000..e1273aa3 --- /dev/null +++ b/libsrc/arithmetic/Makefile.am @@ -0,0 +1,46 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libarithmetic.la + +libarithmetic_la_SOURCES = \ + arith_dispatch.c \ + im_abs.c \ + im_add.c \ + im_avg.c \ + im_point_bilinear.c \ + im_bandmean.c \ + im_cmulnorm.c \ + im_costra.c \ + im_deviate.c \ + im_divide.c \ + im_ceil.c \ + im_floor.c \ + im_expntra.c \ + im_fav4.c \ + im_gadd.c \ + im_gaddim.c \ + im_gfadd.c \ + im_invert.c \ + im_linreg.c \ + im_lintra.c \ + im_litecor.c \ + im_log10tra.c \ + im_logtra.c \ + im_max.c \ + im_maxpos.c \ + im_maxpos_avg.c \ + im_maxpos_vec.c \ + im_measure.c \ + im_min.c \ + im_minpos.c \ + im_multiply.c \ + im_powtra.c \ + im_remainder.c \ + im_rint.c \ + im_sign.c \ + im_sintra.c \ + im_stats.c \ + im_subtract.c \ + im_tantra.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/arithmetic/arith_dispatch.c b/libsrc/arithmetic/arith_dispatch.c new file mode 100644 index 00000000..579c7deb --- /dev/null +++ b/libsrc/arithmetic/arith_dispatch.c @@ -0,0 +1,1247 @@ +/* Function dispatch tables for arithmetic. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Image in, number out. + */ +static im_arg_desc image_in_num_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DOUBLE( "value" ) +}; + +/* Call im_abs via arg vector. + */ +static int +abs_vec( im_object *argv ) +{ + return( im_abs( argv[0], argv[1] ) ); +} + +/* Description of im_abs. + */ +static im_function abs_desc = { + "im_abs", /* Name */ + N_( "absolute value" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + abs_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_add via arg vector. + */ +static int +add_vec( im_object *argv ) +{ + return( im_add( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_add. + */ +static im_function add_desc = { + "im_add", /* Name */ + N_( "add two images" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + add_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_avg via arg vector. + */ +static int +avg_vec( im_object *argv ) +{ + double f; + + if( im_avg( argv[0], &f ) ) + return( -1 ); + + *((double *) argv[1]) = f; + return( 0 ); +} + +/* Description of im_avg. + */ +static im_function avg_desc = { + "im_avg", /* Name */ + N_( "average value of image" ), /* Description */ + IM_FN_PIO, /* Flags */ + avg_vec, /* Dispatch function */ + IM_NUMBER( image_in_num_out ), /* Size of arg list */ + image_in_num_out /* Arg list */ +}; + +/* Args to im_point_bilinear. + */ +static im_arg_desc point_bilinear_args[] = { + IM_INPUT_IMAGE ("in"), + IM_INPUT_DOUBLE("x"), + IM_INPUT_DOUBLE("y"), + IM_INPUT_INT("band"), + IM_OUTPUT_DOUBLE("val") +}; + +/* Call im_point_bilinear via arg vector. + */ +static int +point_bilinear_vec( im_object *argv ) +{ + return im_point_bilinear( argv[0], *(double*)argv[1], *(double*)argv[2], *(int*)argv[3], argv[4] ); +} + +/* Description of im_point_bilinear. + */ +static im_function point_bilinear_desc = { + "im_point_bilinear", + "interpolate value at single point, linearly", + IM_FN_PIO, + point_bilinear_vec, + IM_NUMBER( point_bilinear_args ), + point_bilinear_args +}; + +/* Call im_cmulnorm via arg vector. + */ +static int +cmulnorm_vec( im_object *argv ) +{ + return( im_cmulnorm( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_cmulnorm. + */ +static im_function cmulnorm_desc = { + "im_cmulnorm", /* Name */ + N_( "multiply two complex images, normalising output" ), + IM_FN_PIO, /* Flags */ + cmulnorm_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_deviate via arg vector. + */ +static int +deviate_vec( im_object *argv ) +{ + double f; + + if( im_deviate( argv[0], &f ) ) + return( -1 ); + + *((double *) argv[1]) = f; + return( 0 ); +} + +/* Description of im_deviate. + */ +static im_function deviate_desc = { + "im_deviate", /* Name */ + N_( "standard deviation of image" ), /* Description */ + IM_FN_PIO, /* Flags */ + deviate_vec, /* Dispatch function */ + IM_NUMBER( image_in_num_out ), /* Size of arg list */ + image_in_num_out /* Arg list */ +}; + +/* Call im_exp10tra via arg vector. + */ +static int +exp10tra_vec( im_object *argv ) +{ + return( im_exp10tra( argv[0], argv[1] ) ); +} + +/* Description of im_exp10tra. + */ +static im_function exp10tra_desc = { + "im_exp10tra", /* Name */ + N_( "10^pel of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + exp10tra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_exptra via arg vector. + */ +static int +exptra_vec( im_object *argv ) +{ + return( im_exptra( argv[0], argv[1] ) ); +} + +/* Description of im_exptra. + */ +static im_function exptra_desc = { + "im_exptra", /* Name */ + N_( "e^pel of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + exptra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args for im_powtra(). + */ +static im_arg_desc powtra_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "x" ) +}; + +/* Call im_expntra via arg vector. + */ +static int +expntra_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + + return( im_expntra( argv[0], argv[1], a ) ); +} + +/* Description of im_expntra. + */ +static im_function expntra_desc = { + "im_expntra", /* Name */ + N_( "x^pel of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + expntra_vec, /* Dispatch function */ + IM_NUMBER( powtra_args ), /* Size of arg list */ + powtra_args /* Arg list */ +}; + +/* Args for im_expntra_vec(). + */ +static im_arg_desc expntra_vec_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "v" ) +}; + +/* Call im_expntra_vec() via arg vector. + */ +static int +expntra_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_expntra_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_expntra_vec. + */ +static im_function expntra_vec_desc = { + "im_expntra_vec", /* Name */ + N_( "[x,y,z]^pel of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + expntra_vec_vec, /* Dispatch function */ + IM_NUMBER( expntra_vec_args ), /* Size of arg list */ + expntra_vec_args /* Arg list */ +}; + +/* Four images in, one out. + */ +static im_arg_desc fav4_args[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_INPUT_IMAGE( "in3" ), + IM_INPUT_IMAGE( "in4" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_fav4 via arg vector. + */ +static int +fav4_vec( im_object *argv ) +{ + IMAGE *buf[4]; + + buf[0] = argv[0]; + buf[1] = argv[1]; + buf[2] = argv[2]; + buf[3] = argv[3]; + + return( im_fav4( &buf[0], argv[4] ) ); +} + +/* Description of im_fav4. + */ +static im_function fav4_desc = { + "im_fav4", /* Name */ + N_( "average of 4 images" ), + 0, /* Flags */ + fav4_vec, /* Dispatch function */ + IM_NUMBER( fav4_args ), /* Size of arg list */ + fav4_args /* Arg list */ +}; + +/* Call im_divide via arg vector. + */ +static int +divide_vec( im_object *argv ) +{ + return( im_divide( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_divide. + */ +static im_function divide_desc = { + "im_divide", /* Name */ + N_( "divide two images" ), + IM_FN_PIO, /* Flags */ + divide_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args for im_gadd(). + */ +static im_arg_desc gadd_args[] = { + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_IMAGE( "in2" ), + IM_INPUT_DOUBLE( "c" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_gadd() via arg vector. + */ +static int +gadd_vec( im_object *argv ) +{ + double a = *((double *) argv[0]); + double b = *((double *) argv[2]); + double c = *((double *) argv[4]); + + return( im_gadd( a, argv[1], b, argv[3], c, argv[5] ) ); +} + +/* Description of im_gadd(). + */ +static im_function gadd_desc = { + "im_gadd", /* Name */ + N_( "calculate a*in1 + b*in2 + c = outfile" ), + 0, /* Flags */ + gadd_vec, /* Dispatch function */ + IM_NUMBER( gadd_args ), /* Size of arg list */ + gadd_args /* Arg list */ +}; + +/* Call im_invert via arg vector. + */ +static int +invert_vec( im_object *argv ) +{ + return( im_invert( argv[0], argv[1] ) ); +} + +/* Description of im_invert. + */ +static im_function invert_desc = { + "im_invert", /* Name */ + N_( "photographic negative" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + invert_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args for im_lintra(). + */ +static im_arg_desc lintra_args[] = { + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_IMAGE( "in" ), + IM_INPUT_DOUBLE( "b" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_lintra() via arg vector. + */ +static int +lintra_vec( im_object *argv ) +{ + double a = *((double *) argv[0]); + double b = *((double *) argv[2]); + + return( im_lintra( a, argv[1], b, argv[3] ) ); +} + +/* Description of im_lintra(). + */ +static im_function lintra_desc = { + "im_lintra", /* Name */ + N_( "calculate a*in + b = outfile" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + lintra_vec, /* Dispatch function */ + IM_NUMBER( lintra_args ), /* Size of arg list */ + lintra_args /* Arg list */ +}; + +/* Args for im_lintra_vec(). + */ +static im_arg_desc lintra_vec_args[] = { + IM_INPUT_DOUBLEVEC( "a" ), + IM_INPUT_IMAGE( "in" ), + IM_INPUT_DOUBLEVEC( "b" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_lintra_vec() via arg vector. + */ +static int +lintra_vec_vec( im_object *argv ) +{ + im_doublevec_object *dva = (im_doublevec_object *) argv[0]; + im_doublevec_object *dvb = (im_doublevec_object *) argv[2]; + + if( dva->n != dvb->n ) { + im_error( "im_lintra_vec", _( "vectors not same length" ) ); + return( -1 ); + } + + return( im_lintra_vec( dva->n, dva->vec, argv[1], dvb->vec, argv[3] ) ); +} + +/* Description of im_lintra_vec(). + */ +static im_function lintra_vec_desc = { + "im_lintra_vec", /* Name */ + N_( "calculate a*in + b -> out, a and b vectors" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + lintra_vec_vec, /* Dispatch function */ + IM_NUMBER( lintra_vec_args ), /* Size of arg list */ + lintra_vec_args /* Arg list */ +}; + +/* Args for im_litecor(). + */ +static im_arg_desc litecor_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_IMAGE( "white" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "clip" ), + IM_INPUT_DOUBLE( "factor" ) +}; + +/* Call im_litecor() via arg vector. + */ +static int +litecor_vec( im_object *argv ) +{ + int clip = *((int *) argv[3]); + double factor = *((double *) argv[4]); + + return( im_litecor( argv[0], argv[1], argv[2], clip, factor ) ); +} + +/* Description of im_litecor(). + */ +static im_function litecor_desc = { + "im_litecor", /* Name */ + N_( "calculate max(white)*factor*(in/white), if clip == 1" ), + 0, /* Flags */ + litecor_vec, /* Dispatch function */ + IM_NUMBER( litecor_args ), /* Size of arg list */ + litecor_args /* Arg list */ +}; + +/* Call im_log10tra via arg vector. + */ +static int +log10tra_vec( im_object *argv ) +{ + return( im_log10tra( argv[0], argv[1] ) ); +} + +/* Description of im_log10tra. + */ +static im_function log10tra_desc = { + "im_log10tra", /* Name */ + N_( "log10 of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + log10tra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_logtra via arg vector. + */ +static int +logtra_vec( im_object *argv ) +{ + return( im_logtra( argv[0], argv[1] ) ); +} + +/* Description of im_logtra. + */ +static im_function logtra_desc = { + "im_logtra", /* Name */ + N_( "ln of image" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + logtra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_tantra via arg vector. + */ +static int +tantra_vec( im_object *argv ) +{ + return( im_tantra( argv[0], argv[1] ) ); +} + +/* Description of im_tantra. + */ +static im_function tantra_desc = { + "im_tantra", /* Name */ + N_( "tan of image (angles in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + tantra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_atantra via arg vector. + */ +static int +atantra_vec( im_object *argv ) +{ + return( im_atantra( argv[0], argv[1] ) ); +} + +/* Description of im_atantra. + */ +static im_function atantra_desc = { + "im_atantra", /* Name */ + N_( "atan of image (result in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + atantra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_costra via arg vector. + */ +static int +costra_vec( im_object *argv ) +{ + return( im_costra( argv[0], argv[1] ) ); +} + +/* Description of im_costra. + */ +static im_function costra_desc = { + "im_costra", /* Name */ + N_( "cos of image (angles in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + costra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_acostra via arg vector. + */ +static int +acostra_vec( im_object *argv ) +{ + return( im_acostra( argv[0], argv[1] ) ); +} + +/* Description of im_acostra. + */ +static im_function acostra_desc = { + "im_acostra", /* Name */ + N_( "acos of image (result in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + acostra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_ceil via arg vector. + */ +static int +ceil_vec( im_object *argv ) +{ + return( im_ceil( argv[0], argv[1] ) ); +} + +/* Description of im_ceil. + */ +static im_function ceil_desc = { + "im_ceil", /* Name */ + N_( "round to smallest integal value not less than" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + ceil_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_floor via arg vector. + */ +static int +floor_vec( im_object *argv ) +{ + return( im_floor( argv[0], argv[1] ) ); +} + +/* Description of im_floor. + */ +static im_function floor_desc = { + "im_floor", /* Name */ + N_( "round to largest integal value not greater than" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + floor_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_rint via arg vector. + */ +static int +rint_vec( im_object *argv ) +{ + return( im_rint( argv[0], argv[1] ) ); +} + +/* Description of im_rint. + */ +static im_function rint_desc = { + "im_rint", /* Name */ + N_( "round to nearest integal value" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + rint_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_sintra via arg vector. + */ +static int +sintra_vec( im_object *argv ) +{ + return( im_sintra( argv[0], argv[1] ) ); +} + +/* Description of im_sintra. + */ +static im_function sintra_desc = { + "im_sintra", /* Name */ + N_( "sin of image (angles in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + sintra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_bandmean via arg vector. + */ +static int +bandmean_vec( im_object *argv ) +{ + return( im_bandmean( argv[0], argv[1] ) ); +} + +/* Description of im_bandmean. + */ +static im_function bandmean_desc = { + "im_bandmean", /* Name */ + N_( "average image bands" ), + IM_FN_PIO, /* Flags */ + bandmean_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_sign via arg vector. + */ +static int +sign_vec( im_object *argv ) +{ + return( im_sign( argv[0], argv[1] ) ); +} + +/* Description of im_sign. + */ +static im_function sign_desc = { + "im_sign", /* Name */ + N_( "unit vector in direction of value" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + sign_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_asintra via arg vector. + */ +static int +asintra_vec( im_object *argv ) +{ + return( im_asintra( argv[0], argv[1] ) ); +} + +/* Description of im_asintra. + */ +static im_function asintra_desc = { + "im_asintra", /* Name */ + N_( "asin of image (result in degrees)" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + asintra_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_max via arg vector. + */ +static int +max_vec( im_object *argv ) +{ + double f; + + if( im_max( argv[0], &f ) ) + return( -1 ); + *((double *) argv[1]) = f; + + return( 0 ); +} + +/* Description of im_max. + */ +static im_function max_desc = { + "im_max", /* Name */ + N_( "maximum value of image" ), /* Description */ + IM_FN_PIO, /* Flags */ + max_vec, /* Dispatch function */ + IM_NUMBER( image_in_num_out ), /* Size of arg list */ + image_in_num_out /* Arg list */ +}; + +/* Args for maxpos (and minpos). + */ +static im_arg_desc maxpos_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_COMPLEX( "position" ) +}; + +/* Call im_maxpos via arg vector. + */ +static int +maxpos_vec( im_object *argv ) +{ + double f; + int x, y; + + if( im_maxpos( argv[0], &x, &y, &f ) ) + return( -1 ); + + ((double *) argv[1])[0] = x; + ((double *) argv[1])[1] = y; + + return( 0 ); +} + +/* Description of im_maxpos. + */ +static im_function maxpos_desc = { + "im_maxpos", /* Name */ + N_( "position of maximum value of image" ), + 0, /* Flags */ + maxpos_vec, /* Dispatch function */ + IM_NUMBER( maxpos_args ), /* Size of arg list */ + maxpos_args /* Arg list */ +}; + +/* Args to im_maxpos_avg. + */ +static im_arg_desc maxpos_avg_args[] = { + IM_INPUT_IMAGE ("in"), + IM_OUTPUT_DOUBLE("x"), + IM_OUTPUT_DOUBLE("y"), + IM_OUTPUT_DOUBLE("out") +}; + +/* Call im_maxpos_avg via arg vector. + */ +static int +maxpos_avg_vec( im_object *argv ) +{ + return im_maxpos_avg( argv[0], argv[1], argv[2], argv[3] ); +} + +/* Description of im_maxpos_avg. + */ +static im_function maxpos_avg_desc = { + "im_maxpos_avg", + "position of maximum value of image, averaging in case of draw", + IM_FN_PIO, + maxpos_avg_vec, + IM_NUMBER( maxpos_avg_args ), + maxpos_avg_args +}; + +/* Args for measure. + */ +static im_arg_desc measure_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DMASK( "mask" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ), + IM_INPUT_INT( "w" ), + IM_INPUT_INT( "h" ), + IM_INPUT_INT( "h_patches" ), + IM_INPUT_INT( "v_patches" ) +}; + +/* Call im_measure via arg vector. + */ +static int +measure_vec( im_object *argv ) +{ + IMAGE_BOX box; + int h, v, i; + int *sel; + int nsel; + im_mask_object *mo = argv[1]; + + box.xstart = *((int *) argv[2]); + box.ystart = *((int *) argv[3]); + box.xsize = *((int *) argv[4]); + box.ysize = *((int *) argv[5]); + box.chsel = 0; + + h = *((int *) argv[6]); + v = *((int *) argv[7]); + + nsel = h * v; + if( !(sel = IM_ARRAY( NULL, nsel, int )) ) + return( -1 ); + for( i = 0; i < nsel; i++ ) + sel[i] = i + 1; + + if( !(mo->mask = + im_measure( argv[0], &box, h, v, sel, nsel, mo->name )) ) { + im_free( sel ); + return( -1 ); + } + im_free( sel ); + + return( 0 ); +} + +/* Description of im_measure. + */ +static im_function measure_desc = { + "im_measure", /* Name */ + N_( "measure averages of a grid of patches" ), + IM_FN_PIO, /* Flags */ + measure_vec, /* Dispatch function */ + IM_NUMBER( measure_args ), /* Size of arg list */ + measure_args /* Arg list */ +}; + +/* Call im_min via arg vector. + */ +static int +min_vec( im_object *argv ) +{ + double f; + + if( im_min( argv[0], &f ) ) + return( -1 ); + *((double *) argv[1]) = f; + + return( 0 ); +} + +/* Description of im_min. + */ +static im_function min_desc = { + "im_min", /* Name */ + N_( "minimum value of image" ), /* Description */ + IM_FN_PIO, /* Flags */ + min_vec, /* Dispatch function */ + IM_NUMBER( image_in_num_out ), /* Size of arg list */ + image_in_num_out /* Arg list */ +}; + +/* Call im_minpos via arg vector. + */ +static int +minpos_vec( im_object *argv ) +{ + double f; + int x, y; + + if( im_minpos( argv[0], &x, &y, &f ) ) + return( -1 ); + + ((double *) argv[1])[0] = x; + ((double *) argv[1])[1] = y; + + return( 0 ); +} + +/* Description of im_minpos. + */ +static im_function minpos_desc = { + "im_minpos", /* Name */ + N_( "position of minimum value of image" ), + 0, /* Flags */ + minpos_vec, /* Dispatch function */ + IM_NUMBER( maxpos_args ), /* Size of arg list */ + maxpos_args /* Arg list */ +}; + +/* Call im_remainder via arg vector. + */ +static int +remainder_vec( im_object *argv ) +{ + return( im_remainder( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_remainder. + */ +static im_function remainder_desc = { + "im_remainder", /* Name */ + N_( "remainder after integer division" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + remainder_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_remainderconst via arg vector. + */ +static int +remainderconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_remainderconst( argv[0], argv[1], c ) ); +} + +/* Args for im_remainderconst(). + */ +static im_arg_desc remainderconst_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "x" ) +}; + +/* Description of im_remainderconst. + */ +static im_function remainderconst_desc = { + "im_remainderconst", /* Name */ + N_( "remainder after integer division by a constant" ),/* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + remainderconst_vec, /* Dispatch function */ + IM_NUMBER( remainderconst_args ), /* Size of arg list */ + remainderconst_args /* Arg list */ +}; + +/* Call im_remainderconst_vec via arg vector. + */ +static int +remainderconst_vec_vec( im_object *argv ) +{ + im_doublevec_object *dv = (im_doublevec_object *) argv[2]; + + return( im_remainderconst_vec( argv[0], argv[1], dv->n, dv->vec ) ); +} + +/* Args for im_remainderconst_vec(). + */ +static im_arg_desc remainderconst_vec_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "x" ) +}; + +/* Description of im_remainderconst_vec. + */ +static im_function remainderconst_vec_desc = { + "im_remainderconst_vec", /* Name */ + N_( "remainder after integer division by a vector of constants" ), + /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + remainderconst_vec_vec, /* Dispatch function */ + IM_NUMBER( remainderconst_vec_args ), /* Size of arg list */ + remainderconst_vec_args /* Arg list */ +}; + +/* Call im_multiply via arg vector. + */ +static int +multiply_vec( im_object *argv ) +{ + return( im_multiply( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_multiply. + */ +static im_function multiply_desc = { + "im_multiply", /* Name */ + N_( "multiply two images" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + multiply_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_powtra() via arg vector. + */ +static int +powtra_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + + return( im_powtra( argv[0], argv[1], a ) ); +} + +/* Description of im_powtra(). + */ +static im_function powtra_desc = { + "im_powtra", /* Name */ + N_( "pel^x ofbuildimage" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + powtra_vec, /* Dispatch function */ + IM_NUMBER( powtra_args ), /* Size of arg list */ + powtra_args /* Arg list */ +}; + +/* Call im_powtra_vec() via arg vector. + */ +static int +powtra_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_powtra_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_powtra_vec(). + */ +static im_function powtra_vec_desc = { + "im_powtra_vec", /* Name */ + N_( "pel^[x,y,z] of image" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + powtra_vec_vec, /* Dispatch function */ + IM_NUMBER( expntra_vec_args ), /* Size of arg list */ + expntra_vec_args /* Arg list */ +}; + +/* Args for im_stats. + */ +static im_arg_desc stats_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DMASK_STATS( "statistics" ) +}; + +/* Call im_stats() via arg vector. + */ +static int +stats_vec( im_object *argv ) +{ + im_mask_object *mo = argv[1]; + + if( !(mo->mask = im_stats( argv[0] )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_stats(). + */ +static im_function stats_desc = { + "im_stats", /* Name */ + N_( "many image statistics in one pass" ), + IM_FN_PIO, /* Flags */ + stats_vec, /* Dispatch function */ + IM_NUMBER( stats_args ), /* Size of arg list */ + stats_args /* Arg list */ +}; + +/* Call im_subtract via arg vector. + */ +static int +subtract_vec( im_object *argv ) +{ + return( im_subtract( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_subtract. + */ +static im_function subtract_desc = { + "im_subtract", /* Name */ + N_( "subtract two images" ), /* Description */ + IM_FN_PIO, /* Flags */ + subtract_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args for im_linreg. + */ +static im_arg_desc linreg_args[] = { + IM_INPUT_IMAGEVEC( "ins" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "xs" ) +}; + +/* Call im_linreg() via arg vector. + */ +static int +linreg_vec( im_object *argv ) +{ +#define FUNCTION_NAME "im_linreg_vec" + im_imagevec_object *ins_vec= (im_imagevec_object*) argv[0]; + im_doublevec_object *xs_vec= (im_doublevec_object*) argv[2]; + IMAGE *out= (IMAGE*) argv[1]; + IMAGE **ins= IM_ARRAY( out, ins_vec-> n + 1, IMAGE* ); + int i; + + if( ! ins ) + return -1; + + for( i= 0; i < ins_vec-> n; ++i ) + ins[ i ]= ins_vec-> vec[ i ]; + + ins[ ins_vec-> n ]= NULL; + + if( xs_vec-> n != ins_vec-> n ){ + im_error( FUNCTION_NAME, "image vector and x vector differ in length" ); + return -1; + } + return im_linreg( ins, out, xs_vec-> vec ); + +#undef FUNCTION_NAME +} + +/* Description of im_linreg(). + */ +static im_function linreg_desc = { + "im_linreg", /* Name */ + N_( "pixelwise linear regression" ), + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + linreg_vec, /* Dispatch function */ + IM_NUMBER( linreg_args ), /* Size of arg list */ + linreg_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *arith_list[] = { + &abs_desc, + &acostra_desc, + &add_desc, + &asintra_desc, + &atantra_desc, + &avg_desc, + &point_bilinear_desc, + &bandmean_desc, + &ceil_desc, + &cmulnorm_desc, + &costra_desc, + &deviate_desc, + ÷_desc, + &exp10tra_desc, + &expntra_desc, + &expntra_vec_desc, + &exptra_desc, + &fav4_desc, + &floor_desc, + &gadd_desc, + &invert_desc, + &lintra_desc, + &linreg_desc, + &lintra_vec_desc, + &litecor_desc, + &log10tra_desc, + &logtra_desc, + &max_desc, + &maxpos_desc, + &maxpos_avg_desc, + &measure_desc, + &min_desc, + &minpos_desc, + &multiply_desc, + &powtra_desc, + &powtra_vec_desc, + &remainder_desc, + &remainderconst_desc, + &remainderconst_vec_desc, + &rint_desc, + &sign_desc, + &sintra_desc, + &stats_desc, + &subtract_desc, + &tantra_desc +}; + +/* Package of functions. + */ +im_package im__arithmetic = { + "arithmetic", + IM_NUMBER( arith_list ), + arith_list +}; diff --git a/libsrc/arithmetic/im_abs.c b/libsrc/arithmetic/im_abs.c new file mode 100644 index 00000000..e9dabcdc --- /dev/null +++ b/libsrc/arithmetic/im_abs.c @@ -0,0 +1,241 @@ +/* @(#) Find the absolute value of an image. Copy for UNSIGNED types, negate + * @(#) for int types, fabs() for float types, and calculate modulus for + * @(#) complex types. + * @(#) + * @(#) int + * @(#) im_abs( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 J.Cupitt + * - adapted from im_lintra to work with partial images + * - complex and signed support added + * 30/6/93 JC + * - adapted for partial v2 + * - ANSI conversion + * - spe29873r6k3h()**!@lling errors removed + * 9/2/95 JC + * - adapted for im_wrap... + * 20/6/02 JC + * - tiny speed up + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Integer abs operation: just test and negate. + */ +#define intabs(TYPE) \ + { \ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE v = p[x]; \ + \ + if( v < 0 ) \ + q[x] = 0 - v; \ + else \ + q[x] = v; \ + } \ + } + +/* Float abs operation: call fabs(). + */ +#define floatabs(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = fabs( p[x] );\ + } + +/* Complex abs operation: calculate modulus. + */ +#define complexabs(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double rp = p[0];\ + double ip = p[1];\ + \ + p += 2;\ + q[x] = sqrt( rp * rp + ip * ip );\ + }\ + } + +/* Abs a buffer of PELs. + */ +static void +abs_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Abs all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: +#ifdef HAVE_LIBOIL + oil_abs_u8_s8( (uint8_t *) out, sizeof( uint8_t ), + (int8_t *) in, sizeof( int8_t ), sz ); +#else /*!HAVE_LIBOIL*/ + intabs( signed char ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_SHORT: +#ifdef HAVE_LIBOIL + oil_abs_u16_s16( (uint16_t *) out, sizeof( uint16_t ), + (int16_t *) in, sizeof( int16_t ), sz ); +#else /*!HAVE_LIBOIL*/ + intabs( signed short ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_INT: +#ifdef HAVE_LIBOIL + oil_abs_u32_s32( (uint32_t *) out, sizeof( uint32_t ), + (int32_t *) in, sizeof( int32_t ), sz ); +#else /*!HAVE_LIBOIL*/ + intabs( signed int ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_abs_f32_f32( (float *) out, sizeof( float ), + (float *) in, sizeof( float ), sz ); +#else /*!HAVE_LIBOIL*/ + floatabs( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: +#ifdef HAVE_LIBOIL + oil_abs_f64_f64( (double *) out, sizeof( double ), + (double *) in, sizeof( double ), sz ); +#else /*!HAVE_LIBOIL*/ + floatabs( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_COMPLEX: complexabs( float ); break; + case IM_BANDFMT_DPCOMPLEX: complexabs( double ); break; + + default: + assert( 0 ); + } +} + +/* Abs of image. + */ +int +im_abs( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_abs", _( "not uncoded" ) ); + return( -1 ); + } + + /* Is this one of the unsigned types? Degenerate to im_copy() if it + * is. + */ + if( im_isuint( in ) ) + return( im_copy( in, out ) ); + + /* Prepare output header. Output type == input type, except for + * complex. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_INT: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + /* No action. + */ + break; + + case IM_BANDFMT_COMPLEX: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_DPCOMPLEX: + out->Bbits = IM_BBITS_DOUBLE; + out->BandFmt = IM_BANDFMT_DOUBLE; + break; + + default: + im_error( "im_abs", _( "unknown input type" ) ); + return( -1 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) abs_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_add.c b/libsrc/arithmetic/im_add.c new file mode 100644 index 00000000..df9c31f8 --- /dev/null +++ b/libsrc/arithmetic/im_add.c @@ -0,0 +1,302 @@ +/* @(#) Add two vasari images + * @(#) Function im_add() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int + * @(#) im_add(in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 29/4/93 J.Cupitt + * - now works for partial images + * 1/7/93 JC + * - adapted for partial v2 + * 9/5/95 JC + * - simplified: now just handles 10 cases (instead of 50), using + * im_clip2*() to help + * - now uses im_wrapmany() rather than im_generate() + * 31/5/96 JC + * - SWAP() removed, *p++ removed + * 27/9/04 + * - im__cast_and_call() now matches bands as well + * - ... so 1 band + 4 band image -> 4 band image + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Complex add. + */ +#define cloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double rp1 = p1[0];\ + double ip1 = p1[1];\ + \ + double rp2 = p2[0];\ + double ip2 = p2[1];\ + \ + p1 += 2;\ + p2 += 2;\ + \ + q[0] = rp1 + rp2;\ + q[1] = ip1 + ip2;\ + \ + q += 2;\ + }\ +} + +/* Real add. + */ +#define rloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p1[x] + p2[x];\ +} + +static void +add_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Add all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: rloop( signed char ); break; + case IM_BANDFMT_UCHAR: rloop( unsigned char ); break; + case IM_BANDFMT_SHORT: rloop( signed short ); break; + case IM_BANDFMT_USHORT: rloop( unsigned short ); break; + case IM_BANDFMT_INT: rloop( signed int ); break; + case IM_BANDFMT_UINT: rloop( unsigned int ); break; + + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_add_f32( (float *) out, + (float *) in[0], (float *) in[1], sz ); +#else /*!HAVE_LIBOIL*/ + rloop( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: rloop( double ); break; + case IM_BANDFMT_COMPLEX: cloop( float ); break; + case IM_BANDFMT_DPCOMPLEX: cloop( double ); break; + + default: + assert( 0 ); + } +} + +/* Save a bit of typing. + */ +#define UC IM_BANDFMT_UCHAR +#define C IM_BANDFMT_CHAR +#define US IM_BANDFMT_USHORT +#define S IM_BANDFMT_SHORT +#define UI IM_BANDFMT_UINT +#define I IM_BANDFMT_INT +#define F IM_BANDFMT_FLOAT +#define M IM_BANDFMT_COMPLEX +#define D IM_BANDFMT_DOUBLE +#define DM IM_BANDFMT_DPCOMPLEX + +/* Type conversions for two integer inputs. Rules for float and complex + * encoded with ifs. We are sign and value preserving. + */ +static int iformat[6][6] = { + /* UC C US S UI I */ +/* UC */ { US, S, UI, I, UI, I }, +/* C */ { S, S, I, I, I, I }, +/* US */ { UI, I, UI, I, UI, I }, +/* S */ { I, I, I, I, I, I }, +/* UI */ { UI, I, UI, I, UI, I }, +/* I */ { I, I, I, I, I, I } +}; + +/* Make an n-band image. Input 1 or n bands. + */ +static int +im__bandup( IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *bands[256]; + int i; + + if( in->Bands == n ) + return( im_copy( in, out ) ); + if( in->Bands != 1 ) { + im_error( "im__bandup", _( "not one band or %d bands" ), n ); + return( -1 ); + } + if( n > 256 || n < 1 ) { + im_error( "im__bandup", _( "bad bands" ) ); + return( -1 ); + } + + for( i = 0; i < n; i++ ) + bands[i] = in; + + return( im_gbandjoin( bands, out, n ) ); +} + +/* Convert in1 and in2 to the bands and type of out, and call the function. + * Also used by subtract, multiply, divide, etc. + */ +int +im__cast_and_call( IMAGE *in1, IMAGE *in2, IMAGE *out, + im_wrapmany_fn fn, void *a ) +{ + IMAGE *t[5]; + + if( im_open_local_array( out, t, 4, "type cast:1", "p" ) ) + return( -1 ); + + /* Make sure bbits is set correctly. + */ + out->Bbits = im_bits_of_fmt( out->BandFmt ); + + /* Cast our input images up to the same type as the output. + */ + if( im_clip2fmt( in1, t[0], out->BandFmt ) || + im_clip2fmt( in2, t[1], out->BandFmt ) ) + return( -1 ); + + /* Force bands up to the same as out. + */ + if( im__bandup( t[0], t[2], out->Bands ) || + im__bandup( t[1], t[3], out->Bands ) ) + return( -1 ); + + /* And add! + */ + t[4] = NULL; + if( im_wrapmany( t + 2, out, fn, out, a ) ) + return( -1 ); + + return( 0 ); +} + +int +im_add( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_add", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_add", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* What output type will we write? int, float or complex. + */ + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + /* What kind of complex? + */ + if( in1->BandFmt == IM_BANDFMT_DPCOMPLEX || + in2->BandFmt == IM_BANDFMT_DPCOMPLEX ) + /* Output will be DPCOMPLEX. + */ + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + else + out->BandFmt = IM_BANDFMT_COMPLEX; + + } + else if( im_isfloat( in1 ) || im_isfloat( in2 ) ) { + /* What kind of float? + */ + if( in1->BandFmt == IM_BANDFMT_DOUBLE || + in2->BandFmt == IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_DOUBLE; + else + out->BandFmt = IM_BANDFMT_FLOAT; + } + else + /* Must be int+int -> int. + */ + out->BandFmt = iformat[in1->BandFmt][in2->BandFmt]; + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) add_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} diff --git a/libsrc/arithmetic/im_avg.c b/libsrc/arithmetic/im_avg.c new file mode 100644 index 00000000..960fd9c7 --- /dev/null +++ b/libsrc/arithmetic/im_avg.c @@ -0,0 +1,203 @@ +/* @(#) Find the average of an image. Takes any non-complex image format, + * @(#) returns a double. Finds the average of all bands. + * @(#) + * @(#) int + * @(#) im_avg( im, out ) + * @(#) IMAGE *im; + * @(#) double *out; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/08/1990 + * Modified on: + * 5/5/93 JC + * - now does partial images + * - less likely to overflow + * 1/7/93 JC + * - adapted for partial v2 + * - ANSI C + * 21/2/95 JC + * - modernised again + * 11/5/95 JC + * - oops! return( NULL ) in im_avg(), instead of return( -1 ) + * 20/6/95 JC + * - now returns double + * 13/1/05 + * - use 64 bit arithmetic + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Start function: allocate space for a double in which we can accululate the + * sum. + */ +static void * +start_fn( IMAGE *out ) +{ + double *tmp; + + if( !(tmp = IM_ARRAY( out, 1, double )) ) + return( NULL ); + *tmp = 0.0; + + return( (void *) tmp ); +} + +/* Stop function. Add this little sum to the main sum. + */ +static int +stop_fn( double *tmp, double *sum ) +{ + *sum += *tmp; + + return( 0 ); +} + +/* Loop over region, accumulating a sum in *tmp. + */ +static int +scan_fn( REGION *reg, double *tmp ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( reg ); + double sum = 0.0; + int x, y; + +/* Sum pels in this section. + */ +#define loop(TYPE) \ + { TYPE *p;\ + \ + for( y = to; y < bo; y++ ) { \ + p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < sz; x++ ) \ + sum += *p++;\ + }\ + } + + /* Now generate code for all types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char); break; + case IM_BANDFMT_CHAR: loop(signed char); break; + case IM_BANDFMT_USHORT: loop(unsigned short); break; + case IM_BANDFMT_SHORT: loop(signed short); break; + case IM_BANDFMT_UINT: loop(unsigned int); break; + case IM_BANDFMT_INT: loop(signed int); break; + case IM_BANDFMT_FLOAT: loop(float); break; + + case IM_BANDFMT_DOUBLE: +#ifdef HAVE_LIBOIL + for( y = to; y < bo; y++ ) { + double *p = (double *) IM_REGION_ADDR( reg, le, y ); + double t; + + oil_sum_f64( &t, p, sizeof( double ), sz ); + + sum += t; + } +#else /*!HAVE_LIBOIL*/ + loop(double); +#endif /*HAVE_LIBOIL*/ + break; + + default: + assert( 0 ); + } + + /* Add to sum for this sequence. + */ + *tmp += sum; + + return( 0 ); +} + +/* Find the average of an image. + */ +int +im_avg( IMAGE *in, double *out ) +{ + double sum = 0.0; + gint64 vals, pels; + + /* Check our args. + */ + if( im_pincheck( in ) ) + return( -1 ); + if( im_iscomplex( in ) ) { + im_error( "im_avg", _( "bad input type" ) ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_avg", _( "not uncoded" ) ); + return( -1 ); + } + + /* Loop over input, summing pixels. + */ + if( im_iterate( in, start_fn, scan_fn, stop_fn, &sum, NULL ) ) + return( -1 ); + + /* Calculate and return average. + */ + pels = (gint64) in->Xsize * in->Ysize; + vals = pels * in->Bands; + *out = sum / vals; + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_bandmean.c b/libsrc/arithmetic/im_bandmean.c new file mode 100644 index 00000000..721eb6eb --- /dev/null +++ b/libsrc/arithmetic/im_bandmean.c @@ -0,0 +1,161 @@ +/* @(#) Average the bands in an image. + * @(#) + * @(#) int + * @(#) im_bandmean(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Author: Simon Goodall + * Written on: 17/7/07 + * 17/7/07 JC + * - hacked about a bit + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Int types. Round, keep sum in a larger variable. + */ +#define ILOOP( TYPE, STYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + TYPE *q1 = (TYPE *) q; \ + \ + for( i = 0; i < n; i++ ) { \ + STYPE sum; \ + \ + sum = 0; \ + for( j = 0; j < b; j++ ) \ + sum += p1[j]; \ + *q1++ = sum > 0 ? (sum + b / 2) / b : (sum - b / 2) / b; \ + p1 += b; \ + } \ +} + +/* Float loop. No rounding, sum in same container. + */ +#define FLOOP( TYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + TYPE *q1 = (TYPE *) q; \ + \ + for( i = 0; i < n; i++ ) { \ + TYPE sum; \ + \ + sum = 0; \ + for( j = 0; j < b; j++ ) \ + sum += p1[j]; \ + *q1++ = sum / b; \ + p1 += b; \ + } \ +} + +/* Complex loop. Mean reals and imaginaries separately. + */ +#define CLOOP( TYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + TYPE *q1 = (TYPE *) q; \ + \ + for( i = 0; i < n * 2; i += 2 ) { \ + TYPE sum; \ + \ + sum = 0; \ + for( j = 0; j < b; j++ ) \ + sum += p1[j * 2]; \ + q1[0] = sum / b; \ + sum = 0; \ + for( j = 0; j < b; j++ ) \ + sum += p1[j * 2 + 1]; \ + q1[1] = sum / b; \ + p1 += b; \ + q1 += 2; \ + } \ +} + +static void +bandmean_buffer( PEL *p, PEL *q, int n, IMAGE *in ) +{ + int i, j; + const int b = in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: ILOOP( signed char, int ); break; + case IM_BANDFMT_UCHAR: ILOOP( unsigned char, unsigned int ); break; + case IM_BANDFMT_SHORT: ILOOP( signed short, int ); break; + case IM_BANDFMT_USHORT: ILOOP( unsigned short, unsigned int ); break; + case IM_BANDFMT_INT: ILOOP( signed int, int ); break; + case IM_BANDFMT_UINT: ILOOP( unsigned int, unsigned int ); break; + case IM_BANDFMT_FLOAT: FLOOP( float ); break; + case IM_BANDFMT_DOUBLE: FLOOP( double ); break; + case IM_BANDFMT_COMPLEX: CLOOP( float ); break; + case IM_BANDFMT_DPCOMPLEX: CLOOP( double ); break; + + default: + assert( 0 ); + } +} + +int +im_bandmean( IMAGE *in, IMAGE *out ) +{ + /* Check input params + */ + if( in->Bands == 1 ) + return( im_copy( in, out ) ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_bandmean", _( "uncoded multiband only" ) ); + return( -1 ); + } + + /* Prepare output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = 1; + out->Type = IM_TYPE_B_W; + + /* And process! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) bandmean_buffer, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/arithmetic/im_ceil.c b/libsrc/arithmetic/im_ceil.c new file mode 100644 index 00000000..9d85a6e9 --- /dev/null +++ b/libsrc/arithmetic/im_ceil.c @@ -0,0 +1,114 @@ +/* @(#) ceil() an image ... no promotion, so output type == input type + * @(#) + * @(#) int + * @(#) im_ceil( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 20/6/02 JC + * - adapted from im_abs() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define ceil_loop(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = ceil( p[x] );\ + } + +/* Ceil a buffer of PELs. + */ +static void +ceil_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_FLOAT: ceil_loop(float); break; + case IM_BANDFMT_DOUBLE: ceil_loop(double); break; + case IM_BANDFMT_COMPLEX: sz *= 2; ceil_loop(float); break; + case IM_BANDFMT_DPCOMPLEX: sz *= 2; ceil_loop(double); break; + + default: + assert( 0 ); + } +} + +/* Ceil of image. + */ +int +im_ceil( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_ceil", _( "not uncoded" ) ); + return( -1 ); + } + + /* Is this one of the int types? Degenerate to im_copy() if it + * is. + */ + if( im_isint( in ) ) + return( im_copy( in, out ) ); + + /* Output type == input type. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Generate! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) ceil_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_cmulnorm.c b/libsrc/arithmetic/im_cmulnorm.c new file mode 100644 index 00000000..22eefbb6 --- /dev/null +++ b/libsrc/arithmetic/im_cmulnorm.c @@ -0,0 +1,75 @@ +/* @(#) Multiplies two complex images. complex output is normalised to 1 + * @(#) Inputs can be complex double or complex float + * @(#) Result (double complex or float complex) depends on inputs + * @(#) Function im_cmulnorm() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and must be complex + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int im_cmulnorm(in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 15/4/97 JC + * - thrown away and redone in terms of im_multiply() + * 9/7/02 JC + * - im_sign() broken out, done in terms of that + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_cmulnorm( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_cmulnorm:1", "p" ); + + if( !t1 || im_multiply( in1, in2, t1 ) || im_sign( t1, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_costra.c b/libsrc/arithmetic/im_costra.c new file mode 100644 index 00000000..9330ab5b --- /dev/null +++ b/libsrc/arithmetic/im_costra.c @@ -0,0 +1,239 @@ +/* @(#) Find cos of any non-complex image. Output is always float for integer + * @(#) input and double for double input. All angles in degrees. + * @(#) + * @(#) int + * @(#) im_costra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 24/2/95 JC + * - im_logtra() adapted to make im_costra() + * - adapted for im_wrapone() + * 26/1/96 JC + * - im_acostra() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = cos( IM_RAD( (double) p[x] ) );\ + } + +/* cos a buffer of PELs. + */ +static void +costra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: loop( signed char, float ); break; + case IM_BANDFMT_USHORT: loop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: loop( signed short, float ); break; + case IM_BANDFMT_UINT: loop( unsigned int, float ); break; + case IM_BANDFMT_INT: loop( signed int, float ); break; + case IM_BANDFMT_FLOAT: loop( float, float ); break; + case IM_BANDFMT_DOUBLE: loop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Cos transform. + */ +int +im_costra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_costra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_costra", _( "bad input type" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) costra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* And acos(). + */ +#define aloop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = IM_DEG( acos( (double) p[x] ) );\ + } + +/* acos a buffer of PELs. + */ +static void +acostra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: aloop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: aloop( signed char, float ); break; + case IM_BANDFMT_USHORT: aloop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: aloop( signed short, float ); break; + case IM_BANDFMT_UINT: aloop( unsigned int, float ); break; + case IM_BANDFMT_INT: aloop( signed int, float ); break; + case IM_BANDFMT_FLOAT: aloop( float, float ); break; + case IM_BANDFMT_DOUBLE: aloop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Acos transform. + */ +int +im_acostra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_acostra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_acostra", _( "bad input type" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) acostra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_deviate.c b/libsrc/arithmetic/im_deviate.c new file mode 100644 index 00000000..93cf0cd0 --- /dev/null +++ b/libsrc/arithmetic/im_deviate.c @@ -0,0 +1,222 @@ +/* @(#) Find the standard deviation of an image. Takes any non-complex image + * @(#) format, returns a double. Finds the deviation of all bands. + * @(#) + * @(#) int + * @(#) im_deviate( im, out ) + * @(#) IMAGE *im; + * @(#) double *out; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/08/1990 + * Modified on: + * 5/5/93 JC + * - now does partial images + * - less likely to overflow + * - adapted from im_avg + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 21/2/95 JC + * - modernised again + * 11/5/95 JC + * - oops! return( NULL ) in im_avg(), instead of return( -1 ) + * 20/6/95 JC + * - now returns double, not float + * 13/1/05 + * - use 64 bit arithmetic + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Start function: allocate space for a pair of doubles in which we can + * accumulate the sum and the sum of squares. + */ +static void * +start_fn( IMAGE *out ) +{ + double *tmp; + + if( !(tmp = IM_ARRAY( out, 2, double )) ) + return( NULL ); + tmp[0] = 0.0; + tmp[1] = 0.0; + + return( (void *) tmp ); +} + +/* Stop function. Add this little sum to the main sum. + */ +static int +stop_fn( double *tmp, double *sum ) +{ + sum[0] += tmp[0]; + sum[1] += tmp[1]; + + return( 0 ); +} + +/* Loop over region, adding information to the appropriate fields of tmp. + */ +static int +scan_fn( REGION *reg, double *tmp ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( reg ); + double s = 0.0; + double s2 = 0.0; + int x, y; + +/* Sum pels in this section. + */ +#define loop(TYPE) \ + { TYPE *p; \ + \ + for( y = to; y < bo; y++ ) { \ + p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE v = p[x]; \ + \ + s += v; \ + s2 += (double) v * (double) v; \ + }\ + }\ + } + + /* Now generate code for all types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char); break; + case IM_BANDFMT_CHAR: loop(signed char); break; + case IM_BANDFMT_USHORT: loop(unsigned short); break; + case IM_BANDFMT_SHORT: loop(signed short); break; + case IM_BANDFMT_UINT: loop(unsigned int); break; + case IM_BANDFMT_INT: loop(signed int); break; + case IM_BANDFMT_FLOAT: loop(float); break; + + case IM_BANDFMT_DOUBLE: +#ifdef HAVE_LIBOIL + for( y = to; y < bo; y++ ) { + double *p = (double *) IM_REGION_ADDR( reg, le, y ); + double t; + double t2; + + oil_sum_f64( &t, p, sizeof( double ), sz ); + oil_squaresum_f64( &t2, p, sz ); + + s += t; + s2 += t2; + } +#else /*!HAVE_LIBOIL*/ + loop(double); +#endif /*HAVE_LIBOIL*/ + break; + + default: + assert( 0 ); + } + + /* Add to sum for this sequence. + */ + tmp[0] += s; + tmp[1] += s2; + + return( 0 ); +} + +/* Find the average of an image. + */ +int +im_deviate( IMAGE *in, double *out ) +{ + double sum[2] = { 0.0, 0.0 }; + gint64 N; + + /* Check our args. + */ + if( im_pincheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_deviate", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_deviate", _( "bad input type" ) ); + return( -1 ); + } + + /* Loop over input, summing pixels. + */ + if( im_iterate( in, start_fn, scan_fn, stop_fn, &sum, NULL ) ) + return( -1 ); + + /* + + NOTE: NR suggests a two-pass algorithm to minimise roundoff. + But that's too expensive for us :-( so do it the old one-pass + way. + + */ + + /* Calculate and return deviation. Add a fabs to stop sqrt(<=0). + */ + N = (gint64) in->Xsize * in->Ysize * in->Bands; + *out = sqrt( fabs( sum[1] - (sum[0] * sum[0] / N) ) / (N - 1) ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_divide.c b/libsrc/arithmetic/im_divide.c new file mode 100644 index 00000000..dd8c47b6 --- /dev/null +++ b/libsrc/arithmetic/im_divide.c @@ -0,0 +1,214 @@ +/* @(#) Divide two images + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int + * @(#) im_divide(in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 29/4/93 JC + * - now works for partial images + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 19/10/93 JC + * - coredump-inducing bug in complex*complex fixed + * 13/12/93 + * - char*short bug fixed + * 12/6/95 JC + * - new im_multiply adapted to make new im_divide + * 27/9/04 + * - updated for 1 band $op n band image -> n band image case + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Complex divide. + */ +#define cloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double x1 = p1[0];\ + double y1 = p1[1];\ + double x2 = p2[0];\ + double y2 = p2[1];\ + \ + double quot = x2 * x2 + y2 * y2;\ + \ + p1 += 2;\ + p2 += 2;\ + \ + q[0] = (x1 * x2 + y1 * y2) / quot;\ + q[1] = (y1 * x2 - x1 * y2) / quot;\ + \ + q += 2;\ + }\ +} + +/* Real divide. + */ +#define rloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p1[x] / p2[x];\ +} + +static void +divide_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Divide all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: rloop( signed char ); break; + case IM_BANDFMT_UCHAR: rloop( unsigned char ); break; + case IM_BANDFMT_SHORT: rloop( signed short ); break; + case IM_BANDFMT_USHORT: rloop( unsigned short ); break; + case IM_BANDFMT_INT: rloop( signed int ); break; + case IM_BANDFMT_UINT: rloop( unsigned int ); break; + + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_divide_f32( (float *) out, + (float *) in[0], (float *) in[1], sz ); +#else /*!HAVE_LIBOIL*/ + rloop( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: rloop( double ); break; + case IM_BANDFMT_COMPLEX: cloop( float ); break; + case IM_BANDFMT_DPCOMPLEX: cloop( double ); break; + + default: + assert( 0 ); + } +} + +int +im_divide( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_divide", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_divide", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* What output type will we write? float, double or complex. + */ + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + /* What kind of complex? + */ + if( in1->BandFmt == IM_BANDFMT_DPCOMPLEX || + in2->BandFmt == IM_BANDFMT_DPCOMPLEX ) + /* Output will be DPCOMPLEX. + */ + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + else + out->BandFmt = IM_BANDFMT_COMPLEX; + + } + else if( im_isfloat( in1 ) || im_isfloat( in2 ) ) { + /* What kind of float? + */ + if( in1->BandFmt == IM_BANDFMT_DOUBLE || + in2->BandFmt == IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_DOUBLE; + else + out->BandFmt = IM_BANDFMT_FLOAT; + } + else { + /* An int type -- output must be just float. + */ + out->BandFmt = IM_BANDFMT_FLOAT; + } + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) divide_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} diff --git a/libsrc/arithmetic/im_expntra.c b/libsrc/arithmetic/im_expntra.c new file mode 100644 index 00000000..debe8d45 --- /dev/null +++ b/libsrc/arithmetic/im_expntra.c @@ -0,0 +1,249 @@ +/* @(#) Calculates n^pel, with n as a parameter. + * @(#) If input is up to float, output is float, else input is the same as + * @(#) output. Does not work for complex input. + * @(#) + * @(#) int + * @(#) im_expntra( in, out, e ) + * @(#) IMAGE *in, *out; + * @(#) double e; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 10/12/93 JC + * - now reports total number of x/0, rather than each one. + * 1/2/95 JC + * - rewritten for PIO with im_wrapone() + * - incorrect complex code removed + * - /0 reporting removed for ease of programming + * 8/5/95 JC + * - im_expntra() adapted to make this + * 15/4/97 JC + * - oops, return(0) missing + * - M_E removed, as not everywhere + * 6/7/98 JC + * - _vec version added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Parameters saved here. + */ +typedef struct { + int n; /* Number of bands of constants */ + double *e; /* Exponent values, one per band */ +} ExpntraInfo; + +/* Define what we do for each band element type. Single constant. + */ +#define loop1(IN, OUT)\ +{\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double f = (double) p[x];\ + \ + if( e == 0.0 && f < 0.0 ) {\ + /* Division by zero! Difficult to report tho'\ + */\ + q[x] = 0.0;\ + }\ + else\ + q[x] = pow( e, f );\ + }\ +} + +/* Expntra a buffer. + */ +static int +expntra1_gen( PEL *in, PEL *out, int width, IMAGE *im, ExpntraInfo *inf ) +{ + int sz = width * im->Bands; + double e = inf->e[0]; + int x; + + /* Expntra all non-complex input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop1(unsigned char, float); break; + case IM_BANDFMT_CHAR: loop1(signed char, float); break; + case IM_BANDFMT_USHORT: loop1(unsigned short, float); break; + case IM_BANDFMT_SHORT: loop1(signed short, float); break; + case IM_BANDFMT_UINT: loop1(unsigned int, float); break; + case IM_BANDFMT_INT: loop1(signed int, float); break; + case IM_BANDFMT_FLOAT: loop1(float, float); break; + case IM_BANDFMT_DOUBLE: loop1(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Define what we do for each band element type. One constant per band. + */ +#define loopn(IN, OUT)\ +{\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( i = 0, x = 0; x < width; x++ )\ + for( k = 0; k < im->Bands; k++, i++ ) {\ + double e = inf->e[k];\ + double f = (double) p[i];\ + \ + if( e == 0.0 && f < 0.0 ) {\ + q[i] = 0.0;\ + }\ + else\ + q[i] = pow( e, f );\ + }\ +} + +/* Expntra a buffer. + */ +static int +expntran_gen( PEL *in, PEL *out, int width, IMAGE *im, ExpntraInfo *inf ) +{ + int x, k, i; + + /* Expntra all non-complex input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loopn(unsigned char, float); break; + case IM_BANDFMT_CHAR: loopn(signed char, float); break; + case IM_BANDFMT_USHORT: loopn(unsigned short, float); break; + case IM_BANDFMT_SHORT: loopn(signed short, float); break; + case IM_BANDFMT_UINT: loopn(unsigned int, float); break; + case IM_BANDFMT_INT: loopn(signed int, float); break; + case IM_BANDFMT_FLOAT: loopn(float, float); break; + case IM_BANDFMT_DOUBLE: loopn(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +int +im_expntra_vec( IMAGE *in, IMAGE *out, int n, double *e ) +{ + ExpntraInfo *inf; + int i; + + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_expntra_vec", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_expntra_vec", _( "not non-complex" ) ); + return( -1 ); + } + if( n != 1 && n != in->Bands ) { + im_error( "im_expntra_vec", + _( "not 1 or %d elements in vector" ), in->Bands ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + + /* Make space for a little buffer. + */ + if( !(inf = IM_NEW( out, ExpntraInfo )) || + !(inf->e = IM_ARRAY( out, n, double )) ) + return( -1 ); + for( i = 0; i < n; i++ ) + inf->e[i] = e[i]; + inf->n = n; + + /* Generate! + */ + if( n == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) expntra1_gen, in, inf ) ) + return( -1 ); + } + else { + if( im_wrapone( in, out, + (im_wrapone_fn) expntran_gen, in, inf ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_expntra( IMAGE *in, IMAGE *out, double e ) +{ + return( im_expntra_vec( in, out, 1, &e ) ); +} + +/* Define im_exptra() and im_exp10tra() in terms of im_expntra(). + */ +int +im_exptra( IMAGE *in, IMAGE *out ) +{ + return( im_expntra( in, out, 2.7182818284590452354 ) ); +} + +int +im_exp10tra( IMAGE *in, IMAGE *out ) +{ + return( im_expntra( in, out, 10.0 ) ); +} diff --git a/libsrc/arithmetic/im_fav4.c b/libsrc/arithmetic/im_fav4.c new file mode 100644 index 00000000..c3aa7246 --- /dev/null +++ b/libsrc/arithmetic/im_fav4.c @@ -0,0 +1,95 @@ +/* @(#) Optimised 4 frame average +Copyright (C) 1992, Kirk Martinez, History of Art Dept, Birkbeck College +*/ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define ARGS "fav4: frame average 4 frames\nARGS: im1 im2 im3 im4 outfile" +#define NFRAMES 4 + +/* @(#) Optimised 4 frame average +Copyright (C) 1992, Kirk Martinez, History of Art Dept, Birkbeck College +CHAR images only! +ARGS: array of 4 source images and output image +*/ +int +im_fav4( IMAGE **in, IMAGE *out) +{ + PEL *result, *buffer, *p1, *p2, *p3, *p4; + int x,y; + int linebytes, PICY; + +/* check IMAGEs parameters +*/ +if(im_iocheck(in[1], out)) return(-1); + +/* BYTE images only! +*/ +if( (in[0]->BandFmt != IM_BANDFMT_CHAR) && (in[0]->BandFmt != IM_BANDFMT_UCHAR)) return(-1); + +if ( im_cp_desc(out, in[1]) == -1) /* copy image descriptors */ + return(-1); +if ( im_setupout(out) == -1) + return(-1); + +linebytes = in[0]->Xsize * in[0]->Bands; +PICY = in[0]->Ysize; +buffer = (PEL*)im_malloc(NULL,linebytes); +memset(buffer, 0, linebytes); + + p1 = (PEL*)in[0]->data; + p2 = (PEL*)in[1]->data; + p3 = (PEL*)in[2]->data; + p4 = (PEL*)in[3]->data; + +for (y = 0; y < PICY; y++) + { + result = buffer; + /* average 4 pels with rounding, for whole line*/ + for (x = 0; x < linebytes; x++) { + *result++ = (PEL)((int)((int)*p1++ + (int)*p2++ + (int)*p3++ + (int)*p4++ +2) >> 2); + } + im_writeline(y,out, buffer); + } +im_free(buffer); +return(0); +} + diff --git a/libsrc/arithmetic/im_floor.c b/libsrc/arithmetic/im_floor.c new file mode 100644 index 00000000..2ee3e8b8 --- /dev/null +++ b/libsrc/arithmetic/im_floor.c @@ -0,0 +1,128 @@ +/* @(#) floor() an image ... no promotion, so output type == input type + * @(#) + * @(#) int + * @(#) im_floor( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 20/6/02 JC + * - adapted from im_abs() + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define floor_loop(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = floor( p[x] );\ + } + +/* Ceil a buffer of PELs. + */ +static void +floor_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_floor_f32( (float *) out, (float *) in, sz ); +#else /*!HAVE_LIBOIL*/ + floor_loop(float); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: floor_loop(double); break; + case IM_BANDFMT_COMPLEX: sz *= 2; floor_loop(float); break; + case IM_BANDFMT_DPCOMPLEX: sz *= 2; floor_loop(double); break; + + default: + assert( 0 ); + } +} + +/* Ceil of image. + */ +int +im_floor( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_floor", _( "not uncoded" ) ); + return( -1 ); + } + + /* Is this one of the int types? Degenerate to im_copy() if it + * is. + */ + if( im_isint( in ) ) + return( im_copy( in, out ) ); + + /* Output type == input type. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Generate! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) floor_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_gadd.c b/libsrc/arithmetic/im_gadd.c new file mode 100644 index 00000000..bd227518 --- /dev/null +++ b/libsrc/arithmetic/im_gadd.c @@ -0,0 +1,127 @@ +/* @(#) Generalised addition of two vasari images using the routines + * @(#) im_gaddim or im_gfadd + * @(#) Convention to ease the complilation time. + * @(#) Function im_gadd() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and must not be complex + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int im_gadd(a, in1, b, in2, c, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) double a, b, c; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +extern int im_gfadd(); +extern int im_gaddim(); + +/* This function works on either mmaped files or on images in buffer + */ + +int im_gadd(a, in1, b, in2, c, out) +IMAGE *in1, *in2, *out; +double a, b, c; +{ + int flagint = 0; + int flagfloat = 0; + int value = 0; + + switch(in1->BandFmt) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + flagint = 1; + break; + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + flagfloat = 1; + break; + default: im_error("im_gadd",_("Unable to accept image1")); + return(-1); + } + switch(in2->BandFmt) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + flagint = 1; + break; + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + flagfloat = 1; + break; + default: im_error("im_gadd",_("Unable to accept image1")); + return(-1); + } + /* Select output routines */ + if (flagfloat == 1) + { + value = im_gfadd(a, in1, b, in2, c, out); + if (value == -1) + return(-1); + } + else if (flagint == 1) + { + value = im_gaddim(a, in1, b, in2, c, out); + if (value == -1) + return(-1); + } + else + assert( 0 ); + + return(0); +} diff --git a/libsrc/arithmetic/im_gaddim.c b/libsrc/arithmetic/im_gaddim.c new file mode 100644 index 00000000..a15dd911 --- /dev/null +++ b/libsrc/arithmetic/im_gaddim.c @@ -0,0 +1,241 @@ +/* @(#) Generalised addition of two vasari images. + * @(#)Inputs, outputs are neither float nor double + * @(#) Result at each point is a*in1 + b*in2 + c + * @(#) Result depends on inputs, rounding is carried out; + * @(#) Function im_gaddim() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and must not be complex + * @(#) No check for overflow is done; + * @(#) + * @(#) int im_gaddim(a, in1, b, in2, c, out) + * @(#) double a, b, c; + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* This function works on either mmaped files or on images in buffer + */ + +/* uchar char ushort short uint int */ +static int array[6][6] = { +/* uchar */ { 2, 3, 2, 3, 4, 5 }, +/* char */ { 3, 3, 3, 3, 5, 5 }, +/* ushort */ { 2, 3, 2, 3, 4, 5 }, +/* short */ { 3, 3, 3, 3, 5, 5 }, +/* uint */ { 4, 5, 4, 5, 4, 5 }, +/* int */ { 5, 5, 5, 5, 5, 5 } + }; + +#define select_tmp2_for_out_int(OUT) \ + case IM_BANDFMT_UCHAR: select_tmp1_for_out_int(unsigned char, OUT); break; \ + case IM_BANDFMT_CHAR: select_tmp1_for_out_int(signed char, OUT); break; \ + case IM_BANDFMT_USHORT: select_tmp1_for_out_int(unsigned short, OUT); break; \ + case IM_BANDFMT_SHORT: select_tmp1_for_out_int(signed short, OUT); break; \ + case IM_BANDFMT_UINT: select_tmp1_for_out_int(unsigned int, OUT); break; \ + case IM_BANDFMT_INT: select_tmp1_for_out_int(signed int, OUT); break; \ +\ + default: \ + im_error("im_gaddim","Wrong tmp2 format(1)"); \ + free( line); \ + return(-1); + +#define select_tmp1_for_out_int(IN2, OUT) \ + switch(tmp1->BandFmt) { \ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break; \ + case IM_BANDFMT_INT: loop(int, IN2, OUT); break; \ + default: im_error("im_gaddim","Wrong tmp2 format(2)");\ + free( line);\ + return(-1); \ + } + +#define select_tmp2_for_out_short(OUT) \ + case IM_BANDFMT_UCHAR: select_tmp1_for_out_short(unsigned char, OUT); break; \ + case IM_BANDFMT_CHAR: select_tmp1_for_out_short(signed char, OUT); break; \ + case IM_BANDFMT_USHORT: select_tmp1_for_out_short(unsigned short, OUT); break; \ + case IM_BANDFMT_SHORT: select_tmp1_for_out_short(signed short, OUT); break; +#define select_tmp1_for_out_short(IN2, OUT) \ + switch(tmp1->BandFmt) { \ + case IM_BANDFMT_UCHAR: loop(unsigned char, IN2, OUT); break; \ + case IM_BANDFMT_CHAR: loop(signed char, IN2, OUT); break; \ + case IM_BANDFMT_USHORT: loop(unsigned short, IN2, OUT); break; \ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break; \ + default: im_error("im_gaddim","Wrong image1 format(4)");\ + free( line);\ + return(-1); \ + } + + + +int im_gaddim(a, in1, b, in2, c, out) +IMAGE *in1, *in2, *out; +double a, b, c; +{ + static int bb[] = { IM_BBITS_BYTE, IM_BBITS_BYTE, IM_BBITS_SHORT, + IM_BBITS_SHORT, IM_BBITS_INT, IM_BBITS_INT }; + static int fmt[] = { IM_BANDFMT_UCHAR, IM_BANDFMT_CHAR, + IM_BANDFMT_USHORT, IM_BANDFMT_SHORT, + IM_BANDFMT_UINT, IM_BANDFMT_INT }; + int y, x; + int first, second, result; + IMAGE *tmp1, *tmp2; + PEL *line; + int os; /* size of a line of output image */ + +/* fd, data filename must have been set before the function is called + * Check whether they are set properly */ + if ((im_iocheck(in1, out) == -1) || (im_iocheck(in2, out) == -1)) + { + return(-1); + } +/* Checks the arguments entered in in and prepares out */ + if ( (in1->Xsize != in2->Xsize) || (in1->Ysize != in2->Ysize) || + (in1->Bands != in2->Bands) || (in1->Coding != in2->Coding) ) + { im_error("im_gaddim"," Input images differ"); return(-1); } + if (in1->Coding != IM_CODING_NONE) + { im_error("im_gaddim"," images must be uncoded"); return(-1);} + + switch(in1->BandFmt) { + case IM_BANDFMT_UCHAR: first = 0; break; + case IM_BANDFMT_CHAR: first = 1; break; + case IM_BANDFMT_USHORT: first = 2; break; + case IM_BANDFMT_SHORT: first = 3; break; + case IM_BANDFMT_UINT: first = 4; break; + case IM_BANDFMT_INT: first = 5; break; + default: im_error("im_gaddim"," Unable to accept image1"); + return(-1); + } + switch(in2->BandFmt) { + case IM_BANDFMT_UCHAR: second = 0; break; + case IM_BANDFMT_CHAR: second = 1; break; + case IM_BANDFMT_USHORT: second = 2; break; + case IM_BANDFMT_SHORT: second = 3; break; + case IM_BANDFMT_UINT: second = 4; break; + case IM_BANDFMT_INT: second = 5; break; + default: im_error("im_gaddim"," Unable to accept image2"); + return(-1); + } +/* Define the output */ + result = array[first][second]; + +/* Prepare the output header */ + if ( im_cp_desc(out, in1) == -1) + { im_error("im_gaddim"," im_cp_desc failed"); return(-1); } + out->Bbits = bb[result]; + out->BandFmt = fmt[result]; + + if( im_setupout(out) == -1) + { im_error("im_gaddim"," im_setupout failed"); return(-1); } + +/* Order in1 and in2 */ + if ( first >= second ) + { tmp1 = in1; tmp2 = in2; } + else + { tmp1 = in2; tmp2 = in1; } + +/* Define what we do for each band element type. */ + +#define loop(IN1, IN2, OUT) \ + { IN1 *input1 = (IN1 *) tmp1->data; \ + IN2 *input2 = (IN2 *) tmp2->data; \ + \ + for (y=0; y Ysize; y++) {\ + OUT *cpline = (OUT*)line; \ + for (x=0; xXsize * out->Bands; + line = (PEL *) calloc ( (unsigned)os, sizeof(double) ); + if (line == NULL) + { + im_error("im_gaddim"," Unable to calloc"); + return(-1); + } + + switch (out->BandFmt) { + case IM_BANDFMT_INT: + switch (tmp2->BandFmt) { + select_tmp2_for_out_int(int); + } + break; + + case IM_BANDFMT_UINT: + switch (tmp2->BandFmt) { + select_tmp2_for_out_int(unsigned int); + } + break; + case IM_BANDFMT_SHORT: + switch (tmp2->BandFmt) { + select_tmp2_for_out_short(short); + } + break; + case IM_BANDFMT_USHORT: + switch (tmp2->BandFmt) { + select_tmp2_for_out_short(unsigned short); + } + break; + default: + im_error("im_gaddim"," Impossible output state"); + free( line); + return(-1); + } + + free( line); + + return(0); +} diff --git a/libsrc/arithmetic/im_gfadd.c b/libsrc/arithmetic/im_gfadd.c new file mode 100644 index 00000000..24fd7bcb --- /dev/null +++ b/libsrc/arithmetic/im_gfadd.c @@ -0,0 +1,351 @@ +/* @(#) Generalised addition of two vasari images. Result (double or float) + * @(#)depends on inputs + * @(#) Function im_gfadd() assumes that the both input files + * @(#) are either memory mapped or in a buffer. + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. If in doubt use im_clip2... + * @(#) Result at eachpoint is a*in1 + b*in2 + c + * @(#) + * @(#) int im_gfadd(a, in1, b, in2, c, out) + * @(#) double a, b, c; + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 15/6/93 J.Cupitt + * - externs removed + * - casts added to please ANSI C + * - includes rationalised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* uchar char ushort short uint int float double */ +static int array[8][8] = { +/* uchar */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* char */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* ushort */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* short */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* uint */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* int */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* float */ { 0, 0, 0, 0, 0, 0, 0, 1 }, +/* double */ { 1, 1, 1, 1, 1, 1, 1, 1 } + }; + +#define select_outdouble(IN2, OUT)\ + switch(tmp1->BandFmt) {\ + case IM_BANDFMT_DOUBLE: loop(double, IN2, OUT); break;\ + default: im_error("im_gfadd","Wrong tmp1 format(d)");\ + free( line); return( -1 );\ + } + +#define outfloat_2uchar(IN2, OUT)\ + case IM_BANDFMT_UCHAR: loop(unsigned char, IN2, OUT); break;\ + case IM_BANDFMT_CHAR: loop(signed char, IN2, OUT); break;\ + case IM_BANDFMT_USHORT: loop(unsigned short, IN2, OUT); break;\ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break;\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2char(IN2, OUT)\ + case IM_BANDFMT_CHAR: loop(signed char, IN2, OUT); break;\ + case IM_BANDFMT_USHORT: loop(unsigned short, IN2, OUT); break;\ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break;\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2ushort(IN2, OUT)\ + case IM_BANDFMT_USHORT: loop(unsigned short, IN2, OUT); break;\ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break;\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2short(IN2, OUT)\ + case IM_BANDFMT_SHORT: loop(signed short, IN2, OUT); break;\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2uint(IN2, OUT)\ + case IM_BANDFMT_UINT: loop(unsigned int, IN2, OUT); break;\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2int(IN2, OUT)\ + case IM_BANDFMT_INT: loop(signed int, IN2, OUT); break;\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +#define outfloat_2float(IN2, OUT)\ + case IM_BANDFMT_FLOAT: loop(float, IN2, OUT); break; + +int im_gfadd(a, in1, b, in2, c, out) +double a, b, c; +IMAGE *in1, *in2, *out; +{ + static int bb[] = { IM_BBITS_FLOAT, IM_BBITS_DOUBLE }; + static int fmt[] = { IM_BANDFMT_FLOAT, IM_BANDFMT_DOUBLE }; + int y, x; + int first, second, result; + IMAGE *tmp1, *tmp2; + PEL *line; + int os; /* size of a line of output image */ + +/* fd, data filename must have been set before the function is called + * Check whether they are set properly */ + if ((im_iocheck(in1, out) == -1) || (im_iocheck(in2, out) == -1)) + { im_error("im_gfadd"," im_iocheck failed"); return( -1 ); } +/* Checks the arguments entered in in and prepares out */ + if ( (in1->Xsize != in2->Xsize) || (in1->Ysize != in2->Ysize) || + (in1->Bands != in2->Bands) || (in1->Coding != in2->Coding) ) + { im_error("im_gfadd"," Input images differ"); return( -1 );} + if (in1->Coding != IM_CODING_NONE) + { im_error("im_gfadd"," images are coded"); return( -1 ); } + + switch(in1->BandFmt) { + case IM_BANDFMT_UCHAR: first = 0; break; + case IM_BANDFMT_CHAR: first = 1; break; + case IM_BANDFMT_USHORT: first = 2; break; + case IM_BANDFMT_SHORT: first = 3; break; + case IM_BANDFMT_UINT: first = 4; break; + case IM_BANDFMT_INT: first = 5; break; + case IM_BANDFMT_FLOAT: first = 6; break; + case IM_BANDFMT_DOUBLE: first = 7; break; + default: im_error("im_gfadd"," unable to accept image1"); + return( -1 ); + } + switch(in2->BandFmt) { + case IM_BANDFMT_UCHAR: second = 0; break; + case IM_BANDFMT_CHAR: second = 1; break; + case IM_BANDFMT_USHORT: second = 2; break; + case IM_BANDFMT_SHORT: second = 3; break; + case IM_BANDFMT_UINT: second = 4; break; + case IM_BANDFMT_INT: second = 5; break; + case IM_BANDFMT_FLOAT: second = 6; break; + case IM_BANDFMT_DOUBLE: second = 7; break; + default: im_error("im_gfadd"," unable to accept image2"); + return( -1 ); + } +/* Define the output */ + result = array[first][second]; +/* Prepare output */ + if ( im_cp_desc(out, in1) == -1) + { im_error("im_gfadd"," im_cp_desc failed"); return( -1 ); } + out->Bbits = bb[result]; + out->BandFmt = fmt[result]; + if( im_setupout(out) == -1) + { im_error("im_gfadd"," im_setupout failed"); return( -1 ); } + +/* Order in1 and in2 */ + if ( first >= second ) + { tmp1 = in1; tmp2 = in2; } + else + { tmp1 = in2; tmp2 = in1; } + +/* Define what we do for each band element type. */ + +#define loop(IN1, IN2, OUT)\ + { IN1 *input1 = (IN1 *) tmp1->data;\ + IN2 *input2 = (IN2 *) tmp2->data;\ + \ + for (y=0; y Ysize; y++) {\ + OUT *cpline = (OUT*)line;\ + for (x=0; xXsize * out->Bands; + line = (PEL *) calloc ( (unsigned)os, sizeof(double) ); + if (line == NULL) + { im_error("im_gfadd"," unable to calloc"); return( -1 ); } + + switch (out->BandFmt) { + case IM_BANDFMT_DOUBLE: + switch (tmp2->BandFmt) { + case IM_BANDFMT_UCHAR: + select_outdouble(unsigned char, double); + break; + + case IM_BANDFMT_CHAR: + select_outdouble(signed char, double); + break; + + case IM_BANDFMT_USHORT: + select_outdouble(unsigned short, double); + break; + + case IM_BANDFMT_SHORT: + select_outdouble(signed short, double); + break; + + case IM_BANDFMT_UINT: + select_outdouble(unsigned int, double); + break; + + case IM_BANDFMT_INT: + select_outdouble(signed int, double); + break; + + case IM_BANDFMT_FLOAT: + select_outdouble(float, double); + break; + + case IM_BANDFMT_DOUBLE: + select_outdouble(double, double); + break; + + default: + im_error("im_gfadd","Wrong tmp2 format(d)"); + free( line ); + return( -1 ); + } + + break; + + case IM_BANDFMT_FLOAT : + switch (tmp2->BandFmt) { + case IM_BANDFMT_UCHAR: + switch (tmp1->BandFmt) { + outfloat_2uchar(unsigned char, float); + + default: + im_error("im_gfadd"," Error (a)"); + free( line ); + return( -1 ); + } + break; + + case IM_BANDFMT_CHAR: + switch (tmp1->BandFmt) { + outfloat_2char(signed char, float); + + default: + im_error("im_gfadd"," Error (b)"); + free( line); return( -1 ); + } + break; + + case IM_BANDFMT_USHORT: + switch (tmp1->BandFmt) { + outfloat_2ushort(unsigned short, float); + + default: + im_error("im_gfadd"," Error (c)"); + free( line); return( -1 ); + } + break; + + case IM_BANDFMT_SHORT: + switch (tmp1->BandFmt) { + outfloat_2short(signed short, float); + + default: + im_error("im_gfadd"," Error (d)"); + free( line); return( -1 ); + } + break; + + case IM_BANDFMT_UINT: + switch (tmp1->BandFmt) { + outfloat_2uint(unsigned int, float); + + default: + im_error("im_gfadd"," Error (e)"); + free( line); return( -1 ); + } + break; + + case IM_BANDFMT_INT: + switch (tmp1->BandFmt) { + outfloat_2int(signed int, float); + + default: + im_error("im_gfadd"," Error (f)"); + free( line ); + return( -1 ); + } + break; + + case IM_BANDFMT_FLOAT: + switch (tmp1->BandFmt) { + outfloat_2float(float, float); + + default: + im_error("im_gfadd"," Error (g)"); + free( line ); + return( -1 ); + } + break; + + default: + im_error("im_gfadd"," Wrong tmp2 format(f)"); + free( line ); + return( -1 ); + } + + break; + + default: + im_error("im_gfadd"," Impossible output state"); + free( line ); + return( -1 ); + } + + free( line ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_invert.c b/libsrc/arithmetic/im_invert.c new file mode 100644 index 00000000..a9a474c3 --- /dev/null +++ b/libsrc/arithmetic/im_invert.c @@ -0,0 +1,149 @@ +/* @(#) Invert a UCHAR image. Very simple new-style VIPS routine. See + * @(#) im_exptra() for the next level of complexity. This function is not + * @(#) as quick as it could be - it is intended to be an example rather than + * @(#) to be useful. This should really be written with im_wrapone(). + * @(#) + * @(#) int + * @(#) im_invert( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : + * 7/7/93 JC + * - memory leaks fixed + * - adapted for partial v2 + * - ANSIfied + * 22/2/95 JC + * - tidied up again + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Invert a REGION. We are given the REGION we should write to, the REGION we + * should use for input, and the IMAGE we are processing. On entry to + * invert_gen(), or points to the memory we should write to and ir is blank. + */ +static int +invert_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + /* Left, right, top and bottom for the output region. + */ + int le = or->valid.left; + int to = or->valid.top; + int bo = IM_RECT_BOTTOM( &or->valid ); + + int x, y; + + /* Ask for the section of the input image we need to produce this + * section of the output image. + */ + if( im_prepare( ir, &or->valid ) ) + return( -1 ); + + /* Loop over output, writing input. + */ + for( y = to; y < bo; y++ ) { + /* Point p and q at the start of the line of pels we must + * process this loop. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Loop along the line, processing pels. + * IM_REGION_N_ELEMENTS(region) gives + * the number of band elements across a region. By looping to + * IM_REGION_N_ELEMENTS() rather than ir->valid.width, we work + * for any number of bands. + */ + for( x = 0; x < IM_REGION_N_ELEMENTS( or ); x++ ) + q[x] = 255 - p[x]; + } + + return( 0 ); +} + +/* Invert IMAGE in to IMAGE out. Any number of bands, unsigned char pels + * only. See im_exptra() for an example of a VIPS function which can process + * any input image type. + */ +int +im_invert( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_invert", _( "not uncoded" ) ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_invert", _( "not UCHAR" ) ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Prepare the output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Set demand hints. Like most one-to-one operations, we work best + * with long, thin strips. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate into out. im_start_one() and im_stop_one() are simple + * convenience functions provided by VIPS which do the necessary + * region creation and destruction for one-image-in + * style functions. See im_add(), im_start_many() and im_stop_many() + * for convenience functions for multiple inputs. + */ + if( im_generate( out, + im_start_one, invert_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_linreg.c b/libsrc/arithmetic/im_linreg.c new file mode 100644 index 00000000..94686c14 --- /dev/null +++ b/libsrc/arithmetic/im_linreg.c @@ -0,0 +1,430 @@ +/* @(#) Function to find perform pixelwise linear regression on an array of + * @(#) single band images. + * @(#) + * @(#) int im_linreg( + * @(#) IMAGE **ins, + * @(#) IMAGE *out, + * @(#) double *xs + * @(#) ); + * @(#) + * + * Copyright: 2006, The Nottingham Trent University + * + * Author: Tom Vajzovic + * + * Written on: 2006-12-26 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** TYPES **/ + +typedef struct { + + unsigned int n; + double *xs; + double *difs; + double mean; + double nsig2; + double err_term; + +} x_set; + +#define LINREG_SEQ( TYPE ) typedef struct { \ + REGION **regs; \ + TYPE **ptrs; \ + size_t *skips; \ +} linreg_seq_ ## TYPE + +LINREG_SEQ( gint8 ); +LINREG_SEQ( guint8 ); +LINREG_SEQ( gint16 ); +LINREG_SEQ( guint16 ); +LINREG_SEQ( gint32 ); +LINREG_SEQ( guint32 ); +LINREG_SEQ( float ); +LINREG_SEQ( double ); + + +/** LOCAL FUNCTION DECLARATIONS **/ + +x_set *x_anal( IMAGE *im, double *xs, unsigned int n ); + +#define LINREG_START_DECL( TYPE ) static linreg_seq_ ## TYPE *linreg_start_ ## TYPE( IMAGE *out, IMAGE **ins, x_set *x_vals ) +#define LINREG_GEN_DECL( TYPE ) static int linreg_gen_ ## TYPE( REGION *to_make, linreg_seq_ ## TYPE *seq, void *unrequired, x_set *x_vals ) +#define LINREG_STOP_DECL( TYPE ) static int linreg_stop_ ## TYPE( linreg_seq_ ## TYPE *seq ) +#define INCR_ALL_DECL( TYPE ) static void incr_all_ ## TYPE( TYPE **ptrs, unsigned int n ) +#define SKIP_ALL_DECL( TYPE ) static void skip_all_ ## TYPE( TYPE **ptrs, size_t *skips, unsigned int n ) + +LINREG_START_DECL( gint8 ); +LINREG_START_DECL( guint8 ); +LINREG_START_DECL( gint16 ); +LINREG_START_DECL( guint16 ); +LINREG_START_DECL( gint32 ); +LINREG_START_DECL( guint32 ); +LINREG_START_DECL( float ); +LINREG_START_DECL( double ); + +LINREG_GEN_DECL( gint8 ); +LINREG_GEN_DECL( guint8 ); +LINREG_GEN_DECL( gint16 ); +LINREG_GEN_DECL( guint16 ); +LINREG_GEN_DECL( gint32 ); +LINREG_GEN_DECL( guint32 ); +LINREG_GEN_DECL( float ); +LINREG_GEN_DECL( double ); + +LINREG_STOP_DECL( gint8 ); +LINREG_STOP_DECL( guint8 ); +LINREG_STOP_DECL( gint16 ); +LINREG_STOP_DECL( guint16 ); +LINREG_STOP_DECL( gint32 ); +LINREG_STOP_DECL( guint32 ); +LINREG_STOP_DECL( float ); +LINREG_STOP_DECL( double ); + +INCR_ALL_DECL( gint8 ); +INCR_ALL_DECL( guint8 ); +INCR_ALL_DECL( gint16 ); +INCR_ALL_DECL( guint16 ); +INCR_ALL_DECL( gint32 ); +INCR_ALL_DECL( guint32 ); +INCR_ALL_DECL( float ); +INCR_ALL_DECL( double ); + +SKIP_ALL_DECL( gint8 ); +SKIP_ALL_DECL( guint8 ); +SKIP_ALL_DECL( gint16 ); +SKIP_ALL_DECL( guint16 ); +SKIP_ALL_DECL( gint32 ); +SKIP_ALL_DECL( guint32 ); +SKIP_ALL_DECL( float ); +SKIP_ALL_DECL( double ); + + +/** EXPORTED FUNCTION DEFINITION **/ + +int im_linreg( IMAGE **ins, IMAGE *out, double *xs ){ +#define FUNCTION_NAME "im_linreg" + int n; + x_set *x_vals; + + if( im_poutcheck( out ) ) + return( -1 ); + + for( n= 0; ins[ n ]; ++n ){ +/* + if( ! isfinite( xs[ n ] ) ){ + im_error( FUNCTION_NAME, "invalid argument" ); + return( -1 ); + } +*/ + if( im_pincheck( ins[ n ] ) ) + return( -1 ); + + if( 1 != ins[ n ]-> Bands ){ + im_error( FUNCTION_NAME, "image is not single band" ); + return( -1 ); + } + if( ins[ n ]-> Coding ){ + im_error( FUNCTION_NAME, "image is not uncoded" ); + return( -1 ); + } + if( n ){ + if( ins[ n ]-> BandFmt != ins[ 0 ]-> BandFmt ){ + im_error( FUNCTION_NAME, "image band formats differ" ); + return( -1 ); + } + } + else { + if( ! im_isscalar( ins[ 0 ] ) ){ + im_error( FUNCTION_NAME, "image has non-scalar band format" ); + return( -1 ); + } + } + if( n && ( ins[ n ]-> Xsize != ins[ 0 ]-> Xsize + || ins[ n ]-> Ysize != ins[ 0 ]-> Ysize ) ){ + + im_error( FUNCTION_NAME, "image sizes differ" ); + return( -1 ); + } + } + if( n < 3 ){ + im_error( FUNCTION_NAME, "not enough input images" ); + return( -1 ); + } + if( im_cp_desc_array( out, ins ) ) + return( -1 ); + + out-> Bands= 7; + out-> BandFmt= IM_BANDFMT_DOUBLE; + out-> Bbits= IM_BBITS_DOUBLE; + out-> Type= 0; + + if( im_demand_hint_array( out, IM_THINSTRIP, ins ) ) + return( -1 ); + + x_vals= x_anal( out, xs, n ); + + if( ! x_vals ) + return( -1 ); + + switch( ins[ 0 ]-> BandFmt ){ +#define LINREG_RET( TYPE ) return im_generate( out, (void*) linreg_start_ ## TYPE, linreg_gen_ ## TYPE, linreg_stop_ ## TYPE, ins, x_vals ) + + case IM_BANDFMT_CHAR: + LINREG_RET( gint8 ); + + case IM_BANDFMT_UCHAR: + LINREG_RET( guint8 ); + + case IM_BANDFMT_SHORT: + LINREG_RET( gint16 ); + + case IM_BANDFMT_USHORT: + LINREG_RET( guint16 ); + + case IM_BANDFMT_INT: + LINREG_RET( gint32 ); + + case IM_BANDFMT_UINT: + LINREG_RET( guint32 ); + + case IM_BANDFMT_FLOAT: + LINREG_RET( float ); + + case IM_BANDFMT_DOUBLE: + LINREG_RET( double ); + + default: /* keep -Wall happy */ + return( -1 ); + } +#undef FUNCTION_NAME +} + + +/** LOCAL FUNCTION DECLARATIONS **/ + +x_set *x_anal( IMAGE *im, double *xs, unsigned int n ){ + int i; + + x_set *x_vals= IM_NEW( im, x_set ); + + if( ! x_vals ) + return( NULL ); + + x_vals-> xs= IM_ARRAY( im, 2 * n, double ); + + if( ! x_vals-> xs ) + return( NULL ); + + x_vals-> difs= x_vals-> xs + n; + x_vals-> n= n; + x_vals-> mean= 0.0; + + for( i= 0; i < n; ++i ){ + x_vals-> xs[ i ]= xs[ i ]; + x_vals-> mean+= xs[ i ]; + } + x_vals-> mean/= n; + x_vals-> nsig2= 0.0; + + for( i= 0; i < n; ++i ){ + x_vals-> difs[ i ]= xs[ i ] - x_vals-> mean; + x_vals-> nsig2+= x_vals-> difs[ i ] * x_vals-> difs[ i ]; + } + x_vals-> err_term= ( 1.0 / (double) n ) + ( ( x_vals-> mean * x_vals-> mean ) / x_vals-> nsig2 ); + + return( x_vals ); +} + +#define LINREG_START_DEFN( TYPE ) static linreg_seq_ ## TYPE *linreg_start_ ## TYPE( IMAGE *out, IMAGE **ins, x_set *x_vals ){ \ + linreg_seq_ ## TYPE *seq= IM_NEW( NULL, linreg_seq_ ## TYPE ); \ + \ + if( ! seq ) \ + return NULL; \ + \ + seq-> regs= im_start_many( NULL, ins, NULL ); \ + seq-> ptrs= IM_ARRAY( NULL, x_vals-> n, TYPE* ); \ + seq-> skips= IM_ARRAY( NULL, x_vals-> n, size_t ); \ + \ + if( ! seq-> ptrs || ! seq-> regs || ! seq-> skips ){ \ + im_stop_many( seq-> regs, NULL, NULL ); \ + im_free( seq-> ptrs ); \ + im_free( seq-> skips ); \ + im_free( seq ); \ + return NULL; \ + } \ + return seq; \ +} + +#define N ( (double) n ) +#define y(a) ( (double) (* seq-> ptrs[(a)] ) ) +#define x(a) ( (double) ( x_vals-> xs[(a)] ) ) +#define xd(a) ( (double) ( x_vals-> difs[(a)] ) ) +#define Sxd2 ( x_vals-> nsig2 ) +#define mean_x ( x_vals-> mean ) +#define mean_y ( out[0] ) +#define dev_y ( out[1] ) +#define y_x0 ( out[2] ) +#define d_y_x0 ( out[3] ) +#define dy_dx ( out[4] ) +#define d_dy_dx ( out[5] ) +#define R ( out[6] ) + +#define LINREG_GEN_DEFN( TYPE ) static int linreg_gen_ ## TYPE( REGION *to_make, linreg_seq_ ## TYPE *seq, void *unrequired, x_set *x_vals ){ \ + unsigned int n= x_vals-> n; \ + double *out= (double*) IM_REGION_ADDR_TOPLEFT( to_make ); \ + size_t out_skip= IM_REGION_LSKIP( to_make ) / sizeof( double ); \ + double *out_end= out + out_skip * to_make-> valid. height; \ + double *out_stop; \ + size_t out_n= IM_REGION_N_ELEMENTS( to_make ); \ + int i; \ + \ + out_skip-= out_n; \ + \ + if( im_prepare_many( seq-> regs, & to_make-> valid ) ) \ + return -1; \ + \ + for( i= 0; i < n; ++i ){ \ + seq-> ptrs[ i ]= (TYPE*) IM_REGION_ADDR( seq-> regs[ i ], to_make-> valid. left, to_make-> valid. top ); \ + seq-> skips[ i ]= ( IM_REGION_LSKIP( seq-> regs[ i ] ) / sizeof( TYPE ) ) - IM_REGION_N_ELEMENTS( seq-> regs[ i ] ); \ + } \ + \ + for( ; out < out_end; out+= out_skip, skip_all_ ## TYPE( seq-> ptrs, seq-> skips, n ) ) \ + for( out_stop= out + out_n; out < out_stop; out+= 7, incr_all_ ## TYPE( seq-> ptrs, n ) ){ \ + double Sy= 0.0; \ + double Sxd_y= 0.0; \ + double Syd2= 0.0; \ + double Sxd_yd= 0.0; \ + double Se2= 0.0; \ + \ + for( i= 0; i < n; ++i ){ \ + Sy+= y(i); \ + Sxd_y+= xd(i) * y(i); \ + } \ + mean_y= Sy / N; \ + dy_dx= Sxd_y / Sxd2; \ + y_x0= mean_y - dy_dx * mean_x; \ + \ + for( i= 0; i < n; ++i ){ \ + double yd= y(i) - mean_y; \ + double e= y(i) - dy_dx * x(i) - y_x0; \ + Syd2+= yd * yd; \ + Sxd_yd+= xd(i) * yd; \ + Se2+= e * e; \ + } \ + dev_y= sqrt( Syd2 / N ); \ + Se2/= ( N - 2.0 ); \ + d_dy_dx= sqrt( Se2 / Sxd2 ); \ + d_y_x0= sqrt( Se2 * x_vals-> err_term ); \ + R= Sxd_yd / sqrt( Sxd2 * Syd2 ); \ + } \ + return 0; \ +} + +#define LINREG_STOP_DEFN( TYPE ) static int linreg_stop_ ## TYPE( linreg_seq_ ## TYPE *seq ){ \ + im_stop_many( seq-> regs, NULL, NULL ); \ + im_free( seq-> ptrs ); \ + im_free( seq-> skips ); \ + im_free( seq ); \ + return 0; \ +} + +#define INCR_ALL_DEFN( TYPE ) static void incr_all_ ## TYPE( TYPE **ptrs, unsigned int n ){ \ + TYPE **stop= ptrs + n; \ + for( ; ptrs < stop; ++ptrs ) \ + ++*ptrs; \ +} + +#define SKIP_ALL_DEFN( TYPE ) static void skip_all_ ## TYPE( TYPE **ptrs, size_t *skips, unsigned int n ){ \ + TYPE **stop= ptrs + n; \ + for( ; ptrs < stop; ++ptrs, ++skips ) \ + *ptrs+= *skips; \ +} + +LINREG_START_DEFN( gint8 ); +LINREG_START_DEFN( guint8 ); +LINREG_START_DEFN( gint16 ); +LINREG_START_DEFN( guint16 ); +LINREG_START_DEFN( gint32 ); +LINREG_START_DEFN( guint32 ); +LINREG_START_DEFN( float ); +LINREG_START_DEFN( double ); + +LINREG_GEN_DEFN( gint8 ); +LINREG_GEN_DEFN( guint8 ); +LINREG_GEN_DEFN( gint16 ); +LINREG_GEN_DEFN( guint16 ); +LINREG_GEN_DEFN( gint32 ); +LINREG_GEN_DEFN( guint32 ); +LINREG_GEN_DEFN( float ); +LINREG_GEN_DEFN( double ); + +LINREG_STOP_DEFN( gint8 ); +LINREG_STOP_DEFN( guint8 ); +LINREG_STOP_DEFN( gint16 ); +LINREG_STOP_DEFN( guint16 ); +LINREG_STOP_DEFN( gint32 ); +LINREG_STOP_DEFN( guint32 ); +LINREG_STOP_DEFN( float ); +LINREG_STOP_DEFN( double ); + +INCR_ALL_DEFN( gint8 ); +INCR_ALL_DEFN( guint8 ); +INCR_ALL_DEFN( gint16 ); +INCR_ALL_DEFN( guint16 ); +INCR_ALL_DEFN( gint32 ); +INCR_ALL_DEFN( guint32 ); +INCR_ALL_DEFN( float ); +INCR_ALL_DEFN( double ); + +SKIP_ALL_DEFN( gint8 ); +SKIP_ALL_DEFN( guint8 ); +SKIP_ALL_DEFN( gint16 ); +SKIP_ALL_DEFN( guint16 ); +SKIP_ALL_DEFN( gint32 ); +SKIP_ALL_DEFN( guint32 ); +SKIP_ALL_DEFN( float ); +SKIP_ALL_DEFN( double ); diff --git a/libsrc/arithmetic/im_lintra.c b/libsrc/arithmetic/im_lintra.c new file mode 100644 index 00000000..7bc89ba3 --- /dev/null +++ b/libsrc/arithmetic/im_lintra.c @@ -0,0 +1,383 @@ +/* @(#) Pass an image through a linear transform - ie. out = in*a + b. Output + * @(#) is always float for integer input, double for double input, complex for + * @(#) complex input and double complex for double complex input. + * @(#) + * @(#) int + * @(#) im_lintra( a, in, b, out ) + * @(#) IMAGE *in, *out; + * @(#) double a, b; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 23/4/93 JC + * - adapted to work with partial images + * 1/7/93 JC + * - adapted for partial v2 + * 7/10/94 JC + * - new IM_NEW() + * - more typedefs + * 9/2/95 JC + * - adapted for im_wrap... + * - operations on complex images now just transform the real channel + * 29/9/95 JC + * - complex was broken + * 15/4/97 JC + * - return(0) missing from generate, arrgh! + * 1/7/98 JC + * - im_lintra_vec added + * 3/8/02 JC + * - fall back to im_copy() for a == 1, b == 0 + * 10/10/02 JC + * - auug, failing to multiply imag for complex! (thanks matt) + * 10/12/02 JC + * - removed im_copy() fallback ... meant that output format could change + * with value :-( very confusing + * 30/6/04 + * - added 1 band image * n band vector case + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Struct we need for im_generate(). + */ +typedef struct { + int n; /* Number of bands of constants */ + double *a, *b; +} LintraInfo; + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define LOOP(IN, OUT) { \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + \ + for( x = 0; x < sz; x++ ) \ + q[x] = a * (OUT) p[x] + b; \ +} + +/* Complex input, complex output. + */ +#define LOOPCMPLX(IN, OUT) {\ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + \ + for( x = 0; x < sz; x++ ) { \ + q[0] = a * p[0] + b; \ + q[1] = a * p[1]; \ + q += 2; \ + p += 2; \ + } \ +} + +#ifdef HAVE_LIBOIL +/* Process granularity. + */ +#define CHUNKS (1000) + +/* d[] = s[] * b + c, with liboil + */ +static void +lintra_f32( float *d, float *s, int n, float b, float c ) +{ + float buf[CHUNKS]; + int i; + + for( i = 0; i < n; i += CHUNKS ) { + oil_scalarmultiply_f32_ns( buf, s, + &b, IM_MIN( CHUNKS, n - i ) ); + oil_scalaradd_f32_ns( d, buf, + &c, IM_MIN( CHUNKS, n - i ) ); + + s += CHUNKS; + d += CHUNKS; + } +} +#endif /*HAVE_LIBOIL*/ + +/* Lintra a buffer, 1 set of scale/offset. + */ +static int +lintra1_gen( PEL *in, PEL *out, int width, IMAGE *im, LintraInfo *inf ) +{ + double a = inf->a[0]; + double b = inf->b[0]; + int sz = width * im->Bands; + int x; + + /* Lintra all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOP( unsigned char, float ); break; + case IM_BANDFMT_CHAR: LOOP( signed char, float ); break; + case IM_BANDFMT_USHORT: LOOP( unsigned short, float ); break; + case IM_BANDFMT_SHORT: LOOP( signed short, float ); break; + case IM_BANDFMT_UINT: LOOP( unsigned int, float ); break; + case IM_BANDFMT_INT: LOOP( signed int, float ); break; + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + lintra_f32( (float *) out, (float *) in, sz, a, b ); +#else /*!HAVE_LIBOIL*/ + LOOP( float, float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: LOOP( double, double ); break; + case IM_BANDFMT_COMPLEX: LOOPCMPLX( float, float ); break; + case IM_BANDFMT_DPCOMPLEX: LOOPCMPLX( double, double ); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define LOOPN(IN, OUT)\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( i = 0, x = 0; x < width; x++ )\ + for( k = 0; k < nb; k++, i++ )\ + q[i] = a[k] * (OUT) p[i] + b[k];\ + } + +/* Complex input, complex output. + */ +#define LOOPCMPLXN(IN, OUT)\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < width; x++ ) \ + for( k = 0; k < nb; k++ ) {\ + q[0] = a[k] * p[0] + b[k];\ + q[1] = a[k] * p[1];\ + q += 2;\ + p += 2;\ + }\ + } + +/* Lintra a buffer, n set of scale/offset. + */ +static int +lintran_gen( PEL *in, PEL *out, int width, IMAGE *im, LintraInfo *inf ) +{ + double *a = inf->a; + double *b = inf->b; + int nb = im->Bands; + int i, x, k; + + /* Lintra all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOPN( unsigned char, float ); break; + case IM_BANDFMT_CHAR: LOOPN( signed char, float ); break; + case IM_BANDFMT_USHORT: LOOPN( unsigned short, float ); break; + case IM_BANDFMT_SHORT: LOOPN( signed short, float ); break; + case IM_BANDFMT_UINT: LOOPN( unsigned int, float ); break; + case IM_BANDFMT_INT: LOOPN( signed int, float ); break; + case IM_BANDFMT_FLOAT: LOOPN( float, float ); break; + case IM_BANDFMT_DOUBLE: LOOPN( double, double ); break; + case IM_BANDFMT_COMPLEX: LOOPCMPLXN( float, float ); break; + case IM_BANDFMT_DPCOMPLEX: LOOPCMPLXN( double, double ); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* 1 band image, n band vector. + */ +#define LOOPNV(IN, OUT) { \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + \ + for( i = 0, x = 0; x < width; x++ ) { \ + OUT v = p[x]; \ + \ + for( k = 0; k < nb; k++, i++ ) \ + q[i] = a[k] * v + b[k]; \ + } \ +} + +#define LOOPCMPLXNV(IN, OUT) { \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + \ + for( x = 0; x < width; x++ ) { \ + OUT p0 = p[0]; \ + OUT p1 = p[1]; \ + \ + for( k = 0; k < nb; k++ ) { \ + q[0] = a[k] * p0 + b[k]; \ + q[1] = a[k] * p1; \ + q += 2; \ + } \ + \ + p += 2; \ + } \ +} + +static int +lintranv_gen( PEL *in, PEL *out, int width, IMAGE *im, LintraInfo *inf ) +{ + double *a = inf->a; + double *b = inf->b; + int nb = inf->n; + int i, x, k; + + /* Lintra all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOPNV( unsigned char, float ); break; + case IM_BANDFMT_CHAR: LOOPNV( signed char, float ); break; + case IM_BANDFMT_USHORT: LOOPNV( unsigned short, float ); break; + case IM_BANDFMT_SHORT: LOOPNV( signed short, float ); break; + case IM_BANDFMT_UINT: LOOPNV( unsigned int, float ); break; + case IM_BANDFMT_INT: LOOPNV( signed int, float ); break; + case IM_BANDFMT_FLOAT: LOOPNV( float, float ); break; + case IM_BANDFMT_DOUBLE: LOOPNV( double, double ); break; + case IM_BANDFMT_COMPLEX: LOOPCMPLXNV( float, float ); break; + case IM_BANDFMT_DPCOMPLEX: LOOPCMPLXNV( double, double ); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Linear transform n bands. + */ +int +im_lintra_vec( int n, double *a, IMAGE *in, double *b, IMAGE *out ) +{ + LintraInfo *inf; + int i; + + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_lintra_vec", _( "not uncoded" ) ); + return( -1 ); + } + + /* If n and bands differ, one of them must be one (and we multiplex + * the other). + */ + if( n != in->Bands && (n != 1 && in->Bands != 1) ) { + im_error( "im_lintra_vec", + _( "not 1 or %d elements in vector" ), in->Bands ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + if( in->Bands == 1 ) + out->Bands = n; + + /* Make space for a little buffer. + */ + if( !(inf = IM_NEW( out, LintraInfo )) || + !(inf->a = IM_ARRAY( out, n, double )) || + !(inf->b = IM_ARRAY( out, n, double )) ) + return( -1 ); + inf->n = n; + for( i = 0; i < n; i++ ) { + inf->a[i] = a[i]; + inf->b[i] = b[i]; + } + + /* Generate! + */ + if( n == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) lintra1_gen, in, inf ) ) + return( -1 ); + } + else if( in->Bands == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) lintranv_gen, in, inf ) ) + return( -1 ); + } + else { + if( im_wrapone( in, out, + (im_wrapone_fn) lintran_gen, in, inf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Linear transform. + */ +int +im_lintra( double a, IMAGE *in, double b, IMAGE *out ) +{ + return( im_lintra_vec( 1, &a, in, &b, out ) ); +} diff --git a/libsrc/arithmetic/im_litecor.c b/libsrc/arithmetic/im_litecor.c new file mode 100644 index 00000000..9b4aa577 --- /dev/null +++ b/libsrc/arithmetic/im_litecor.c @@ -0,0 +1,321 @@ +/* @(#) Function to perform lighting correction. + * @(#) One band IM_BANDFMT_UCHAR images only. Always writes UCHAR. + * @(#) + * @(#) Function im_litecor() assumes that imin + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) int im_litecor(in, w, out, clip, factor) + * @(#) IMAGE *in, *w, *out; + * @(#) int clip; + * @(#) double factor; + * @(#) + * @(#) clip==1 + * @(#) - Compute max(white)*factor*(image/white), Clip to 255. + * @(#) clip==0 + * @(#) - Compute factor for you. + * @(#) + * @(#) + * @(#) + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt, 1991 N. Dessipris + * + * Author: J. Cupitt, N. Dessipris + * Written on: 02/08/1990 + * Modified on : 6/11/1991, by ND to produce a UCHAR output + * 1/4/93 J.Cupitt + * - bugs if white is smaller than image fixed + * - im_warning() now called + * - clip==0 case not tested or changed! do not use! + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* If maximum output is > 255 scale output between minout and maxout, + * by normalising maxout to 255. + * If maximum output is < 255 do the light correction without scaling + */ +static int +im_litecor0( in, white, out ) +IMAGE *in, *white, *out; +{ PEL *p, *w; + PEL *q, *bu; + int c; + int x, y; + float xrat = (float) in->Xsize / white->Xsize; + float yrat = (float) in->Ysize / white->Ysize; + int xstep = (int) xrat; + int ystep = (int) yrat; + double max; + int wtmp, maxw, maxout, temp; + + /* Check white is some simple multiple of image. + */ + if( xrat < 1.0 || xrat != xstep || yrat < 1.0 || yrat != ystep ) { + im_error( "im_litecor", "white not simple scale of image" ); + return( -1 ); + } + + /* Find the maximum of the white. + */ + if( im_max( white, &max ) ) + return( -1 ); + maxw = (int)max; + + /* Set up the output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Make buffer for outputting to. + */ + if( !(bu = (PEL *) im_malloc( out, out->Xsize )) ) + return( -1 ); + + /* Find largest value we might generate if factor == 1.0 + */ + maxout = -1; + p = (PEL *) in->data; + for( y = 0; y < in->Ysize; y++ ) { + /* Point w to the start of the line in the white + * corresponding to the line we are about to correct. c counts + * up to xstep; each time it wraps, we should move w on one. + */ + w = (PEL *) (white->data + white->Xsize * (int)(y/ystep)); + c = 0; + + /* Scan along line. + */ + for( x = 0; x < out->Xsize; x++ ) { + wtmp = (int)*w; + temp = ( maxw * (int) *p++ + (wtmp>>1) ) / wtmp; + if (temp > maxout ) + maxout = temp; + + /* Move white pointer on if necessary. */ + c++; + if( c == xstep ) { + w++; + c = 0; + } + } + } + + /* Do exactly the same as above by scaling the result with respect to + * maxout + */ + p = (PEL *) in->data; + if (maxout <= 255 ) /* no need for rescaling output */ + { + for( y = 0; y < in->Ysize; y++ ) + { + q = bu; + w = (PEL *) (white->data + + white->Xsize * (int)(y/ystep)); + c = 0; + + /* Scan along line. */ + for( x = 0; x < in->Xsize; x++ ) + { + wtmp = (int)*w; + *q++ = (PEL) + ( ( maxw * (int) *p++ + (wtmp>>1) ) / wtmp ); + /* Move white pointer on if necessary. + */ + c++; + if( c == xstep ) { w++; c = 0; } + } + if( im_writeline( y, out, bu ) ) + { + im_error("im_litecor", "im_writeline failed"); + return( -1 ); + } + } + } + else /* rescale output wrt maxout */ + { + for( y = 0; y < in->Ysize; y++ ) + { + q = bu; + w = (PEL *) (white->data + + white->Xsize * (int)(y/ystep)); + c = 0; + + /* Scan along line. */ + for( x = 0; x < in->Xsize; x++ ) + { + wtmp = maxout * ((int)*w); + *q++ = (PEL) + ( ( maxw * (int) *p++ * 255 + (wtmp>>1)) / wtmp ); + /* Move white pointer on if necessary. + */ + c++; + if( c == xstep ) { w++; c = 0; } + } + if( im_writeline( y, out, bu ) ) + { + im_error("im_litecor", "im_writeline failed"); + return( -1 ); + } + } + } + + return( 0 ); +} + +/* Clip all corrected values above 255, if any. + */ +static int +im_litecor1( in, white, out, factor ) +IMAGE *in, *white, *out; +double factor; +{ PEL *p, *w; + PEL *q, *bu; + int c; + int x, y; + float xrat = (float) in->Xsize / white->Xsize; + float yrat = (float) in->Ysize / white->Ysize; + int xstep = (int) xrat; + int ystep = (int) yrat; + double max; + double maxw, temp; + int nclipped = 0; + + /* Check white is some simple multiple of image. + */ + if( xrat < 1.0 || xrat != xstep || yrat < 1.0 || yrat != ystep ) { + im_error( "im_litecor", "white not simple scale of image" ); + return( -1 ); + } + + /* Find the maximum of the white. + */ + if( im_max( white, &max ) ) + return( -1 ); + maxw = max; + + /* Set up the output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Make buffer we write to. + */ + if( !(bu = (PEL *) im_malloc( out, out->Xsize )) ) + return( -1 ); + + /* Loop through sorting max output + */ + p = (PEL *) in->data; + for( y = 0; y < in->Ysize; y++ ) { + q = bu; + w = (PEL *) (white->data + white->Xsize * (int)(y / ystep)); + c = 0; + + for( x = 0; x < out->Xsize; x++ ) { + temp = ((factor * maxw * (int) *p++)/((int) *w)) + 0.5; + if( temp > 255.0 ) { + temp = 255; + nclipped++; + } + *q++ = temp; + + /* Move white pointer on if necessary. + */ + c++; + if( c == xstep ) { + w++; + c = 0; + } + } + + if( im_writeline( y, out, bu ) ) + return( -1 ); + } + + if( nclipped ) + im_warn( "im_litecor", "%d pels over 255 clipped", nclipped ); + + return( 0 ); +} + +/* Lighting correction. One band uchar images only. + * Assumes the white is some simple multiple of the image in size; ie. the + * white has been taken with some smaller or equal set of resolution + * parameters. + */ +int +im_litecor( in, white, out, clip, factor ) +IMAGE *in, *white, *out; +int clip; +double factor; +{ /* Check our args. + */ + if( im_iocheck( in, out ) ) + return( -1 ); + if( in->Bands != 1 || in->Bbits != 8 || + in->Coding != IM_CODING_NONE || in->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_litecor", "bad input format" ); + return( -1 ); + } + if( white->Bands != 1 || white->Bbits != 8 || + white->Coding != IM_CODING_NONE || white->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_litecor", "bad white format" ); + return( -1 ); + } + + switch( clip ) { + case 1: + return( im_litecor1( in, white, out, factor ) ); + + case 0: + return( im_litecor0( in, white, out ) ); + + default: + im_error( "im_litecor", "unknown flag %d", clip ); + return( -1 ); + } +} diff --git a/libsrc/arithmetic/im_log10tra.c b/libsrc/arithmetic/im_log10tra.c new file mode 100644 index 00000000..4b450c65 --- /dev/null +++ b/libsrc/arithmetic/im_log10tra.c @@ -0,0 +1,166 @@ +/* @(#) Find base 10 log of any non-complex image. Output + * @(#) is always float for integer input and double for double input. + * @(#) + * @(#) int + * @(#) im_log10tra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop(IN, OUT)\ + for( y = to; y < bo; y++ ) {\ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y );\ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ )\ + *q++ = log10( *p++ );\ + } + +/* log10tra a small area. + */ +static int +log10tra_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + int x, y; + + /* Ask for input we need. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* log10tra all input types. + */ + switch( ir->im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char, float); break; + case IM_BANDFMT_CHAR: loop(signed char, float); break; + case IM_BANDFMT_USHORT: loop(unsigned short, float); break; + case IM_BANDFMT_SHORT: loop(signed short, float); break; + case IM_BANDFMT_UINT: loop(unsigned int, float); break; + case IM_BANDFMT_INT: loop(signed int, float); break; + case IM_BANDFMT_FLOAT: loop(float, float); break; + case IM_BANDFMT_DOUBLE: loop(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Log 10 transform. + */ +int +im_log10tra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_log10tra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_log10tra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, log10tra_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_logtra.c b/libsrc/arithmetic/im_logtra.c new file mode 100644 index 00000000..e56dfa84 --- /dev/null +++ b/libsrc/arithmetic/im_logtra.c @@ -0,0 +1,166 @@ +/* @(#) Find natural log of any non-complex image. Output + * @(#) is always float for integer input and double for double input. + * @(#) + * @(#) int + * @(#) im_logtra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop(IN, OUT)\ + for( y = to; y < bo; y++ ) {\ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y );\ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ )\ + *q++ = log( *p++ );\ + } + +/* logtra a small area. + */ +static int +logtra_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + int x, y; + + /* Ask for input we need. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* logtra all input types. + */ + switch( ir->im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char, float); break; + case IM_BANDFMT_CHAR: loop(signed char, float); break; + case IM_BANDFMT_USHORT: loop(unsigned short, float); break; + case IM_BANDFMT_SHORT: loop(signed short, float); break; + case IM_BANDFMT_UINT: loop(unsigned int, float); break; + case IM_BANDFMT_INT: loop(signed int, float); break; + case IM_BANDFMT_FLOAT: loop(float, float); break; + case IM_BANDFMT_DOUBLE: loop(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Log transform. + */ +int +im_logtra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_logtra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_logtra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, logtra_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_max.c b/libsrc/arithmetic/im_max.c new file mode 100644 index 00000000..d040bf6f --- /dev/null +++ b/libsrc/arithmetic/im_max.c @@ -0,0 +1,242 @@ +/* @(#) Function to find the maximim of an image. Works for any + * @(#) image type. Returns a double. + * @(#) + * @(#) int im_max(in, max) + * @(#) IMAGE *in; + * @(#) double *max; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/05/1990 + * Modified on : 18/03/1991, N. Dessipris + * 7/7/93 JC + * - complex case fixed + * - im_incheck() call added + * 20/6/95 JC + * - now returns double + * - modernised a little + * - now returns max square amplitude rather than amplitude for complex + * 9/5/02 JC + * - partialed + * 3/4/02 JC + * - random wrong result for >1 thread :-( (thanks Joe) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Per-call state. + */ +typedef struct _MaxInfo { + /* Parameters. + */ + IMAGE *in; + double *out; + + /* Global max so far. + */ + double value; + int valid; /* zero means value is unset */ +} MaxInfo; + +/* Per thread state. + */ +typedef struct _Seq { + MaxInfo *inf; + + double value; + int valid; /* zero means value is unset */ +} Seq; + +/* New sequence value. + */ +static void * +start_fn( MaxInfo *inf ) +{ + Seq *seq = IM_NEW( NULL, Seq ); + + seq->inf = inf; + seq->valid = 0; + + return( (void *) seq ); +} + +/* Merge the sequence value back into the per-call state. + */ +static int +stop_fn( Seq *seq, MaxInfo *inf ) +{ + if( seq->valid ) { + if( !inf->valid ) + /* Just copy. + */ + inf->value = seq->value; + else + /* Merge. + */ + inf->value = IM_MAX( inf->value, seq->value ); + + inf->valid = 1; + } + + im_free( seq ); + + return( 0 ); +} + +/* Loop over region, adding to seq. + */ +static int +scan_fn( REGION *reg, Seq *seq ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int nel = IM_REGION_N_ELEMENTS( reg ); + + int x, y; + + double m; + +#define loop(TYPE) { \ + m = *((TYPE *) IM_REGION_ADDR( reg, le, to )); \ + \ + for( y = to; y < bo; y++ ) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < nel; x++ ) { \ + double v = p[x]; \ + \ + if( v > m ) \ + m = v; \ + } \ + } \ +} + +#define complex_loop(TYPE) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, to ); \ + double real = p[0]; \ + double imag = p[1]; \ + \ + m = real * real + imag * imag; \ + \ + for( y = to; y < bo; y++ ) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < nel * 2; x += 2 ) { \ + double mod; \ + \ + real = p[x]; \ + imag = p[x + 1]; \ + mod = real * real + imag * imag; \ + \ + if( mod > m ) \ + m = mod; \ + } \ + } \ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char ); break; + case IM_BANDFMT_CHAR: loop( signed char ); break; + case IM_BANDFMT_USHORT: loop( unsigned short ); break; + case IM_BANDFMT_SHORT: loop( signed short ); break; + case IM_BANDFMT_UINT: loop( unsigned int ); break; + case IM_BANDFMT_INT: loop( signed int ); break; + case IM_BANDFMT_FLOAT: loop( float ); break; + case IM_BANDFMT_DOUBLE: loop( double ); break; + case IM_BANDFMT_COMPLEX: complex_loop( float ); break; + case IM_BANDFMT_DPCOMPLEX: complex_loop( double ); break; + + default: + assert( 0 ); + } + + if( seq->valid ) { + seq->value = IM_MAX( seq->value, m ); + } + else { + seq->value = m; + seq->valid = 1; + } + +#ifdef DEBUG + printf( "im_max: left = %d, top = %d, width = %d, height = %d\n", + r->left, r->top, r->width, r->height ); + printf( " (max = %g)\n", seq->value ); +#endif /*DEBUG*/ + + return( 0 ); +} + +int +im_max( IMAGE *in, double *out ) +{ + MaxInfo inf; + + inf.in = in; + inf.out = out; + inf.valid = 0; + + if( im_pincheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_max", _( "not uncoded" ) ); + return( -1 ); + } + + if( im_iterate( in, start_fn, scan_fn, stop_fn, &inf, NULL ) ) + return( -1 ); + + *out = inf.value; + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_maxpos.c b/libsrc/arithmetic/im_maxpos.c new file mode 100644 index 00000000..32cd8284 --- /dev/null +++ b/libsrc/arithmetic/im_maxpos.c @@ -0,0 +1,150 @@ +/* @(#) Function to find the maximum of an image. Works for any + * @(#) image type. Returns a double and the location of max. + * @(#) + * @(#) Function im_maxpos() assumes that input + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) int im_maxpos(in, xpos, ypos, max) + * @(#) IMAGE *in; + * @(#) int *xpos, *ypos; + * @(#) double *max; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/05/1990 + * Modified on : 18/03/1991, N. Dessipris + * 23/11/92: J.Cupitt - correct result for more than 1 band now. + * 23/7/93 JC + * - im_incheck() call added + * 20/6/95 JC + * - now returns double for value, like im_max() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Useful: Call a macro with the name, type pairs for all VIPS functions. + */ +#define im_for_all_types() \ + case IM_BANDFMT_UCHAR: loop(unsigned char); break; \ + case IM_BANDFMT_CHAR: loop(signed char); break; \ + case IM_BANDFMT_USHORT: loop(unsigned short); break; \ + case IM_BANDFMT_SHORT: loop(signed short); break; \ + case IM_BANDFMT_UINT: loop(unsigned int); break; \ + case IM_BANDFMT_INT: loop(signed int); break; \ + case IM_BANDFMT_FLOAT: loop(float); break; \ + case IM_BANDFMT_DOUBLE: loop(double); break; \ + case IM_BANDFMT_COMPLEX: loopcmplx(float); break; \ + case IM_BANDFMT_DPCOMPLEX: loopcmplx(double); break; + +/* Find the position of the maximum of an image. Take any format, returns a + * float and its position. + */ +int +im_maxpos( IMAGE *in, int *xpos, int *ypos, double *out ) +{ + double m; + int xp=0, yp=0; + int os; + +/* Check our args. */ + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_maxpos", _( "not uncoded" ) ); + return( -1 ); + } + +/* What type? First define the loop we want to perform for all types. */ +#define loop(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y; \ + m = (double) *p; \ + \ + for ( y=0; yYsize; y++ ) \ + for ( x=0; x m ) {\ + m = (double) *p; \ + xp = x; yp = y; \ + }\ + p++ ;\ + }\ + } + +#define loopcmplx(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + double re=(double)*p;\ + double im=(double)*(p+1);\ + double mod = re * re + im * im;\ + int x, y; \ + m = mod; \ + \ + for ( y=0; yYsize; y++ ) \ + for ( x=0; x m ) {\ + m = mod; \ + xp = x; yp = y; \ + }\ + }\ + } + +/* Now generate code for all types. */ + os = in->Xsize * in->Bands; + switch( in->BandFmt ) { + im_for_all_types(); + default: { + assert( 0 ); + return( -1 ); + } + } + + /* Return maxima and position of maxima. Nasty: we divide the xpos by + * the number of bands to get the position in pixels. + */ + *out = m; + *xpos = xp / in->Bands; + *ypos = yp; + return( 0 ); +} diff --git a/libsrc/arithmetic/im_maxpos_avg.c b/libsrc/arithmetic/im_maxpos_avg.c new file mode 100644 index 00000000..4d2bbe11 --- /dev/null +++ b/libsrc/arithmetic/im_maxpos_avg.c @@ -0,0 +1,201 @@ +/* @(#) Function to find the maximum of an image. Returns coords and value at + * @(#) double precision. In the event of a draw, returns average of all + * @(#) drawing coords, and interpolated value at that position. + * @(#) + * @(#) int im_maxpos_avg( + * @(#) IMAGE *im, + * @(#) double *xpos, + * @(#) double *ypos, + * @(#) double *out + * @(#) ); + * @(#) + * + * Copyright: 2006, The Nottingham Trent University + * Copyright: 2006, Tom Vajzovic + * + * Author: Tom Vajzovic + * + * Written on: 2006-09-25 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** LOCAL TYPES **/ + +typedef struct { + double x_avg; + double y_avg; + double val; + unsigned int occurances; + +} pos_avg_t; + + +/** LOCAL FUNCTIONS DECLARATIONS **/ + +static pos_avg_t * +maxpos_avg_start( + IMAGE *im +); +static int /* should be void (always returns 0) */ +maxpos_avg_scan( + REGION *reg, + pos_avg_t *seq +); +static int +maxpos_avg_stop( + pos_avg_t *seq, + pos_avg_t *master +); + + +/** EXPORTED FUNCTION **/ + +int im_maxpos_avg( IMAGE *im, double *xpos, double *ypos, double *out ){ +#define FUNCTION_NAME "im_maxpos_avg" + + pos_avg_t master= { 0.0, 0.0, 0.0, 0 }; + + if( im_pincheck( im ) ) + return -1; + + if( im-> Coding ){ + im_error( FUNCTION_NAME, _("uncoded images only") ); + return -1; + } + if( !( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, _("scalar images only") ); + return -1; + } + if( 1 != im-> Bands ){ + im_error( FUNCTION_NAME, _("single band images only") ); + return -1; + } + if( ! xpos || ! ypos || ! out ){ + im_error( FUNCTION_NAME, _("invalid argument") ); + return -1; + } + if( im_iterate( im, (void*)maxpos_avg_start, maxpos_avg_scan, maxpos_avg_stop, &master, NULL ) ) + return -1; + + *xpos= master. x_avg / master. occurances; + *ypos= master. y_avg / master. occurances; + + return im_point_bilinear( im, *xpos, *ypos, 0, out ); + +#undef FUNCTION_NAME +} + +static pos_avg_t *maxpos_avg_start( IMAGE *im ){ + pos_avg_t *seq; + + seq= im_malloc( NULL, sizeof( pos_avg_t ) ); + if( ! seq ) + return NULL; + + seq-> x_avg= 0.0; + seq-> y_avg= 0.0; + seq-> val= 0.0; + seq-> occurances= 0; + + return seq; +} + +/* should be void (always returns 0) */ +static int maxpos_avg_scan( REGION *reg, pos_avg_t *seq ){ + + const int right= reg-> valid. left + reg-> valid. width; + const int bottom= reg-> valid. top + reg-> valid. height; + int x; + int y; + +#define LOOPS(type){ \ + type *read= (type*) IM_REGION_ADDR( reg, reg-> valid. left, reg-> valid. top ) - reg-> valid. left; \ + size_t skip= IM_REGION_LSKIP( reg ) / sizeof( type ); \ + \ + for( y= reg-> valid. top; y < bottom; ++y, read+= skip ) \ + for( x= reg-> valid. left; x < right; ++x ) \ + if( read[x] > seq-> val ){ \ + seq-> val= read[x]; \ + seq-> x_avg= x; \ + seq-> y_avg= y; \ + seq-> occurances= 1; \ + } \ + else if( read[x] == seq-> val ){ \ + seq-> x_avg+= x; \ + seq-> y_avg+= y; \ + ++ (seq-> occurances); \ + } \ +} + + switch( reg-> im-> BandFmt ){ + case IM_BANDFMT_CHAR: LOOPS( gint8 ) break; + case IM_BANDFMT_UCHAR: LOOPS( guint8 ) break; + case IM_BANDFMT_SHORT: LOOPS( gint16 ) break; + case IM_BANDFMT_USHORT: LOOPS( guint16 ) break; + case IM_BANDFMT_INT: LOOPS( gint32 ) break; + case IM_BANDFMT_UINT: LOOPS( guint32 ) break; + case IM_BANDFMT_FLOAT: LOOPS( float ) break; + case IM_BANDFMT_DOUBLE: LOOPS( double ) break; + } + + return 0; +#undef LOOPS +} + +/* should be void (always returns 0) */ +static int maxpos_avg_stop( pos_avg_t *seq, pos_avg_t *master ){ + + if( seq-> val > master-> val ){ + master-> val= seq-> val; + master-> x_avg= seq-> x_avg; + master-> y_avg= seq-> y_avg; + master-> occurances= seq-> occurances; + } + else if( seq-> val == master-> val ){ + master-> x_avg+= seq-> x_avg; + master-> y_avg+= seq-> y_avg; + master-> occurances+= seq-> occurances; + } + return im_free( seq ); +} + diff --git a/libsrc/arithmetic/im_maxpos_vec.c b/libsrc/arithmetic/im_maxpos_vec.c new file mode 100644 index 00000000..7dcda1e4 --- /dev/null +++ b/libsrc/arithmetic/im_maxpos_vec.c @@ -0,0 +1,459 @@ +/* @(#) Find the coordinates and values of the maxima of an image. + * @(#) + * @(#) int im_maxpos_vec( + * @(#) IMAGE *im, + * @(#) int *xpos, + * @(#) int *ypos, + * @(#) double *maxima, + * @(#) int n + * @(#) ); + * @(#) + * @(#) Find the coordinates and values of the minima of an image. + * @(#) + * @(#) int im_minpos_vec( + * @(#) IMAGE *im, + * @(#) int *xpos, + * @(#) int *ypos, + * @(#) double *minima, + * @(#) int n + * @(#) ); + * @(#) + * + * Copyright: 2006, The Nottingham Trent University + * Copyright: 2006, Tom Vajzovic + * + * Author: Tom Vajzovic + * + * Written on: 2006-09-01 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** TYPE DEFINITIONS **/ + +typedef struct { + + int *xs; + int *ys; + double *vals; + int *ptrs; + int start; + +} maxpos_list; + + +/** LOCAL FUNCTIONS DECLARATIONS **/ + +static maxpos_list *maxpos_list_alloc( int n ); +static void maxpos_list_free( maxpos_list *list ); + +static void maxpos_list_init( maxpos_list *list, int n ); +static maxpos_list *maxpos_vec_start( void *unrequired, int *n ); +static int maxpos_vec_scan( REGION *reg, maxpos_list *list ); +static void add_to_maxpos_list( maxpos_list *list, int x, int y, double val ); +static int maxpos_vec_stop( maxpos_list *list, int *n, maxpos_list *master_list ); + +static void minpos_list_init( maxpos_list *list, int n ); +static maxpos_list *minpos_vec_start( void *unrequired, int *n ); +static int minpos_vec_scan( REGION *reg, maxpos_list *list ); +static void add_to_minpos_list( maxpos_list *list, int x, int y, double val ); +static int minpos_vec_stop( maxpos_list *list, int *n, maxpos_list *master_list ); + + +/** EXPORTED FUNCTIONS **/ + +int im_maxpos_vec( IMAGE *im, int *xpos, int *ypos, double *maxima, int n ){ +#define FUNCTION_NAME "im_maxpos_vec" + /* number of sequences used is beyond my control at this level, but I note that */ + /* effeciency decreases as more sequences are used - speed may still increase */ + + int result; + int *pointers= im_malloc( NULL, n * sizeof( int* ) ); + maxpos_list master_list= { xpos, ypos, maxima, pointers, 0 }; + + if( im_pincheck( im ) ) + return -1; + + if( !pointers ) + return -1; + + if( ! ( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, _( "scalar images only" ) ); + return -1; + } + + if( 1 != im-> Bands ){ + im_error( FUNCTION_NAME, _( "single band images only" ) ); + return -1; + } + + if( IM_CODING_NONE != im-> Coding ){ + im_error( FUNCTION_NAME, _( "uncoded images only" ) ); + return -1; + } + + if( ! xpos || ! ypos || ! maxima || n < 1 ){ + im_error( FUNCTION_NAME, _( "invalid argument" ) ); + return -1; + } + + maxpos_list_init( &master_list, n ); + + result= im_iterate( im, (void*)maxpos_vec_start, maxpos_vec_scan, maxpos_vec_stop, &n, &master_list ); + + im_free( pointers ); + + return result; +#undef FUNCTION_NAME +} + + +int im_minpos_vec( IMAGE *im, int *xpos, int *ypos, double *minima, int n ){ +#define FUNCTION_NAME "im_minpos_vec" + /* number of sequences used is beyond my control at this level, but I note that */ + /* effeciency decreases as more sequences are used - speed may still increase */ + + int result; + int *pointers= im_malloc( NULL, n * sizeof( int* ) ); + maxpos_list master_list= { xpos, ypos, minima, pointers, 0 }; + + if( im_pincheck( im ) ) + return -1; + + if( !pointers ) + return -1; + + if( ! ( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, _( "scalar images only" ) ); + return -1; + } + + if( 1 != im-> Bands ){ + im_error( FUNCTION_NAME, _( "single band images only" ) ); + return -1; + } + + if( IM_CODING_NONE != im-> Coding ){ + im_error( FUNCTION_NAME, _( "uncoded images only" ) ); + return -1; + } + + if( ! xpos || ! ypos || ! minima || n < 1 ){ + im_error( FUNCTION_NAME, _( "invalid argument" ) ); + return -1; + } + + minpos_list_init( &master_list, n ); + + result= im_iterate( im, (void*)minpos_vec_start, minpos_vec_scan, minpos_vec_stop, &n, &master_list ); + + im_free( pointers ); + + return result; +#undef FUNCTION_NAME +} + + +/** LOCAL FUNCTION DEFINITIONS **/ + +static maxpos_list *maxpos_list_alloc( int n ){ + + maxpos_list *list= im_malloc( NULL, sizeof( maxpos_list ) ); + + if( ! list ) + return NULL; + + list-> xs= im_malloc( NULL, 3 * n * sizeof( int ) ); + list-> vals= im_malloc( NULL, n * sizeof( double ) ); + + if( ! list-> xs || ! list-> vals ){ + im_free( list-> xs ); + im_free( list-> vals ); + im_free( list ); + return NULL; + } + list-> ys= list-> xs + n; + list-> ptrs= list-> ys + n; + + return list; +} + +static void maxpos_list_free( maxpos_list *list ){ + im_free( list-> xs ); + im_free( list-> vals ); + im_free( list ); +} + + +static void maxpos_list_init( maxpos_list *list, int n ){ + int i; + + for( i= 0; i < n; ++i ){ + list-> xs[ i ]= 0; + list-> ys[ i ]= 0; + list-> vals[ i ]= 0; + list-> ptrs[ i ]= i + 1; + } + + list-> ptrs[ n - 1 ]= -1; + list-> start= 0; +} + +static maxpos_list *maxpos_vec_start( void *unrequired, int *n ){ + + maxpos_list *list= maxpos_list_alloc( *n ); + + if( ! list ) + return NULL; + + maxpos_list_init( list, *n ); + + return list; +} + +static int maxpos_vec_scan( REGION *reg, maxpos_list *list ){ + +#define MAXPOS_VEC_SCAN( type ){ \ + \ + int y= reg-> valid. top; \ + int x; \ + int ymax= y + reg-> valid. height; \ + int xmax= reg-> valid. left + reg-> valid. width; \ + \ + type *row= (type*)IM_REGION_ADDR( reg, reg-> valid. left, y ) - reg-> valid. left; \ + size_t skip= IM_REGION_LSKIP( reg ) / sizeof( type ); \ + \ + for( ; y < ymax; ++y, row+= skip ) \ + for( x= reg-> valid. left; x < xmax; ++x ) \ + if( row[ x ] > list-> vals[ list-> start ] ) \ + add_to_maxpos_list( list, x, y, row[ x ] ); \ +} + + switch( reg-> im-> BandFmt ){ + case IM_BANDFMT_UCHAR: MAXPOS_VEC_SCAN( guint8 ) break; + case IM_BANDFMT_CHAR: MAXPOS_VEC_SCAN( gint8 ) break; + case IM_BANDFMT_USHORT: MAXPOS_VEC_SCAN( guint16 ) break; + case IM_BANDFMT_SHORT: MAXPOS_VEC_SCAN( gint16 ) break; + case IM_BANDFMT_UINT: MAXPOS_VEC_SCAN( guint32 ) break; + case IM_BANDFMT_INT: MAXPOS_VEC_SCAN( gint32 ) break; + case IM_BANDFMT_FLOAT: MAXPOS_VEC_SCAN( float ) break; + case IM_BANDFMT_DOUBLE: MAXPOS_VEC_SCAN( double ) break; + } + +#undef MAXPOS_VEC_SCAN + + return 0; +} + +static void add_to_maxpos_list( maxpos_list *list, int x, int y, double val ){ + + int pointer= list-> start; + + while( -1 != list-> ptrs[ pointer ] && val > list-> vals[ list-> ptrs[ pointer ] ] ) + pointer= list-> ptrs[ pointer ]; + + list-> xs[ list-> start ]= x; + list-> ys[ list-> start ]= y; + list-> vals[ list-> start ]= val; + + if( list-> start != pointer ){ + /* we are adding mid-chain not at the very bottom */ + int second= list-> ptrs[ list-> start ]; + + list-> ptrs[ list-> start ]= list-> ptrs[ pointer ]; + list-> ptrs[ pointer ]= list-> start; + list-> start= second; + } +} + +static int maxpos_vec_stop( maxpos_list *list, int *n, maxpos_list *master_list ){ + + /* reverse list */ + + int prev= -1; + int pointer= list-> start; + + while( -1 != list-> ptrs[ pointer ] ){ + + int next= list-> ptrs[ pointer ]; + + list-> ptrs[ pointer ]= prev; + prev= pointer; + pointer= next; + } + list-> ptrs[ pointer ]= prev; + list-> start= pointer; + + /* add to main list */ + + for( ; -1 != pointer; pointer= list-> ptrs[ pointer ] ) + /* loop over all the ones found in this sequence */ + + if( list-> vals[ pointer ] > master_list-> vals[ master_list-> start ] ) + add_to_maxpos_list( master_list, list-> xs[ pointer ], list-> ys[ pointer ], list-> vals[ pointer ] ); + else + break; + /* since we are now high->low, if this one isn't big enough, none of the rest are */ + + maxpos_list_free( list ); + + return 0; +} + + +static void minpos_list_init( maxpos_list *list, int n ){ + int i; + + for( i= 0; i < n; ++i ){ + list-> xs[ i ]= 0; + list-> ys[ i ]= 0; + list-> vals[ i ]= DBL_MAX; + list-> ptrs[ i ]= i + 1; + } + + list-> ptrs[ n - 1 ]= -1; + list-> start= 0; +} + +static maxpos_list *minpos_vec_start( void *unrequired, int *n ){ + + maxpos_list *list= maxpos_list_alloc( *n ); + + if( ! list ) + return NULL; + + minpos_list_init( list, *n ); + + return list; +} + +static int minpos_vec_scan( REGION *reg, maxpos_list *list ){ + +#define MINPOS_VEC_SCAN( type ){ \ + \ + int y= reg-> valid. top; \ + int x; \ + int ymax= y + reg-> valid. height; \ + int xmax= reg-> valid. left + reg-> valid. width; \ + \ + type *row= (type*)IM_REGION_ADDR( reg, reg-> valid. left, y ) - reg-> valid. left; \ + size_t skip= IM_REGION_LSKIP( reg ) / sizeof( type ); \ + \ + for( ; y < ymax; ++y, row+= skip ) \ + for( x= reg-> valid. left; x < xmax; ++x ) \ + if( row[ x ] < list-> vals[ list-> start ] ) \ + add_to_minpos_list( list, x, y, row[ x ] ); \ +} + + switch( reg-> im-> BandFmt ){ + case IM_BANDFMT_UCHAR: MINPOS_VEC_SCAN( guint8 ) break; + case IM_BANDFMT_CHAR: MINPOS_VEC_SCAN( gint8 ) break; + case IM_BANDFMT_USHORT: MINPOS_VEC_SCAN( guint16 ) break; + case IM_BANDFMT_SHORT: MINPOS_VEC_SCAN( gint16 ) break; + case IM_BANDFMT_UINT: MINPOS_VEC_SCAN( guint32 ) break; + case IM_BANDFMT_INT: MINPOS_VEC_SCAN( gint32 ) break; + case IM_BANDFMT_FLOAT: MINPOS_VEC_SCAN( float ) break; + case IM_BANDFMT_DOUBLE: MINPOS_VEC_SCAN( double ) break; + } + +#undef MINPOS_VEC_SCAN + + return 0; +} + +static void add_to_minpos_list( maxpos_list *list, int x, int y, double val ){ + + int pointer= list-> start; + + while( -1 != list-> ptrs[ pointer ] && val < list-> vals[ list-> ptrs[ pointer ] ] ) + pointer= list-> ptrs[ pointer ]; + + list-> xs[ list-> start ]= x; + list-> ys[ list-> start ]= y; + list-> vals[ list-> start ]= val; + + if( list-> start != pointer ){ + /* we are adding mid-chain not at the very bottom */ + int second= list-> ptrs[ list-> start ]; + + list-> ptrs[ list-> start ]= list-> ptrs[ pointer ]; + list-> ptrs[ pointer ]= list-> start; + list-> start= second; + } +} + +static int minpos_vec_stop( maxpos_list *list, int *n, maxpos_list *master_list ){ + + /* reverse list */ + + int prev= -1; + int pointer= list-> start; + + while( -1 != list-> ptrs[ pointer ] ){ + + int next= list-> ptrs[ pointer ]; + + list-> ptrs[ pointer ]= prev; + prev= pointer; + pointer= next; + } + list-> ptrs[ pointer ]= prev; + list-> start= pointer; + + /* add to main list */ + + for( ; -1 != pointer; pointer= list-> ptrs[ pointer ] ) + /* loop over all the ones found in this sequence */ + + if( list-> vals[ pointer ] < master_list-> vals[ master_list-> start ] ) + add_to_minpos_list( master_list, list-> xs[ pointer ], list-> ys[ pointer ], list-> vals[ pointer ] ); + else + break; + /* since we are now high->low, if this one isn't big enough, none of the rest are */ + + maxpos_list_free( list ); + + return 0; +} + diff --git a/libsrc/arithmetic/im_measure.c b/libsrc/arithmetic/im_measure.c new file mode 100644 index 00000000..31d53fda --- /dev/null +++ b/libsrc/arithmetic/im_measure.c @@ -0,0 +1,210 @@ +/* Analyse a grid of colour patches, producing a DOUBLEMASK of averages. + * Pass an IMAGE, an IMAGE_BOX, the number of horizontal and vertical + * patches, an array giving the numbers of the patches to measure (patches are + * numbered left-to-right, top-to-bottom) and the name we should give the + * output mask. Return a DOUBLEMASK in which rows are patches and columns are + * bands. + * + * Example: 6 band image of 4x2 block of colour patches. + * + * +---+---+---+---+ + * | 1 | 2 | 3 | 4 | + * +---+---+---+---+ + * | 5 | 6 | 7 | 8 | + * +---+---+---+---+ + * + * Then call im_measure( im, box, 4, 2, { 2, 4 }, 2, "fred" ) makes a mask + * "fred" which has 6 columns, two rows. The first row contains the averages + * for patch 2, the second for patch 4. + * + * Modified: + * 19/8/94 JC + * - now uses doubles for addressing + * - could miss by up to h pixels previously! + * - ANSIfied + * - now issues warning if any deviations are greater than 20% of the + * mean + * 31/10/95 JC + * - more careful about warning for averages <0, or averages near zero + * - can get these cases with im_measure() of IM_TYPE_LAB images + * 28/10/02 JC + * - number bands from zero in error messages + * 7/7/04 + * - works on labq + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Measure into array. + */ +static int +measure_patches( IMAGE *im, double *coeff, IMAGE_BOX *box, + int h, int v, int *sel, int nsel ) +{ + IMAGE *tmp; + int patch; + IMAGE_BOX sub; + int i, j; + int m, n; + double avg, dev; + + /* How large are the patches we are to measure? + */ + double pw = (double) box->xsize / (double) h; + double ph = (double) box->ysize / (double) v; + + /* Set up sub to be the size we need for a patch. + */ + sub.xsize = (pw + 1) / 2; + sub.ysize = (ph + 1) / 2; + + /* Loop through sel, picking out areas to measure. + */ + for( j = 0, patch = 0; patch < nsel; patch++ ) { + /* Sanity check. Is the patch number sensible? + */ + if( sel[patch] <= 0 || sel[patch] > h*v ) { + im_error( "im_measure", + _( "patch %d is out of range" ), + sel[patch] ); + return( 1 ); + } + + /* Patch coordinates. + */ + m = (sel[patch] - 1) % h; + n = (sel[patch] - 1) / h; + + /* Move sub to correct position. + */ + sub.xstart = box->xstart + m*pw + (pw + 2)/4; + sub.ystart = box->ystart + n*ph + (ph + 2)/4; + + /* Loop through bands. + */ + for( i = 0; i < im->Bands; i++, j++ ) { + /* Make temp buffer to extract to. + */ + if( !(tmp = im_open( "patch", "t" )) ) + return( -1 ); + + /* Extract and measure. + */ + sub.chsel = i; + if( im_extract( im, tmp, &sub ) || + im_avg( tmp, &avg ) || + im_deviate( tmp, &dev ) ) { + im_close( tmp ); + return( -1 ); + } + im_close( tmp ); + + /* Is the deviation large compared with the average? + * This could be a clue that our parameters have + * caused us to miss the patch. Look out for averages + * <0, or averages near zero (can get these if use + * im_measure() on IM_TYPE_LAB images). + */ + if( dev*5 > fabs( avg ) && fabs( avg ) > 3 ) + im_warn( "im_measure", + _( "patch %d, band %d: " + "avg = %g, sdev = %g" ), + patch, i, avg, dev ); + + /* Save results. + */ + coeff[j] = avg; + } + } + + return( 0 ); +} + +/* Measure up image. + */ +DOUBLEMASK * +im_measure( IMAGE *im, IMAGE_BOX *box, int h, int v, + int *sel, int nsel, const char *name ) +{ + DOUBLEMASK *mask; + + /* Check input image. + */ + if( im->Coding == IM_CODING_LABQ ) { + IMAGE *t1; + + if( !(t1 = im_open( "measure-temp", "p" )) ) + return( NULL ); + if( im_LabQ2Lab( im, t1 ) || + !(mask = im_measure( t1, + box, h, v, sel, nsel, name )) ) { + im_close( t1 ); + return( NULL ); + } + + im_close( t1 ); + + return( mask ); + } + + if( im->Coding != IM_CODING_NONE ) { + im_error( "im_measure", _( "not uncoded" ) ); + return( NULL ); + } + if( im_iscomplex( im ) ) { + im_error( "im_measure", _( "bad input type" ) ); + return( NULL ); + } + + /* What size mask do we need? + */ + if( !(mask = im_create_dmask( name, im->Bands, nsel )) ) + return( NULL ); + + /* Perform measure and return. + */ + if( measure_patches( im, mask->coeff, box, h, v, sel, nsel ) ) { + im_free_dmask( mask ); + return( NULL ); + } + + return( mask ); +} diff --git a/libsrc/arithmetic/im_min.c b/libsrc/arithmetic/im_min.c new file mode 100644 index 00000000..c63cf92e --- /dev/null +++ b/libsrc/arithmetic/im_min.c @@ -0,0 +1,241 @@ +/* @(#) Function to find the minimum of an image. Works for any + * @(#) image type. Returns a double. + * @(#) + * @(#) int im_min(in, min) + * @(#) IMAGE *in; + * @(#) double *min; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/05/1990 + * Modified on : 18/03/1991, N. Dessipris + * 7/7/93 JC + * - complex case fixed + * - im_incheck() call added + * 20/6/95 JC + * - now returns double + * - modernised a little + * - now returns min square amplitude rather than amplitude for complex + * 9/5/02 JC + * - partialed, based in im_max() + * 3/4/02 JC + * - random wrong result for >1 thread :-( (thanks Joe) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Per-call state. + */ +typedef struct _MinInfo { + /* Parameters. + */ + IMAGE *in; + double *out; + + /* Global min so far. + */ + double value; + int valid; /* zero means value is unset */ +} MinInfo; + +/* Per thread state. + */ +typedef struct _Seq { + MinInfo *inf; + + double value; + int valid; /* zero means value is unset */ +} Seq; + +/* New sequence value. + */ +static void * +start_fn( MinInfo *inf ) +{ + Seq *seq = IM_NEW( NULL, Seq ); + + seq->inf = inf; + seq->valid = 0; + + return( (void *) seq ); +} + +/* Merge the sequence value back into the per-call state. + */ +static int +stop_fn( Seq *seq, MinInfo *inf ) +{ + if( seq->valid ) { + if( !inf->valid ) + /* Just copy. + */ + inf->value = seq->value; + else + /* Merge. + */ + inf->value = IM_MIN( inf->value, seq->value ); + + inf->valid = 1; + } + + im_free( seq ); + + return( 0 ); +} + +/* Loop over region, adding to seq. + */ +static int +scan_fn( REGION *reg, Seq *seq ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int nel = IM_REGION_N_ELEMENTS( reg ); + + int x, y; + + double m; + +#ifdef DEBUG + printf( "im_min: left = %d, top = %d, width = %d, height = %d\n", + r->left, r->top, r->width, r->height ); +#endif /*DEBUG*/ + +#define loop(TYPE) { \ + m = *((TYPE *) IM_REGION_ADDR( reg, le, to )); \ + \ + for( y = to; y < bo; y++ ) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < nel; x++ ) { \ + double v = p[x]; \ + \ + if( v < m ) \ + m = v; \ + } \ + } \ +} + +#define complex_loop(TYPE) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, to ); \ + double real = p[0]; \ + double imag = p[1]; \ + \ + m = real * real + imag * imag; \ + \ + for( y = to; y < bo; y++ ) { \ + TYPE *p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( x = 0; x < nel * 2; x += 2 ) { \ + double mod; \ + \ + real = p[x]; \ + imag = p[x + 1]; \ + mod = real * real + imag * imag; \ + \ + if( mod < m ) \ + m = mod; \ + } \ + } \ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char ); break; + case IM_BANDFMT_CHAR: loop( signed char ); break; + case IM_BANDFMT_USHORT: loop( unsigned short ); break; + case IM_BANDFMT_SHORT: loop( signed short ); break; + case IM_BANDFMT_UINT: loop( unsigned int ); break; + case IM_BANDFMT_INT: loop( signed int ); break; + case IM_BANDFMT_FLOAT: loop( float ); break; + case IM_BANDFMT_DOUBLE: loop( double ); break; + case IM_BANDFMT_COMPLEX: complex_loop( float ); break; + case IM_BANDFMT_DPCOMPLEX: complex_loop( double ); break; + + default: + assert( 0 ); + } + + if( seq->valid ) { + seq->value = IM_MIN( seq->value, m ); + } + else { + seq->value = m; + seq->valid = 1; + } + + return( 0 ); +} + +int +im_min( IMAGE *in, double *out ) +{ + MinInfo inf; + + inf.in = in; + inf.out = out; + inf.valid = 0; + + if( im_pincheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_min", _( "not uncoded" ) ); + return( -1 ); + } + + if( im_iterate( in, start_fn, scan_fn, stop_fn, &inf, NULL ) ) + return( -1 ); + + *out = inf.value; + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_minpos.c b/libsrc/arithmetic/im_minpos.c new file mode 100644 index 00000000..98a1e966 --- /dev/null +++ b/libsrc/arithmetic/im_minpos.c @@ -0,0 +1,143 @@ +/* @(#) Function to find the minimim of an image. Works for any + * @(#) image type. Returns a double and the location of min + * @(#) + * @(#) Function im_minpos() assumes that input + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) int im_minpos(in, xpos, ypos, out) + * @(#) IMAGE *in; + * @(#) int *xpos, *ypos; + * @(#) double *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/05/1990 + * Modified on : 18/03/1991, N. Dessipris + * 23/11/92 JC + * - correct result for more than 1 band now. + * 23/7/93 JC + * - im_incheck() added + * 20/6/95 JC + * - now returns double for value, like im_max() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Find the minimum of an image. Take any format, returns a double. */ +int +im_minpos( IMAGE *in, int *xpos, int *ypos, double *out ) +{ + double m; + int xp=0, yp=0; + int os; + +/* Check our args. */ + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) + { + im_error("im_minpos", _("input must be uncoded")); + return( -1 ); + } + +/* What type? First define the loop we want to perform for all types. */ +#define loop(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y; \ + m = (double) *p; \ + \ + for ( y=0; yYsize; y++ ) \ + for ( x=0; xdata; \ + double re=(double)*p;\ + double im=(double)*(p+1);\ + double mod = re * re + im * im;\ + int x, y; \ + m = mod; \ + \ + for ( y=0; yYsize; y++ ) \ + for ( x=0; xXsize * in->Bands; + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: loop(unsigned char); break; + case IM_BANDFMT_CHAR: loop(signed char); break; + case IM_BANDFMT_USHORT: loop(unsigned short); break; + case IM_BANDFMT_SHORT: loop(signed short); break; + case IM_BANDFMT_UINT: loop(unsigned int); break; + case IM_BANDFMT_INT: loop(signed int); break; + case IM_BANDFMT_FLOAT: loop(float); break; + case IM_BANDFMT_DOUBLE: loop(double); break; + case IM_BANDFMT_COMPLEX: loopcmplx(float); break; + case IM_BANDFMT_DPCOMPLEX: loopcmplx(double); break; + + default: + assert( 0 ); + } + + /* Take out bands on x. + */ + *out = m; + *xpos = xp / in->Bands; + *ypos = yp; + return( 0 ); +} diff --git a/libsrc/arithmetic/im_multiply.c b/libsrc/arithmetic/im_multiply.c new file mode 100644 index 00000000..5747aa18 --- /dev/null +++ b/libsrc/arithmetic/im_multiply.c @@ -0,0 +1,261 @@ +/* @(#) Multiply two images + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int + * @(#) im_multiply(in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 29/4/93 JC + * - now works for partial images + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 19/10/93 JC + * - coredump-inducing bug in complex*complex fixed + * 13/12/93 + * - char*short bug fixed + * 12/6/95 JC + * - new im_add adapted to make new im_multiply + * 27/9/04 + * - updated for 1 band $op n band image -> n band image case + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Swap two IMAGE pointers. + */ +#define SWAP(A,B) { \ + IMAGE *t; \ + t = (A); (A) = (B); (B) = t; \ +} + +/* Complex multiply. + */ +#define cloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double x1 = p1[0];\ + double y1 = p1[1];\ + double x2 = p2[0];\ + double y2 = p2[1];\ + \ + p1 += 2;\ + p2 += 2;\ + \ + q[0] = x1 * x2 - y1 * y2;\ + q[1] = x1 * y2 + x2 * y1;\ + \ + q += 2;\ + }\ +} + +/* Real multiply. + */ +#define rloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p1[x] * p2[x];\ +} + +static void +multiply_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Multiply all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: rloop( signed char ); break; + case IM_BANDFMT_UCHAR: rloop( unsigned char ); break; + case IM_BANDFMT_SHORT: rloop( signed short ); break; + case IM_BANDFMT_USHORT: rloop( unsigned short ); break; + case IM_BANDFMT_INT: rloop( signed int ); break; + case IM_BANDFMT_UINT: rloop( unsigned int ); break; + + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_multiply_f32( (float *) out, + (float *) in[0], (float *) in[1], sz ); +#else /*!HAVE_LIBOIL*/ + rloop( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: rloop( double ); break; + case IM_BANDFMT_COMPLEX: cloop( float ); break; + case IM_BANDFMT_DPCOMPLEX: cloop( double ); break; + + default: + assert( 0 ); + } +} + +/* Save a bit of typing. + */ +#define UC IM_BANDFMT_UCHAR +#define C IM_BANDFMT_CHAR +#define US IM_BANDFMT_USHORT +#define S IM_BANDFMT_SHORT +#define UI IM_BANDFMT_UINT +#define I IM_BANDFMT_INT +#define F IM_BANDFMT_FLOAT +#define M IM_BANDFMT_COMPLEX +#define D IM_BANDFMT_DOUBLE +#define DM IM_BANDFMT_DPCOMPLEX + +/* Type conversions for two integer inputs. Rules for float and complex + * encoded with ifs. We are sign and value preserving. + */ +static int iformat[6][6] = { + /* UC C US S UI I */ +/* UC */ { US, S, UI, I, UI, I }, +/* C */ { S, S, I, I, I, I }, +/* US */ { UI, I, UI, I, UI, I }, +/* S */ { I, I, I, I, I, I }, +/* UI */ { UI, I, UI, I, UI, I }, +/* I */ { I, I, I, I, I, I } +}; + +int +im_multiply( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + + if( in1->Xsize != in2->Xsize || in1->Ysize != in2->Ysize ) { + im_error( "im_multiply", _( "not same size" ) ); + return( -1 ); + } + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_multiply", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_multiply", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* Swap arguments to get the largest on the left. + */ + if( in1->Bbits < in2->Bbits ) + SWAP( in1, in2 ); + + /* What output type will we write? int, float or complex. + */ + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + /* Make sure we have complex on the left. + */ + if( !im_iscomplex( in1 ) ) + SWAP( in1, in2 ); + + /* What kind of complex? + */ + if( in1->BandFmt == IM_BANDFMT_DPCOMPLEX ) + /* Output will be DPCOMPLEX. + */ + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + else + out->BandFmt = IM_BANDFMT_COMPLEX; + } + else if( im_isfloat( in1 ) || im_isfloat( in2 ) ) { + /* Make sure we have float on the left. + */ + if( !im_isfloat( in1 ) ) + SWAP( in1, in2 ); + + /* What kind of float? + */ + if( in1->BandFmt == IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_DOUBLE; + else + out->BandFmt = IM_BANDFMT_FLOAT; + } + else + /* Must be int+int = int. + */ + out->BandFmt = iformat[in2->BandFmt][in1->BandFmt]; + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) multiply_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} diff --git a/libsrc/arithmetic/im_point_bilinear.c b/libsrc/arithmetic/im_point_bilinear.c new file mode 100644 index 00000000..b55d2bad --- /dev/null +++ b/libsrc/arithmetic/im_point_bilinear.c @@ -0,0 +1,127 @@ +/* @(#) Find the value at (x,y) in given band of image. + * @(#) Use bilinear interpolation if x or y are non-integral. + * @(#) + * @(#) int im_maxpos_vec( + * @(#) IMAGE *im, + * @(#) double x, + * @(#) double y, + * @(#) int band, + * @(#) double *val + * @(#) ); + * @(#) + * + * Copyright: 2006, The Nottingham Trent University + * + * Author: Tom Vajzovic + * + * Written on: 2006-09-26 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** EXPORTED FUNCTION **/ + +int im_point_bilinear( IMAGE *im, double x, double y, int band, double *val ){ +#define FUNCTION_NAME "im_point_bilinear" + + double x_frac= x - (int) x; + double y_frac= y - (int) y; + Rect need= { x, y, ( x_frac ? 2 : 1 ), ( y_frac ? 2 : 1 ) }; + REGION *reg; + + if( im_pincheck( im ) ) + return -1; + + if( im-> Coding ){ + im_error( FUNCTION_NAME, _("uncoded images only") ); + return -1; + } + if( !( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, _("scalar images only") ); + return -1; + } + if( band >= im-> Bands || x < 0.0 || y < 0.0 || x > im-> Xsize || y > im-> Ysize ){ + im_error( FUNCTION_NAME, _("coords outside image") ); + return -1; + } + if( ! val ){ + im_error( FUNCTION_NAME, _("invalid arguments") ); + return -1; + } + + reg= im_region_create( im ); + + if( ! reg || im_prepare( reg, &need ) ) + return -1; + + if( ! im_rect_includesrect( ®-> valid, &need ) ){ + im_error( FUNCTION_NAME, _("coords outside image") ); + im_region_free( reg ); + return -1; + } + + if( x_frac ) + if( y_frac ) + *val= x_frac * y_frac * IM_REGION_VALUE( reg, ((int)x + 1), ((int)y + 1), band ) + + x_frac * ( 1.0 - y_frac ) * IM_REGION_VALUE( reg, ((int)x + 1), (int)y , band ) + + ( 1.0 - x_frac ) * y_frac * IM_REGION_VALUE( reg, (int)x, ((int)y + 1), band ) + + ( 1.0 - x_frac ) * ( 1.0 - y_frac ) * IM_REGION_VALUE( reg, (int)x, (int)y , band ); + + else + *val= x_frac * IM_REGION_VALUE( reg, ((int)x + 1), (int)y, band ) + + ( 1.0 - x_frac ) * IM_REGION_VALUE( reg, (int)x, (int)y, band ); + + else + if( y_frac ) + *val= y_frac * IM_REGION_VALUE( reg, (int)x, ((int)y + 1), band ) + + ( 1.0 - y_frac ) * IM_REGION_VALUE( reg, (int)x, (int)y , band ); + + else + *val= IM_REGION_VALUE( reg, (int)x, (int)y, band ); + + im_region_free( reg ); + + return 0; + +#undef FUNCTION_NAME +} + diff --git a/libsrc/arithmetic/im_powtra.c b/libsrc/arithmetic/im_powtra.c new file mode 100644 index 00000000..80e7d368 --- /dev/null +++ b/libsrc/arithmetic/im_powtra.c @@ -0,0 +1,235 @@ +/* @(#) Finds the power of an image + * @(#) Function im_powtra() assumes that the imin file + * @(#) is either memory mapped or in a buffer. + * @(#) Output image pow(imin, exponent) depends on input + * @(#) If input is up to float, output is float + * @(#) else input is the same as output + * @(#) Works on any number of bands. + * @(#) + * @(#) int im_powtra( in, out, e ) + * @(#) IMAGE *in, *out; + * @(#) double e; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 10/12/93 JC + * - now reports total number of x/0, rather than each one. + * 1/2/95 JC + * - rewritten for PIO with im_wrapone() + * - incorrect complex code removed + * - /0 reporting removed for ease of programming + * 15/4/97 JC + * - return( 0 ) missing, oops! + * 6/7/98 JC + * - _vec form added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Parameters saved here. + */ +typedef struct { + int n; + double *e; /* Exponent value */ +} PowtraInfo; + +/* Define what we do for each band element type. Single constant. + */ +#define loop1(IN, OUT)\ +{\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double f = (double) p[x];\ + \ + if( f == 0.0 && e < 0.0 ) {\ + /* Division by zero! Difficult to report tho'\ + */\ + q[x] = 0.0;\ + }\ + else\ + q[x] = pow( f, e );\ + }\ +} + +/* Powtra a buffer. + */ +static int +powtra1_gen( PEL *in, PEL *out, int width, IMAGE *im, PowtraInfo *inf ) +{ + int sz = width * im->Bands; + double e = inf->e[0]; + int x; + + /* Powtra all non-complex input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop1(unsigned char, float); break; + case IM_BANDFMT_CHAR: loop1(signed char, float); break; + case IM_BANDFMT_USHORT: loop1(unsigned short, float); break; + case IM_BANDFMT_SHORT: loop1(signed short, float); break; + case IM_BANDFMT_UINT: loop1(unsigned int, float); break; + case IM_BANDFMT_INT: loop1(signed int, float); break; + case IM_BANDFMT_FLOAT: loop1(float, float); break; + case IM_BANDFMT_DOUBLE: loop1(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Define what we do for each band element type. One constant per band. + */ +#define loopn(IN, OUT)\ +{\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( i = 0, x = 0; x < width; x++ )\ + for( k = 0; k < im->Bands; k++, i++ ) {\ + double e = inf->e[k];\ + double f = (double) p[i];\ + \ + if( f == 0.0 && e < 0.0 ) {\ + q[i] = 0.0;\ + }\ + else\ + q[i] = pow( f, e );\ + }\ +} + +/* Powtra a buffer. + */ +static int +powtran_gen( PEL *in, PEL *out, int width, IMAGE *im, PowtraInfo *inf ) +{ + int x, k, i; + + /* Powtra all non-complex input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loopn(unsigned char, float); break; + case IM_BANDFMT_CHAR: loopn(signed char, float); break; + case IM_BANDFMT_USHORT: loopn(unsigned short, float); break; + case IM_BANDFMT_SHORT: loopn(signed short, float); break; + case IM_BANDFMT_UINT: loopn(unsigned int, float); break; + case IM_BANDFMT_INT: loopn(signed int, float); break; + case IM_BANDFMT_FLOAT: loopn(float, float); break; + case IM_BANDFMT_DOUBLE: loopn(double, double); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +int +im_powtra_vec( IMAGE *in, IMAGE *out, int n, double *e ) +{ + PowtraInfo *inf; + int i; + + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_powtra_vec", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_powtra_vec", _( "not non-complex" ) ); + return( -1 ); + } + if( n != 1 && n != in->Bands ) { + im_error( "im_powtra_vec", + _( "not 1 or %d elements in vector" ), in->Bands ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + + /* Make space for a little buffer. + */ + if( !(inf = IM_NEW( out, PowtraInfo )) || + !(inf->e = IM_ARRAY( out, n, double )) ) + return( -1 ); + for( i = 0; i < n; i++ ) + inf->e[i] = e[i]; + inf->n = n; + + /* Generate! + */ + if( n == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) powtra1_gen, in, inf ) ) + return( -1 ); + } + else { + if( im_wrapone( in, out, + (im_wrapone_fn) powtran_gen, in, inf ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_powtra( IMAGE *in, IMAGE *out, double e ) +{ + return( im_powtra_vec( in, out, 1, &e ) ); +} diff --git a/libsrc/arithmetic/im_remainder.c b/libsrc/arithmetic/im_remainder.c new file mode 100644 index 00000000..3491ec14 --- /dev/null +++ b/libsrc/arithmetic/im_remainder.c @@ -0,0 +1,278 @@ +/* @(#) Remainder after integer division + * + * 2/8/99 JC + * - im_divide adapted to make im_remainder + * 8/5/02 JC + * - im_remainderconst added + * - im_remainderconst_vec added + * 27/9/04 + * - updated for 1 band $op n band image -> n band image case + * 26/2/07 + * - oop, broken for _vec case :-( + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + if( p2[x] )\ + q[x] = p1[x] % p2[x];\ + else\ + q[x] = -1;\ +} + +static void +remainder_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: loop( signed char ); break; + case IM_BANDFMT_UCHAR: loop( unsigned char ); break; + case IM_BANDFMT_SHORT: loop( signed short ); break; + case IM_BANDFMT_USHORT: loop( unsigned short ); break; + case IM_BANDFMT_INT: loop( signed int ); break; + case IM_BANDFMT_UINT: loop( unsigned int ); break; + + default: + assert( 0 ); + } +} + +int +im_remainder( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + if( in1->Xsize != in2->Xsize || in1->Ysize != in2->Ysize ) { + im_error( "im_remainder", _( "not same size" ) ); + return( -1 ); + } + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_remainder", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_remainder", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* What output type will we write? Same as LHS type, except float + * and double become signed int. + */ + if( im_isfloat( in1 ) || im_iscomplex( in1 ) ) + out->BandFmt = IM_BANDFMT_INT; + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) remainder_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} + +/* Parameters saved here. + */ +typedef struct _Remainderconst { + IMAGE *in; + IMAGE *out; + int n; + int *c; +} Remainderconst; + +#define const1_loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + \ + for( x = 0; x < sz; x++ ) \ + q[x] = p[x] % c; \ +} + +static void +remainderconst1_buffer( PEL *in, PEL *out, int width, Remainderconst *rc ) +{ + IMAGE *im = rc->in; + int sz = width * im->Bands; + int c = rc->c[0]; + int x; + + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: const1_loop( signed char ); break; + case IM_BANDFMT_UCHAR: const1_loop( unsigned char ); break; + case IM_BANDFMT_SHORT: const1_loop( signed short ); break; + case IM_BANDFMT_USHORT: const1_loop( unsigned short ); break; + case IM_BANDFMT_INT: const1_loop( signed int ); break; + case IM_BANDFMT_UINT: const1_loop( unsigned int ); break; + + default: + assert( 0 ); + } +} + +#define const_loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + \ + for( i = 0, x = 0; x < width; x++ ) \ + for( k = 0; k < b; k++, i++ ) \ + q[i] = p[i] % c[k]; \ +} + +static void +remainderconst_buffer( PEL *in, PEL *out, int width, Remainderconst *rc ) +{ + IMAGE *im = rc->in; + int b = im->Bands; + int *c = rc->c; + int i, x, k; + + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: const_loop( signed char ); break; + case IM_BANDFMT_UCHAR: const_loop( unsigned char ); break; + case IM_BANDFMT_SHORT: const_loop( signed short ); break; + case IM_BANDFMT_USHORT: const_loop( unsigned short ); break; + case IM_BANDFMT_INT: const_loop( signed int ); break; + case IM_BANDFMT_UINT: const_loop( unsigned int ); break; + + default: + assert( 0 ); + } +} + +int +im_remainderconst_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + Remainderconst *rc; + int i; + + /* Basic checks. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_remainderconst_vec", _( "not uncoded" ) ); + return( -1 ); + } + if( n != 1 && n != in->Bands ) { + im_error( "im_remainderconst_vec", + _( "not 1 or %d elements in vector" ), in->Bands ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Make space for a little buffer. + */ + if( !(rc = IM_NEW( out, Remainderconst )) || + !(rc->c = IM_ARRAY( out, n, int )) ) + return( -1 ); + rc->in = in; + rc->out = out; + rc->n = n; + for( i = 0; i < n; i++ ) { + /* Cast down to int ... we pass in double for consistency with + * the other _vec functions. + */ + rc->c[i] = c[i]; + + if( c[i] == 0 ) { + im_error( "im_remainderconst_vec", + _( "division by zero" ) ); + return( -1 ); + } + } + + /* What output type will we write? Same as input type, except float + * and double become signed int. + */ + if( im_isfloat( in ) || im_iscomplex( in ) ) { + IMAGE *t = im_open_local( out, "im_remainderconst:1", "p" ); + + out->BandFmt = IM_BANDFMT_INT; + out->Bbits = IM_BBITS_INT; + if( !t || im_clip2fmt( in, t, out->BandFmt ) ) + return( -1 ); + + rc->in = in = t; + } + + if( n == 1 ) { + if( im_wrapone( in, out, + (im_wrapone_fn) remainderconst1_buffer, rc, NULL ) ) + return( -1 ); + } + else { + if( im_wrapone( in, out, + (im_wrapone_fn) remainderconst_buffer, rc, NULL ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_remainderconst( IMAGE *in, IMAGE *out, double c ) +{ + return( im_remainderconst_vec( in, out, 1, &c ) ); +} diff --git a/libsrc/arithmetic/im_rint.c b/libsrc/arithmetic/im_rint.c new file mode 100644 index 00000000..da628948 --- /dev/null +++ b/libsrc/arithmetic/im_rint.c @@ -0,0 +1,114 @@ +/* @(#) rint() an image ... no promotion, so output type == input type + * @(#) + * @(#) int + * @(#) im_rint( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 20/6/02 JC + * - adapted from im_floor() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define rint_loop(TYPE)\ + {\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = IM_RINT( p[x] );\ + } + +/* rint a buffer of PELs. + */ +static void +rint_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_FLOAT: rint_loop(float); break; + case IM_BANDFMT_DOUBLE: rint_loop(double); break; + case IM_BANDFMT_COMPLEX: sz *= 2; rint_loop(float); break; + case IM_BANDFMT_DPCOMPLEX: sz *= 2; rint_loop(double); break; + + default: + assert( 0 ); + } +} + +/* rint of image. + */ +int +im_rint( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_rint", _( "not uncoded" ) ); + return( -1 ); + } + + /* Is this one of the int types? Degenerate to im_copy() if it + * is. + */ + if( im_isint( in ) ) + return( im_copy( in, out ) ); + + /* Output type == input type. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Generate! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) rint_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_sign.c b/libsrc/arithmetic/im_sign.c new file mode 100644 index 00000000..29aa206b --- /dev/null +++ b/libsrc/arithmetic/im_sign.c @@ -0,0 +1,162 @@ +/* @(#) Find the unit vector in the direction of the pixel. + * @(#) + * @(#) int im_sign(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 9/7/02 JC + * - from im_cmulnorm + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define sign_complex( IN, OUT ) \ +{ \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + int x; \ + \ + for( x = 0; x < n; x++ ) { \ + IN re = p[0]; \ + IN im = p[1]; \ + double fac = sqrt( re * re + im * im ); \ + \ + p += 2; \ + \ + if( fac == 0.0 ) { \ + q[0] = 0.0; \ + q[1] = 0.0; \ + } \ + else { \ + q[0] = re / fac; \ + q[1] = im / fac; \ + } \ + \ + q += 2; \ + } \ +} + +#define sign( IN, OUT ) \ +{ \ + IN *p = (IN *) in; \ + OUT *q = (OUT *) out; \ + int x; \ + \ + for( x = 0; x < n; x++ ) { \ + IN v = p[x]; \ + \ + if( v > 0 ) \ + q[x] = 1; \ + else if( v == 0 ) \ + q[x] = 0; \ + else \ + q[x] = -1; \ + } \ +} + +/* sign buffer processor. + */ +static void +buffer_sign( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + sign( unsigned char, signed char ); + break; + case IM_BANDFMT_CHAR: + sign( signed char, signed char ); + break; + case IM_BANDFMT_USHORT: + sign( unsigned short, signed char ); + break; + case IM_BANDFMT_SHORT: + sign( signed short, signed char ); + break; + case IM_BANDFMT_UINT: + sign( unsigned int, signed char ); + break; + case IM_BANDFMT_INT: + sign( signed int, signed char ); + break; + case IM_BANDFMT_FLOAT: + sign( float, signed char ); + break; + case IM_BANDFMT_DOUBLE: + sign( double, signed char ); + break; + case IM_BANDFMT_COMPLEX: + sign_complex( float, float ); + break; + case IM_BANDFMT_DPCOMPLEX: + sign_complex( double, double ); + break; + default: + assert( 0 ); + } +} + +int +im_sign( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_sign", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + if( !im_iscomplex( in ) ) { + out->Bbits = IM_BBITS_BYTE; + out->BandFmt = IM_BANDFMT_CHAR; + } + + /* Do the processing. + */ + if( im_wrapone( in, out, (im_wrapone_fn) buffer_sign, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_sintra.c b/libsrc/arithmetic/im_sintra.c new file mode 100644 index 00000000..2024e5f4 --- /dev/null +++ b/libsrc/arithmetic/im_sintra.c @@ -0,0 +1,239 @@ +/* @(#) Find sin of any non-complex image. Output is always float for integer + * @(#) input and double for double input. All angles in degrees. + * @(#) + * @(#) int + * @(#) im_sintra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 24/2/95 JC + * - im_logtra() adapted to make im_sintra() + * - adapted for im_wrapone() + * 26/1/96 JC + * - im_asintra() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = sin( IM_RAD( (double) p[x] ) );\ + } + +/* sin a buffer of PELs. + */ +static void +sintra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: loop( signed char, float ); break; + case IM_BANDFMT_USHORT: loop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: loop( signed short, float ); break; + case IM_BANDFMT_UINT: loop( unsigned int, float ); break; + case IM_BANDFMT_INT: loop( signed int, float ); break; + case IM_BANDFMT_FLOAT: loop( float, float ); break; + case IM_BANDFMT_DOUBLE: loop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Sin transform. + */ +int +im_sintra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_sintra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_sintra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) sintra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* And asin(). + */ +#define aloop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = IM_DEG( asin( (double) p[x] ) );\ + } + +/* asin a buffer of PELs. + */ +static void +asintra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: aloop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: aloop( signed char, float ); break; + case IM_BANDFMT_USHORT: aloop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: aloop( signed short, float ); break; + case IM_BANDFMT_UINT: aloop( unsigned int, float ); break; + case IM_BANDFMT_INT: aloop( signed int, float ); break; + case IM_BANDFMT_FLOAT: aloop( float, float ); break; + case IM_BANDFMT_DOUBLE: aloop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Asin transform. + */ +int +im_asintra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_asintra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_asintra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) asintra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/im_stats.c b/libsrc/arithmetic/im_stats.c new file mode 100644 index 00000000..6c2a1266 --- /dev/null +++ b/libsrc/arithmetic/im_stats.c @@ -0,0 +1,277 @@ +/* @(#) im_stats: find general image statistics for all bands separately +(C) Kirk Martinez 1993 +23/4/93 J.Cupitt + - adapted to partial images + - special struct abandoned; now returns DOUBLEMASK. +1/7/93 JC + - adapted for partial v2 + - ANSIfied +27/7/93 JC + - init of mask changed to use actual values, not IM_MAXDOUBLE and + (-IM_MAXDOUBLE). These caused problems when assigned to floats. + funny business with offset==42, yuk! +31/8/93 JC + - forgot to init global max/min properly! sorry. +21/6/95 JC + - still did not init max and min correctly --- now fixed for good + + * 13/1/05 + * - use 64 bit arithmetic + +*/ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Make and initialise a DOUBLEMASK suitable for grabbing statistics. + */ +static void * +make_mask( IMAGE *im ) +{ + DOUBLEMASK *out; + + /* Make temp output. + */ + if( !(out = im_create_dmask( "stats", 6, im->Bands + 1 )) ) + return( NULL ); + + /* Set offset to magic value: 42 indicates we have not yet initialised + * max and min for this mask. + */ + out->offset = 42; + + return( (void *) out ); +} + +/* Merge a temp DOUBLEMASK into the real DOUBLEMASK. Row 0 is unused, row 1 + * has the stats for band 1. These are: (minimum, maximum, sum, sum^2). If the + * offset of out if 42, then it has not been inited yet and we just copy. + */ +static int +merge_mask( DOUBLEMASK *tmp, DOUBLEMASK *out ) +{ + double *rowi, *rowo; + int z; + + /* Merge, or just copy? + */ + if( out->offset != 42 ) + /* Add info from tmp. + */ + for( z = 1; z < tmp->ysize; z++ ) { + rowi = tmp->coeff + z * 6; + rowo = out->coeff + z * 6; + + rowo[0] = IM_MIN( rowi[0], rowo[0] ); + rowo[1] = IM_MAX( rowi[1], rowo[1] ); + rowo[2] += rowi[2]; + rowo[3] += rowi[3]; + } + else { + /* Copy info from tmp. + */ + for( z = 1; z < tmp->ysize; z++ ) { + rowi = tmp->coeff + z * 6; + rowo = out->coeff + z * 6; + + rowo[0] = rowi[0]; + rowo[1] = rowi[1]; + rowo[2] = rowi[2]; + rowo[3] = rowi[3]; + } + + out->offset = 0; + } + + /* Can now free tmp. + */ + im_free_dmask( tmp ); + + return( 0 ); +} + +/* Loop over region, adding information to the appropriate fields of tmp. + * We set max, min, sum, sum of squares. Our caller fills in the rest. + */ +static int +scan_fn( REGION *reg, DOUBLEMASK *tmp ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int bands = im->Bands; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int x, y, z; + +/* What type? First define the loop we want to perform for all types. + * We scan lines bands times to avoid repeating band loops. + * Use temp variables of same type for min/max for faster comparisons. + * Use double to sum bands. + */ +#define non_complex_loop(TYPE) \ + { TYPE *p, *q; \ + TYPE value, small, big; \ + double *row; \ + \ + /* Have min and max been initialised? \ + */ \ + if( tmp->offset == 42 ) { \ + /* Init min and max for each band. \ + */ \ + p = (TYPE *) IM_REGION_ADDR( reg, le, to ); \ + for( z = 1; z < bands + 1; z++ ) { \ + row = tmp->coeff + z * 6; \ + row[0] = p[z - 1]; \ + row[1] = p[z - 1]; \ + } \ + tmp->offset = 0; \ + } \ + \ + for( y = to; y < bo; y++ ) { \ + p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ + \ + for( z = 0; z < bands; z++ ) { \ + q = p + z; \ + row = tmp->coeff + (z + 1)*6; \ + small = row[0]; \ + big = row[1]; \ + \ + for( x = le; x < ri; x++ ) { \ + value = *q; \ + q += bands; \ + row[2] += value;\ + row[3] += (double)value*(double)value;\ + if( value > big ) \ + big = value; \ + else if( value < small ) \ + small = value;\ + }\ + \ + row[0] = small; \ + row[1] = big; \ + }\ + }\ + } + + /* Now generate code for all types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: non_complex_loop(unsigned char); break; + case IM_BANDFMT_CHAR: non_complex_loop(signed char); break; + case IM_BANDFMT_USHORT: non_complex_loop(unsigned short); break; + case IM_BANDFMT_SHORT: non_complex_loop(signed short); break; + case IM_BANDFMT_UINT: non_complex_loop(unsigned int); break; + case IM_BANDFMT_INT: non_complex_loop(signed int); break; + case IM_BANDFMT_DOUBLE: non_complex_loop(double); break; + case IM_BANDFMT_FLOAT: non_complex_loop(float); break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +/* Find the statistics of an image. Take any non-complex format. Write the + * stats to a DOUBLEMASK of size 6 by (in->Bands+1). We hold a row for each + * band, plus one row for all bands. Row n has 6 elements, which are, in + * order, (minimum, maximum, sum, sum^2, mean, deviation) for band n. Row 0 has + * the figures for all bands together. + */ +DOUBLEMASK * +im_stats( IMAGE *in ) +{ + DOUBLEMASK *out; + double *row, *base; + gint64 pels, vals, z; + + /* Check our args. + */ + if( im_pincheck( in ) ) + return( NULL ); + if( im_iscomplex( in ) ) { + im_error( "im_stats", _( "bad input type" ) ); + return( NULL ); + } + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_stats", _( "not uncoded" ) ); + return( NULL ); + } + + /* Make output. + */ + pels = (gint64) in->Xsize * in->Ysize; + vals = pels * in->Bands; + if( !(out = make_mask( in )) ) + return( NULL ); + + /* Loop over input, calculating min, max, sum, sum^2 for each band + * separately. + */ + if( im_iterate( in, make_mask, scan_fn, merge_mask, out, NULL ) ) { + im_free_dmask( out ); + return( NULL ); + } + + /* Calculate mean, deviation, plus overall stats. + */ + base = out->coeff; + base[0] = base[6]; /* Init global max/min */ + base[1] = base[7]; + for( z = 0; z < in->Bands; z++ ) { + row = base + (z + 1) * 6; + base[0] = IM_MIN( base[0], row[0] ); + base[1] = IM_MAX( base[1], row[1] ); + base[2] += row[2]; + base[3] += row[3]; + row[4] = row[2] / pels; + row[5] = sqrt( fabs( row[3] - (row[2] * row[2] / pels) ) / + (pels - 1) ); + } + base[4] = base[2] / vals; + base[5] = sqrt( fabs( base[3] - (base[2] * base[2] / vals) ) / + (vals - 1) ); + + return( out ); +} diff --git a/libsrc/arithmetic/im_subtract.c b/libsrc/arithmetic/im_subtract.c new file mode 100644 index 00000000..82e329c7 --- /dev/null +++ b/libsrc/arithmetic/im_subtract.c @@ -0,0 +1,232 @@ +/* @(#) Subtract two images + * @(#) Images must have the same no of bands and can be of any type + * @(#) No check for overflow is carried out. + * @(#) + * @(#) int + * @(#) im_subtract( in1, in2, out) + * @(#) IMAGE *in1, *in2, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 29/4/93 J.Cupitt + * - now works for partial images + * 1/7/93 JC + * - adapted for partial v2 + * 9/5/95 JC + * - simplified: now just handles 10 cases (instead of 50), using + * im_clip2*() to help + * - now uses im_wrapmany() rather than im_generate() + * 12/6/95 JC + * - new im_add() adapted to make this + * 31/5/96 JC + * - what was this SWAP() stuff? failed for small - big! + * 22/8/03 JC + * - cast up more quickly to help accuracy + * 27/9/04 + * - updated for 1 band $op n band image -> n band image case + * 8/12/06 + * - add liboil support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Complex subtract. + */ +#define cloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ ) {\ + double rp1 = p1[0];\ + double ip1 = p1[1];\ + \ + double rp2 = p2[0];\ + double ip2 = p2[1];\ + \ + p1 += 2;\ + p2 += 2;\ + \ + q[0] = rp1 - rp2;\ + q[1] = ip1 - ip2;\ + \ + q += 2;\ + }\ +} + +/* Real subtract. + */ +#define rloop(TYPE) \ +{\ + TYPE *p1 = (TYPE *) in[0];\ + TYPE *p2 = (TYPE *) in[1];\ + TYPE *q = (TYPE *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p1[x] - p2[x];\ +} + +static void +subtract_buffer( PEL **in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Subtract all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: rloop( signed char ); break; + case IM_BANDFMT_UCHAR: rloop( unsigned char ); break; + case IM_BANDFMT_SHORT: rloop( signed short ); break; + case IM_BANDFMT_USHORT: rloop( unsigned short ); break; + case IM_BANDFMT_INT: rloop( signed int ); break; + case IM_BANDFMT_UINT: rloop( unsigned int ); break; + case IM_BANDFMT_FLOAT: +#ifdef HAVE_LIBOIL + oil_subtract_f32( (float *) out, + (float *) in[0], (float *) in[1], sz ); +#else /*!HAVE_LIBOIL*/ + rloop( float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: rloop( double ); break; + case IM_BANDFMT_COMPLEX: cloop( float ); break; + case IM_BANDFMT_DPCOMPLEX: cloop( double ); break; + + default: + assert( 0 ); + } +} + +/* Save a bit of typing. + */ +#define S IM_BANDFMT_SHORT +#define I IM_BANDFMT_INT +#define D IM_BANDFMT_DOUBLE + +/* Type conversions for two integer inputs. Rules for float and complex + * input encoded with ifs. We are sign and value preserving. + */ +static int iformat[6][6] = { + /* UC C US S UI I */ +/* UC */ { S, S, I, I, I, I }, +/* C */ { S, S, I, I, I, I }, +/* US */ { I, I, I, I, I, I }, +/* S */ { I, I, I, I, I, I }, +/* UI */ { I, I, I, I, I, I }, +/* I */ { I, I, I, I, I, I } +}; + +int +im_subtract( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + /* Basic checks. + */ + if( im_piocheck( in1, out ) || im_pincheck( in2 ) ) + return( -1 ); + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_subtract", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_subtract", _( "not uncoded" ) ); + return( -1 ); + } + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + + /* What number of bands will we write? + */ + out->Bands = IM_MAX( in1->Bands, in2->Bands ); + + /* What output type will we write? int, float or complex. + */ + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + /* What kind of complex? + */ + if( in1->BandFmt == IM_BANDFMT_DPCOMPLEX || + in2->BandFmt == IM_BANDFMT_DPCOMPLEX ) + /* Output will be DPCOMPLEX. + */ + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + else + out->BandFmt = IM_BANDFMT_COMPLEX; + } + else if( im_isfloat( in1 ) || im_isfloat( in2 ) ) { + /* What kind of float? + */ + if( in1->BandFmt == IM_BANDFMT_DOUBLE || + in2->BandFmt == IM_BANDFMT_DOUBLE ) + out->BandFmt = IM_BANDFMT_DOUBLE; + else + out->BandFmt = IM_BANDFMT_FLOAT; + } + else + /* Must be int+int -> int. + */ + out->BandFmt = iformat[in1->BandFmt][in2->BandFmt]; + + /* And process! + */ + if( im__cast_and_call( in1, in2, out, + (im_wrapmany_fn) subtract_buffer, NULL ) ) + return( -1 ); + + /* Success! + */ + return( 0 ); +} diff --git a/libsrc/arithmetic/im_tantra.c b/libsrc/arithmetic/im_tantra.c new file mode 100644 index 00000000..9cbd0801 --- /dev/null +++ b/libsrc/arithmetic/im_tantra.c @@ -0,0 +1,240 @@ +/* @(#) Find tan of any non-complex image. Output is always float for integer + * @(#) input and double for double input. All angles in degrees. + * @(#) + * @(#) int + * @(#) im_tantra( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 5/5/93 JC + * - adapted from im_lintra to work with partial images + * - incorrect implementation of complex logs removed + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 24/2/95 JC + * - im_logtra() adapted to make im_tantra() + * - adapted for im_wrapone() + * 26/1/96 JC + * - atan() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define loop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = tan( IM_RAD( (double) p[x] ) );\ + } + +/* tan a buffer of PELs. + */ +static void +tantra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: loop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: loop( signed char, float ); break; + case IM_BANDFMT_USHORT: loop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: loop( signed short, float ); break; + case IM_BANDFMT_UINT: loop( unsigned int, float ); break; + case IM_BANDFMT_INT: loop( signed int, float ); break; + case IM_BANDFMT_FLOAT: loop( float, float ); break; + case IM_BANDFMT_DOUBLE: loop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Tan transform. + */ +int +im_tantra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_tantra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_tantra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) tantra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Define what we do for each band element type. Non-complex input, any + * output. + */ +#define aloop( IN, OUT )\ + {\ + IN *p = (IN *) in;\ + OUT *q = (OUT *) out;\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = IM_DEG( atan( (double) p[x] ) );\ + } + +/* atan a buffer of PELs. + */ +static void +atantra_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = width * im->Bands; + + /* Switch for all input types. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: aloop( unsigned char, float ); break; + case IM_BANDFMT_CHAR: aloop( signed char, float ); break; + case IM_BANDFMT_USHORT: aloop( unsigned short, float ); break; + case IM_BANDFMT_SHORT: aloop( signed short, float ); break; + case IM_BANDFMT_UINT: aloop( unsigned int, float ); break; + case IM_BANDFMT_INT: aloop( signed int, float ); break; + case IM_BANDFMT_FLOAT: aloop( float, float ); break; + case IM_BANDFMT_DOUBLE: aloop( double, double ); break; + + default: + assert( 0 ); + } +} + +/* Atan transform. + */ +int +im_atantra( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_atantra", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_atantra", _( "not non-complex" ) ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + break; + + default: + assert( 0 ); + } + + /* Generate! + */ + if( im_wrapone( in, out, (im_wrapone_fn) atantra_gen, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/arithmetic/man3/Makefile.am b/libsrc/arithmetic/man3/Makefile.am new file mode 100644 index 00000000..fb5bab54 --- /dev/null +++ b/libsrc/arithmetic/man3/Makefile.am @@ -0,0 +1,48 @@ +man_MANS = \ + im_abs.3 \ + im_acostra.3 \ + im_bandmean.3 \ + im_sign.3 \ + im_ceil.3 \ + im_floor.3 \ + im_add.3 \ + im_asintra.3 \ + im_atantra.3 \ + im_avg.3 \ + im_cmulnorm.3 \ + im_costra.3 \ + im_deviate.3 \ + im_divide.3 \ + im_exp10tra.3 \ + im_expntra.3 \ + im_exptra.3 \ + im_fav4.3 \ + im_gadd.3 \ + im_gaddim.3 \ + im_gfadd.3 \ + im_invert.3 \ + im_lintra.3 \ + im_lintra_vec.3 \ + im_litecor.3 \ + im_log10tra.3 \ + im_logtra.3 \ + im_max.3 \ + im_maxpos.3 \ + im_maxpos_vec.3 \ + im_measure.3 \ + im_min.3 \ + im_minpos.3 \ + im_minpos_vec.3 \ + im_multiply.3 \ + im_powtra.3 \ + im_sintra.3 \ + im_rint.3 \ + im_stats.3 \ + im_remainder.3 \ + im_remainderconst.3 \ + im_subtract.3 \ + im_tantra.3 \ + im_expntra_vec.3 \ + im_powtra_vec.3 + +EXTRA_DIST = ${man_MANS} diff --git a/libsrc/arithmetic/man3/im_abs.3 b/libsrc/arithmetic/man3/im_abs.3 new file mode 100644 index 00000000..329746f4 --- /dev/null +++ b/libsrc/arithmetic/man3/im_abs.3 @@ -0,0 +1,18 @@ +.TH IM_ABS 3 "25 April 1991" +.SH NAME +im_abs \- finds the absolute value or the magnitude of an image +.SH SYNOPSIS +#include + +int im_abs( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_abs(3) +finds the absolute value of an image. Copy for UNSIGNED types, negate +for int types, fabs(3) for float types, and calculate modulus for +complex types. Any size, any number of bands. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_exp10tra(3), im_sign(3) diff --git a/libsrc/arithmetic/man3/im_acostra.3 b/libsrc/arithmetic/man3/im_acostra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_acostra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/arithmetic/man3/im_add.3 b/libsrc/arithmetic/man3/im_add.3 new file mode 100644 index 00000000..144c589e --- /dev/null +++ b/libsrc/arithmetic/man3/im_add.3 @@ -0,0 +1,102 @@ +.TH ADDITION 3 "24 April 1991" +.SH NAME +im_add, im_gadd, im_gaddim, im_gfadd \- add two images +.SH SYNOPSIS +#include + +int im_add(in1, in2, out) +.br +IMAGE *in1, *in2, *out; + +int im_gadd(a, in1, b, in2, c, out) +.br +double a, b, c; +.br +IMAGE *in1, *in2, *out; + +int im_gaddim(a, in1, b, in2, c, out) +.br +double a, b, c; +.br +IMAGE *in1, *in2, *out; + +int im_gfadd(a, in1, b, in2, c, out) +.br +double a, b, c; +.br +IMAGE *in1, *in2, *out; + +.SH DESCRIPTION +These functions operate on two images held by image descriptors in1 and in2 +and write the result to the image descriptor out. Input images in1 and in2 +should have the same channels and the same size; however they can be of +different types. Only the history of the image descriptor pointed by in1 is +copied to out. + +.B im_add(3) + +For two integer images, add the two images and write the output as + + in1 - uchar char ushort short uint int + -------|----------------------------------------- + in2 | + uchar | ushort short ushort short uint int + char | short short short short int int + ushort | ushort short ushort short uint int + short | short short short short int int + uint | uint int uint int uint int + int | int int int int int int + +If one or more of the images is a floating point type, the output is FMTFLOAT, +unless one or more of the inputs is FMTDOUBLE, in which case the output is +also FMTDOUBLE. + +If one or more of the images is a complex type, the output is FMTCOMPLEX, +unless one or more of the inputs is FMTDPCOMPLEX, in which case the output is +also FMTDPCOMPLEX. + +.B im_gadd(3) +performs generalised addition of two images by calling +.B im_gaddim(3) +and +.B im_gfadd(3). +These are very old and tired things, and should not be used. + +Input should be non complex. Output depends on input according to function +called. The result at each point is: a * pel1 + b * pel2 + c, properly +rounded if necessary. Pel1 and pel2 are the +corresponding pixels from in1 and in2 respectively. + +im_gaddim() performs generalised addition of in1 and in2, on the condition +they are neither float nor double nor complex. The format of the resultant +image is given by the table: + + in1 - uchar char ushort short uint int + -------|----------------------------------------- + in2 | + uchar | ushort short ushort short uint int + char | short short short short int int + ushort | ushort short ushort short uint int + short | short short short short int int + uint | uint int uint int uint int + int | int int int int int int + +The result at each point is: a * pel1 + b * pel2 + c, properly rounded. Pel1 +and pel2 are the corresponding pixels from in1 and in2 respectively. + +.B im_gfadd(3) +adds the non-complex images pointed by in1 and in2. Result is +float except if one (or both) inputs is double. In the latter case the result +is double. The result at each point is: a * pel1 + b * pel2 + c. Pel1 and +pel2 are the corresponding pixels from in1 and in2 respectively. + +.SH BUGS +None of the functions checks the result for over/underflow. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_subtract(3), im_lintra(3), im_multiply(3). +.SH AUTHOR +N. Dessipris \- 22/04/1991 +.br +J. Cupitt, im_add(), \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_asintra.3 b/libsrc/arithmetic/man3/im_asintra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_asintra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/arithmetic/man3/im_atantra.3 b/libsrc/arithmetic/man3/im_atantra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_atantra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/arithmetic/man3/im_avg.3 b/libsrc/arithmetic/man3/im_avg.3 new file mode 100644 index 00000000..12e9570b --- /dev/null +++ b/libsrc/arithmetic/man3/im_avg.3 @@ -0,0 +1,101 @@ +.TH STATS 3 "24 April 1991" +.SH NAME +im_avg, im_deviate, im_min, im_minpos, im_max, im_maxpos \- find the mean, standard deviation, minimum and maximum of an image +.SH SYNOPSIS +.B #include + +.B int im_avg(im, out) +.br +.B IMAGE *im; +.br +.B double *out; + +.B int im_deviate(im, out) +.br +.B IMAGE *im; +.br +.B double *out; + +.B int im_min(im, out) +.br +.B IMAGE *im; +.br +.B double *out; + +.B int im_minpos(im, xpos, ypos, min) +.br +.B IMAGE *im; +.br +.B int *xpos, *ypos; +.br +.B double *min; + +.B int im_max(im, out) +.br +.B IMAGE *im; +.br +.B double *out; + +.B int im_maxpos(im, xpos, ypos, max) +.br +.B IMAGE *im; +.br +.B int *xpos, *ypos; +.br +.B double *max; + +.SH DESCRIPTION +These functions find the mean, standard deviation, minimum, maximum of an image. +They operate on all bands of the input image. Use +.B im_stats(3) +if you need to calculate on bands separately. +All computations are carried out in +double precision arithmetic. +The standard deviation is calculated using the formula: + + Var{E} = 1 / (N - 1) * (E{X^2} - E{X}^2 / N) + stdev{E} = sqrt(Var{E}). + +.B im_avg(3) +finds the average of an image pointed by im. Takes as input any non-complex +image format and returns a double at the location pointed by out. + +.B im_deviate(3) +finds the standard deviation of an image pointed by im. Takes as +input any non-complex image format and returns +a double at the location pointed by out. + +.B im_min(3) +finds the the minimum value of the image pointed by im and returns it at the +location pointed by out. Takes as +input any image format and returns +a double at the location pointed by out. If input is complex +the min square amplitude (re*re+im*im) is returned. + +.B im_minpos(3) +finds the the minimum value of the image pointed by im and returns it at the +location pointed by out. The coordinates of the last occurrence of +min is returned at locations pointed by xpos, ypos. If input is complex +the min square amplitude (re*re+im*im) is returned. + +.B im_max(3) +finds the the maximum value of the image pointed by im and returns it at the +location pointed by out. If input is complex +the max square amplitude (re*re+im*im) is returned. + +.B im_maxpos(3) +finds the the maximum value of the image pointed by im and returns it at the +location pointed by max. The coordinates of the last occurrence of +max is returned at locations pointed by xpos, ypos. If input is complex +the max square amplitude (re*re+im*im) and its last occurrence is returned. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_exptra(3), im_lintra(3), im_abs(3), im_stats(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 24/04/1991 +.br +J. Cupitt \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_bandmean.3 b/libsrc/arithmetic/man3/im_bandmean.3 new file mode 100644 index 00000000..69f234ae --- /dev/null +++ b/libsrc/arithmetic/man3/im_bandmean.3 @@ -0,0 +1,20 @@ +.TH IM_BANDMEAN 3 "18 July 2007" +.SH NAME +im_bandmean \- average bands in an image +.SH SYNOPSIS +#include + +int im_bandmean( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_bandmean(3) +writes a one-band image where each pixel is the average of the bands for that +pixel in the input image. The output band format is the same as the input +band format. + +Any size, any number of bands, any type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_mean(3), im_recomb(3) diff --git a/libsrc/arithmetic/man3/im_ceil.3 b/libsrc/arithmetic/man3/im_ceil.3 new file mode 100644 index 00000000..4a52b016 --- /dev/null +++ b/libsrc/arithmetic/man3/im_ceil.3 @@ -0,0 +1,17 @@ +.TH IM_CEIL 3 "20 June 2002" +.SH NAME +im_ceil \- for each pixel, find the smallest integral value not less than +.SH SYNOPSIS +#include + +int im_ceil( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_ceil(3) +finds the smallest integral value not less than. Copy for integer types, +call ceil(3) for float and complex types. Output type == input type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_abs(3), im_clip2fmt(3), im_floor(3) diff --git a/libsrc/arithmetic/man3/im_cmulnorm.3 b/libsrc/arithmetic/man3/im_cmulnorm.3 new file mode 100644 index 00000000..2962752e --- /dev/null +++ b/libsrc/arithmetic/man3/im_cmulnorm.3 @@ -0,0 +1,67 @@ +.TH IM_MULTIPLY 3 "24 April 1991" +.SH NAME +im_cmulnorm, im_multiply \- multiply two images +.SH SYNOPSIS +.B #include + +.B int im_cmulnorm(in1, in2, out) +.br +.B IMAGE *in1, *in2, *out; + +.B int im_multiply(in1, in2, out) +.br +.B IMAGE *in1, *in2, *out; + +.SH DESCRIPTION +These functions operate on two images held by image descriptors in1 and in2 +and write the result to the image descriptor out. Input images in1 and in2 +should have the same channels and the same sizes; however they can be of +different types. Only the history of the image descriptor pointed by in1 is +copied to out. + +.B im_multiply(3) +applied to two integer images multiplies the two images and writes the output +as: + + in1 | uchar char ushort short uint int + -------+----------------------------------------- + in2 | + uchar | ushort short ushort short uint int + char | short short short short int int + ushort | ushort short ushort short uint int + short | short short short short int int + uint | uint int uint int uint int + int | int int int int int int + +If one or more of the images is a floating point type, the output is FMTFLOAT, +unless one or more of the inputs is FMTDOUBLE, in which case the output is +also FMTDOUBLE. + +If one or more of the images is a complex type, the output is FMTCOMPLEX, +unless one or more of the inputs is FMTDPCOMPLEX, in which case the output is +also FMTDPCOMPLEX. + +For complex input pels (x1,y1) and (x2,y2), im_multiply() writes +(x1*x2 - y1*y2, x1*y2 + x2*y1). + +.B im_cmulnorm(3) +multiplies two complex images. The complex output is normalised to 1 by +dividing both the real and the imaginary part of each pel with the norm; for +instance if the complex multiplication produces (a,b) then the output written +by this function is (a/norm, b/norm), where norm=a*a+b*b. Result is checked +for norm=0. The function is useful for phase correlation. Both inputs should +be complex. + +Result is float complex if both inputs are float complex. In any other case +the result is double complex. + +.SH BUGS +None of the functions checks the result for over/underflow. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_subtract(3), im_lintra(3), im_add(3). +.SH AUTHOR +N. Dessipris \- 22/04/1991 +.br +J. Cupitt (im_multiply) \- 22/04/1991 diff --git a/libsrc/arithmetic/man3/im_costra.3 b/libsrc/arithmetic/man3/im_costra.3 new file mode 100644 index 00000000..be8b6393 --- /dev/null +++ b/libsrc/arithmetic/man3/im_costra.3 @@ -0,0 +1,47 @@ +.TH IM_COSTRA 3 "24 April 1991" +.SH NAME +im_costra, im_sintra, im_tantra, +im_acostra, im_asintra, im_atantra \- basic trig functions +.SH SYNOPSIS +#include + +int im_costra(in, out) +.br +IMAGE *in, *out; + +int im_sintra(in, out) +.br +IMAGE *in, *out; + +int im_tantra(in, out) +.br +IMAGE *in, *out; + +int im_acostra(in, out) +.br +IMAGE *in, *out; + +int im_asintra(in, out) +.br +IMAGE *in, *out; + +int im_atantra(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +Basic trig functions. The input image is mapped through either cos(3), sin(3) or +tan(3) and written to out. All work in degrees. + +The size and number of bands are unchanged, the output type is float, unless +the input is double, in which case the output is double. Non-complex images +only! + +.SH RETURN VALUE +Each function returns 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_lintra(3), im_abs(3), im_mean(3), im_logtra(3) +.SH COPYRIGHT +National Gallery, 1995. +.SH AUTHOR +J. Cupitt \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_deviate.3 b/libsrc/arithmetic/man3/im_deviate.3 new file mode 100644 index 00000000..46443e20 --- /dev/null +++ b/libsrc/arithmetic/man3/im_deviate.3 @@ -0,0 +1 @@ +.so man3/im_avg.3 diff --git a/libsrc/arithmetic/man3/im_divide.3 b/libsrc/arithmetic/man3/im_divide.3 new file mode 100644 index 00000000..49e3b3b0 --- /dev/null +++ b/libsrc/arithmetic/man3/im_divide.3 @@ -0,0 +1,33 @@ +.TH DIVISION 3 "24 April 1991" +.SH NAME +im_divide \- divide two images +.SH SYNOPSIS +.B #include + +.B int im_divide(in1, in2, out) +.br +.B IMAGE *in1, *in2, *out; + +.SH DESCRIPTION +.B im_divide(3) +divides two images. The result is float except if one (or both) input is +double. In the latter case the result is double. If either input is complex, +the result is complex. If either input is double complex, the output is double +complex. + +Input images in1 and in2 should have the same channels and the same sizes, +however they can be of different types. + +For complex input pels (x1,y1) and (x2,y2), +.B im_divide(3) +calculates + ((x1*x2 + y1*y2)/(x2*x2 + y2*y2), (y1*x2 - x1*y2)/(x2*x2 + y2*y2)). + +.SH BUGS +The function does not check the result for over/underflow. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_remainder(3), im_multiply(3), im_subtract(3), im_lintra(3), im_add(3). +.SH COPYRIGHT +National Gallery, 1995 diff --git a/libsrc/arithmetic/man3/im_exp10tra.3 b/libsrc/arithmetic/man3/im_exp10tra.3 new file mode 100644 index 00000000..6538cef4 --- /dev/null +++ b/libsrc/arithmetic/man3/im_exp10tra.3 @@ -0,0 +1,100 @@ +.TH IM_EXPTRA 3 "24 April 1991" +.SH NAME +im_exp10tra, im_exptra, im_expntra, im_expntra_vec, im_log10tra, im_logtra, +im_powtra, im_powtra_vec \- logarithmic, exponential and power transform of an image +.SH SYNOPSIS +.B #include + +.B int im_expntra(in, out, base) +.br +.B IMAGE *in, *out; +.br +.B double base; + +.B int im_expntra_vec(in, out, n, vec) +.br +.B IMAGE *in, *out; +.br +.B int n; +.br +.B double *vec; + +.B int im_exp10tra(in, out) +.br +.B IMAGE *in, *out; + +.B int im_exptra(in, out) +.br +.B IMAGE *in, *out; + +.B int im_log10tra(in, out) +.br +.B IMAGE *in, *out; + +.B int im_logtra(in, out) +.br +.B IMAGE *in, *out; + +.B int im_powtra(in, out, exponent) +.br +.B IMAGE *in, *out; +.br +.B double exponent; + +.B int im_powtra_vec(in, out, n, vec) +.br +.B IMAGE *in, *out; +.br +.B int n; +.br +.B double *vec; + +.SH DESCRIPTION +Each of the above functions maps in through a log or anti-log +function of some sort and writes the result to out. The size and number of +bands are unchanged, the output type is float, unless the input is double, in +which case the output is double. Non-complex images only! + +.B im_expntra(3) +transforms element x of input, to pow(base, x) in output. It detects division +by zero, setting those pixels to zero in the output. Beware: it does this +silently! + +.B im_expntra_vec(3) +works as im_expntra(), but lets you specify a constant per band. + +.B im_exp10tra(3) +transforms element x of input, to pow(10,0, x) in output. Internally, it is +defined in terms of im_expntra(). + +.B im_exptra(3) +transforms element x of input, to pow(e, x) in output, where e is the +mathematical constant. Internally, it is defined in terms of im_expntra(). + +.B im_log10tra(3) +transforms element x of input, to log10tra(x) in output. + +.B im_logtra(3) +transforms element x of input, to logtra(x) in output. + +.B im_powtra(3) +transforms element x of input, to pow(x, exponent) in output. It detects +division by zero, setting those pixels to zero in the output. Beware: it does +this silently! + +.B im_powtra_vec(3) +works as im_powtra(3), but lets you specify a constant per band. + +.SH BUGS +None of the functions checks for under/overflow. Overflow is very common for +many of these functions! + +.SH RETURN VALUE +Each function returns 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_multiply(3), im_subtract(3), im_lintra(3), +im_absim(3), im_mean(3), im_max(3). +.SH AUTHOR +N. Dessipris \- 24/04/1991 +.br +J. Cupitt (rewrite) \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_expntra.3 b/libsrc/arithmetic/man3/im_expntra.3 new file mode 100644 index 00000000..b7685e3d --- /dev/null +++ b/libsrc/arithmetic/man3/im_expntra.3 @@ -0,0 +1 @@ +.so man3/im_exp10tra.3 diff --git a/libsrc/arithmetic/man3/im_expntra_vec.3 b/libsrc/arithmetic/man3/im_expntra_vec.3 new file mode 100644 index 00000000..b7685e3d --- /dev/null +++ b/libsrc/arithmetic/man3/im_expntra_vec.3 @@ -0,0 +1 @@ +.so man3/im_exp10tra.3 diff --git a/libsrc/arithmetic/man3/im_exptra.3 b/libsrc/arithmetic/man3/im_exptra.3 new file mode 100644 index 00000000..b7685e3d --- /dev/null +++ b/libsrc/arithmetic/man3/im_exptra.3 @@ -0,0 +1 @@ +.so man3/im_exp10tra.3 diff --git a/libsrc/arithmetic/man3/im_fav4.3 b/libsrc/arithmetic/man3/im_fav4.3 new file mode 100644 index 00000000..a2af2034 --- /dev/null +++ b/libsrc/arithmetic/man3/im_fav4.3 @@ -0,0 +1,23 @@ +.TH IM_FAV4 3 "13 April 1992" +.SH NAME +im_fav4 \- averages four frames +.SH SYNOPSIS +.B #include + +.B int im_fav4(in, out) +.br +.B IMAGE **in, *out; +.SH DESCRIPTION +.B im_fav4 +averages four input VIPS images, which must be unsigned byte images with any +number of bands. This is useful for noise reduction by grabbing 4 frames +and averaging them. With 7 bit images this will give quite a convincing +eighth bit. +.SH RETURN VALUE +returns 0 on success or -1 on error +.SH SEE\ ALSO +add(1) +.SH COPYRIGHT +.br +K. Martinez 13/4/92 + diff --git a/libsrc/arithmetic/man3/im_floor.3 b/libsrc/arithmetic/man3/im_floor.3 new file mode 100644 index 00000000..79320b9e --- /dev/null +++ b/libsrc/arithmetic/man3/im_floor.3 @@ -0,0 +1,17 @@ +.TH IM_FLOOR 3 "20 June 2002" +.SH NAME +im_floor \- for each pixel, find the largest integral value not greater than +.SH SYNOPSIS +#include + +int im_floor( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_floor(3) +finds the largest integral value not greater than. Copy for integer types, +call floor(3) for float and complex types. Output type == input type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_abs(3), im_clip2fmt(3), im_ceil(3) diff --git a/libsrc/arithmetic/man3/im_gadd.3 b/libsrc/arithmetic/man3/im_gadd.3 new file mode 100644 index 00000000..295df09e --- /dev/null +++ b/libsrc/arithmetic/man3/im_gadd.3 @@ -0,0 +1 @@ +.so man3/im_add.3 diff --git a/libsrc/arithmetic/man3/im_gaddim.3 b/libsrc/arithmetic/man3/im_gaddim.3 new file mode 100644 index 00000000..295df09e --- /dev/null +++ b/libsrc/arithmetic/man3/im_gaddim.3 @@ -0,0 +1 @@ +.so man3/im_add.3 diff --git a/libsrc/arithmetic/man3/im_gfadd.3 b/libsrc/arithmetic/man3/im_gfadd.3 new file mode 100644 index 00000000..295df09e --- /dev/null +++ b/libsrc/arithmetic/man3/im_gfadd.3 @@ -0,0 +1 @@ +.so man3/im_add.3 diff --git a/libsrc/arithmetic/man3/im_invert.3 b/libsrc/arithmetic/man3/im_invert.3 new file mode 100644 index 00000000..dbe092ed --- /dev/null +++ b/libsrc/arithmetic/man3/im_invert.3 @@ -0,0 +1,24 @@ +.TH IM_INVERT 3 "11 April 1990" +.SH NAME +im_invert \- inverts an image pointed by an image descriptor. +.SH SYNOPSIS +.B #include + +.B int im_invert(in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +.B im_invert +inverts the image held by image descriptor in and writes the result on the +image descriptor out. The function works on FMTUCHAR images only. +The result at point x of the input image is 255-x at the output image. +Input can have any no of channels. +.br + This is not a generally useful program -- it is included as an example of a +very simple new-style IO function. Feel the power of the source, Luke! +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_add(3), im_lintra(3), im_multiply(3). +.SH AUTHOR +J. Cupitt \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_lintra.3 b/libsrc/arithmetic/man3/im_lintra.3 new file mode 100644 index 00000000..788ba37a --- /dev/null +++ b/libsrc/arithmetic/man3/im_lintra.3 @@ -0,0 +1,58 @@ +.TH IM_LINTRA 3 "24 April 1991" +.SH NAME +im_lintra, im_lintra_vec \- performs a linear transformation on an image +.SH SYNOPSIS +.B #include + +.B int im_lintra_vec(n, a, in, b, out) +.br +.B int n; +.br +.B double *a, *b; +.br +.B IMAGE *in, *out; + +.B int im_lintra(a, in, b, out) +.br +.B double a, b; +.br +.B IMAGE *in, *out; + +.SH DESCRIPTION +.B im_lintra_vec(3) +performs a linear transform on image in, that is, it calculates + + out = a * in + b + +.B a +and +.B b +are vectors, ie. arrays of constants of length +.B n. +If +.B in +has one band, then the vectors may be any length and the output image will +have the same number of bands as the length of the vector. If +.B in +has many bands, then the vector must be length 1, or have the same length as +the number of bands in the image. + +If the input format is one of the integer types +then output is float. In all other cases the +output is the same as the input. + +.B im_lintra(3) +is a convenience function which calls +.B im_lintra_vec(3) +with a vector of length 1. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH BUGS +The function does not check for under/overflow +.SH SEE\ ALSO +im_exptra(3), im_logtra(3) +.SH AUTHOR +N. Dessipris \- 24/04/1991 +.br +J. Cupitt (rewrite) \- 21/7/93 diff --git a/libsrc/arithmetic/man3/im_lintra_vec.3 b/libsrc/arithmetic/man3/im_lintra_vec.3 new file mode 100644 index 00000000..4c6c0ca9 --- /dev/null +++ b/libsrc/arithmetic/man3/im_lintra_vec.3 @@ -0,0 +1 @@ +.so man3/im_lintra.3 diff --git a/libsrc/arithmetic/man3/im_litecor.3 b/libsrc/arithmetic/man3/im_litecor.3 new file mode 100644 index 00000000..46a838b2 --- /dev/null +++ b/libsrc/arithmetic/man3/im_litecor.3 @@ -0,0 +1,52 @@ +.TH IM_LITECOR 3 "5 December 1991" +.SH NAME +im_litecor \- perform light correction +.SH SYNOPSIS +.B #include + +.B int im_litecor(in, white, out, clip, factor) +.br +.B IMAGE *in, *out; +.br +.B int clip; +.br +.B double factor; +.SH DESCRIPTION +.B im_litecor(3) +performs light correction on the image held by the IMAGE descriptor in, +with respect to a reference white image held by the IMAGE descriptor white. +The result is written onto the IMAGE descriptor out. The function works +on byte one channel images only. + +The flag clip can take two values 0 and 1. If clip is 1 then the input is +corrected with reference to the maximum value of white (maxw) as follows. + + pel_out = factor * pel_in * maxw / pel_white. + +If clip is 0 then the output is scaled with the maximum possible output set +to 255. In this case factor is not used but it must be set to a dummy value. + +The basic reason for lighting correction is that the input frame does not +have a uniform distribution of white light due to the optical response of +the lens. The function accepts a white image which is a simple multiple +of the input image in size; for example it is possible that the white +is a subsampled version of in; however the sizes of in must be an exact +multiple of the white. If clip is set to 0, lighting correction is +carried out and the result is scaled between 0 and 255. This can be used +to correct individual frames. + +If multiband images are grabbed, then flag should be set to 1, since +no scaling must be done. In this case the factor can reduce the number of +clipped pels if overshooting occurs in the brightest band. The program +prints the number of clipped pels with im_warning(3). +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH BUGS +clip==0 case not working too well. +.SH SEE\ ALSO +im_add(3), im_lintra(3), im_multiply(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 05/12/1991 diff --git a/libsrc/arithmetic/man3/im_log10tra.3 b/libsrc/arithmetic/man3/im_log10tra.3 new file mode 100644 index 00000000..9ae2c5a1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_log10tra.3 @@ -0,0 +1,2 @@ +.so man3/im_exp10tra.3 + diff --git a/libsrc/arithmetic/man3/im_logtra.3 b/libsrc/arithmetic/man3/im_logtra.3 new file mode 100644 index 00000000..9ae2c5a1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_logtra.3 @@ -0,0 +1,2 @@ +.so man3/im_exp10tra.3 + diff --git a/libsrc/arithmetic/man3/im_max.3 b/libsrc/arithmetic/man3/im_max.3 new file mode 100644 index 00000000..fc6dde89 --- /dev/null +++ b/libsrc/arithmetic/man3/im_max.3 @@ -0,0 +1,2 @@ +.so man3/im_avg.3 + diff --git a/libsrc/arithmetic/man3/im_maxpos.3 b/libsrc/arithmetic/man3/im_maxpos.3 new file mode 100644 index 00000000..fc6dde89 --- /dev/null +++ b/libsrc/arithmetic/man3/im_maxpos.3 @@ -0,0 +1,2 @@ +.so man3/im_avg.3 + diff --git a/libsrc/arithmetic/man3/im_maxpos_vec.3 b/libsrc/arithmetic/man3/im_maxpos_vec.3 new file mode 100644 index 00000000..6a82283c --- /dev/null +++ b/libsrc/arithmetic/man3/im_maxpos_vec.3 @@ -0,0 +1,42 @@ +.TH IM_MAXPOS_VEC 3 "01 September 2006" +.SH NAME + im_maxpos_vec, im_minpos_vec \- Find highest or lowest pixel values +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "int im_maxpos_vec( IMAGE " "*im" ", int " "*xpos" ", int " "*ypos" ", double " "*maxima" ", int " "n" " ); +.br + +.BI "int im_minpos_vec( IMAGE " "*im" ", int " "*xpos" ", int " "*ypos" ", double " "*minima" ", int " "n" " ); +.fi +.SH DESCRIPTION +.B im_maxpos_vec(3) +fills the arrays +.I xpos +and +.I ypos +with the x and y coordinates of the +.I n +pixels in the image +.I im +which have the highest values. It writes those values (at double-precsion) +into the corresponding elements in the array +.IR maxima ". +.PP +If there is a draw for the +.IR n "th +pixel, then which of the drawing pixels is used is not defined. +.PP +.B im_minpos_vec(3) +performs the same operation, looking for the lowest valued pixels. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_maxpos(3), im_minpos(3) +.SH COPYRIGHT +.br +Copyright 2006, The Nottingham Trent University. +Copyright 2006, Tom Vajzovic. +.SH AUTHOR +Tom Vajzovic diff --git a/libsrc/arithmetic/man3/im_measure.3 b/libsrc/arithmetic/man3/im_measure.3 new file mode 100644 index 00000000..5aa240f1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_measure.3 @@ -0,0 +1,47 @@ +.TH IM_MEASURE 3 "24 October 1992" +.SH NAME +im_measure \- measure colour patches off images +.SH SYNOPSIS +#include + +DOUBLEMASK *im_measure(in, box, h, v, sel, nsel, name) +.br +IMAGE *in; +.br +IMAGE_BOX *box; +.br +int h, v; +.br +int *sel; +.br +int nsel; +.br +char *name; + +.SH DESCRIPTION +Analyse a grid of colour patches, producing a DOUBLEMASK of averages. +Pass an IMAGE, an IMAGE_BOX, the number of horizontal and vertical +patches, an array giving the numbers of the patches to measure (patches +are numbered left-to-right, top-to-bottom, starting with 1) and the name we +should give the output mask. Return a DOUBLEMASK in which rows are patches and +columns are bands. Only the central 50% of each patch is averaged. + +Example: 6 band image of 4x2 block of colour patches. + + +---+---+---+---+ + | 1 | 2 | 3 | 4 | + +---+---+---+---+ + | 5 | 6 | 7 | 8 | + +---+---+---+---+ + +Then call im_measure( im, box, 4, 2, { 2, 4 }, 2, "fred" ) makes a mask +"fred" which has 6 columns, two rows. The first row contains the averages +for patch 2, the second for patch 4. + +Output warnings: a warning is issued if the standard deviation of any patch is +greater than 20% of the mean of that patch. + +.SH RETURN VALUE +NULL on error. +.SH SEE ALSO +im_avg(3), im_deviate(3), im_stats(3). diff --git a/libsrc/arithmetic/man3/im_min.3 b/libsrc/arithmetic/man3/im_min.3 new file mode 100644 index 00000000..46443e20 --- /dev/null +++ b/libsrc/arithmetic/man3/im_min.3 @@ -0,0 +1 @@ +.so man3/im_avg.3 diff --git a/libsrc/arithmetic/man3/im_minpos.3 b/libsrc/arithmetic/man3/im_minpos.3 new file mode 100644 index 00000000..fc6dde89 --- /dev/null +++ b/libsrc/arithmetic/man3/im_minpos.3 @@ -0,0 +1,2 @@ +.so man3/im_avg.3 + diff --git a/libsrc/arithmetic/man3/im_minpos_vec.3 b/libsrc/arithmetic/man3/im_minpos_vec.3 new file mode 100644 index 00000000..b92d839c --- /dev/null +++ b/libsrc/arithmetic/man3/im_minpos_vec.3 @@ -0,0 +1,2 @@ +.so man3/im_maxpos_vec.3 + diff --git a/libsrc/arithmetic/man3/im_multiply.3 b/libsrc/arithmetic/man3/im_multiply.3 new file mode 100644 index 00000000..29c07b75 --- /dev/null +++ b/libsrc/arithmetic/man3/im_multiply.3 @@ -0,0 +1 @@ +.so man3/im_cmulnorm.3 diff --git a/libsrc/arithmetic/man3/im_powtra.3 b/libsrc/arithmetic/man3/im_powtra.3 new file mode 100644 index 00000000..9ae2c5a1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_powtra.3 @@ -0,0 +1,2 @@ +.so man3/im_exp10tra.3 + diff --git a/libsrc/arithmetic/man3/im_powtra_vec.3 b/libsrc/arithmetic/man3/im_powtra_vec.3 new file mode 100644 index 00000000..b7685e3d --- /dev/null +++ b/libsrc/arithmetic/man3/im_powtra_vec.3 @@ -0,0 +1 @@ +.so man3/im_exp10tra.3 diff --git a/libsrc/arithmetic/man3/im_remainder.3 b/libsrc/arithmetic/man3/im_remainder.3 new file mode 100644 index 00000000..3a0a96c6 --- /dev/null +++ b/libsrc/arithmetic/man3/im_remainder.3 @@ -0,0 +1,47 @@ +.TH REMAINDER 3 "May 2002" +.SH NAME +im_remainder, im_remainderconst, im_remainderconst_vec \- remainder after integer division +.SH SYNOPSIS +#include + +int im_remainder( IMAGE *in1, IMAGE *in2, IMAGE *out ) +.br + +int im_remainderconst( IMAGE *in, IMAGE *out, double c ) +.br + +int im_remainderconst_vec( IMAGE *in, IMAGE *out, int n, double *c ) +.SH DESCRIPTION +.B im_remainder(3) +calculates the remainder after integer division of two images. The output +type is the same as the type of +.B in1 +unless +.B in1 +is float or complex, in which +case the output type is signed integer. + +.B im_remainderconst(3) +calculates the remainder after integer division of +.B in +by the constant +.B c. +The output +type is the same as the type of +.B in +unless +.B in +is float or complex, in which +case the output type is signed integer. + +.B im_remainderconst_vec(3) +works as +.B im_remainderconst(3), +but lets you specify a constant per band. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_lintra(3), im_divide(3) +.SH COPYRIGHT +National Gallery, 2002 diff --git a/libsrc/arithmetic/man3/im_remainderconst.3 b/libsrc/arithmetic/man3/im_remainderconst.3 new file mode 100644 index 00000000..bef634ce --- /dev/null +++ b/libsrc/arithmetic/man3/im_remainderconst.3 @@ -0,0 +1 @@ +.so man3/im_remainder.3 diff --git a/libsrc/arithmetic/man3/im_remainderconst_vec.3 b/libsrc/arithmetic/man3/im_remainderconst_vec.3 new file mode 100644 index 00000000..bef634ce --- /dev/null +++ b/libsrc/arithmetic/man3/im_remainderconst_vec.3 @@ -0,0 +1 @@ +.so man3/im_remainder.3 diff --git a/libsrc/arithmetic/man3/im_rint.3 b/libsrc/arithmetic/man3/im_rint.3 new file mode 100644 index 00000000..15f371e1 --- /dev/null +++ b/libsrc/arithmetic/man3/im_rint.3 @@ -0,0 +1,18 @@ +.TH IM_RINT 3 +.SH NAME +im_rint \- for each pixel, find the nearest integral value +.SH SYNOPSIS +#include + +int im_rint( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_rint(3) +finds the nearest integral value. Copy for integer types, +call rint(3) for float and complex types. Output type == input type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_abs(3), im_clip2fmt(3), im_floor(3) + diff --git a/libsrc/arithmetic/man3/im_sign.3 b/libsrc/arithmetic/man3/im_sign.3 new file mode 100644 index 00000000..f6fc7781 --- /dev/null +++ b/libsrc/arithmetic/man3/im_sign.3 @@ -0,0 +1,19 @@ +.TH IM_SIGN 3 "July 2002" +.SH NAME +im_sign \- find the unit vector in the direction of value +.SH SYNOPSIS +#include + +int im_sign( in, out ) +.br +IMAGE *in, *out; +.SH DESCRIPTION +.B im_sign(3) +finds the unit vector in the direction of the pixel value. For non-complex +images, it returns a signed char image with values -1, 0, and 1 for negative, +zero and positive pixels. For complex images it returns a +complex normalised to length 1. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_abs(3), im_cmulnorm(3), im_c2amph(3) diff --git a/libsrc/arithmetic/man3/im_sintra.3 b/libsrc/arithmetic/man3/im_sintra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_sintra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/arithmetic/man3/im_stats.3 b/libsrc/arithmetic/man3/im_stats.3 new file mode 100644 index 00000000..4061098b --- /dev/null +++ b/libsrc/arithmetic/man3/im_stats.3 @@ -0,0 +1,24 @@ +.TH IM_STATS 3 "24 October 1992" +.SH NAME +im_stats \- calculate many image statistics in a single pass +.SH SYNOPSIS +.B #include + +.B DOUBLEMASK *im_stats(in) +.br +.B IMAGE *in; +.SH DESCRIPTION +Find many image statistics in a single pass through the PELs. Returns a +DOUBLEMASK of 6 columns by n+1 (where n is number of bands in image in) rows. +Columns are statistics, and are, in order: minimum, maximum, sum, sum of +squares, mean, standard deviation. Row 0 has statistics for all bands +together, row 1 has stats for band 1, and so on. + +Non-complex images only! +.SH RETURN VALUE +NULL on error. +.SH SEE\ ALSO +im_avg(3), im_deviate(3) +.SH COPYRIGHT +.br +National Gallery, 1992 diff --git a/libsrc/arithmetic/man3/im_subtract.3 b/libsrc/arithmetic/man3/im_subtract.3 new file mode 100644 index 00000000..a20f8b40 --- /dev/null +++ b/libsrc/arithmetic/man3/im_subtract.3 @@ -0,0 +1,41 @@ +.TH SUBTRACTION 3 "24 April 1991" +.SH NAME +im_subtract \- subtracts two images +.SH SYNOPSIS +#include + +int im_subtract(in1, in2, out) +.br +IMAGE *in1, *in2, *out; +.SH DESCRIPTION + +This functions calculates in1 - in2 and writes the result in the image +descriptor out. Input images in1 and in2 should have the same channels and +the same sizes; however they can be of different types. Only the history of +the image descriptor pointed by in1 is copied to out. + +The type of the output is given by the table: + + in1 - uchar char ushort short uint int + -------|----------------------------------------- + in2 | + uchar | short short short short int int + char | short short short short int int + ushort | short short short short int int + short | short short short short int int + uint | int int int int int int + int | int int int int int int + +The result of this operation cannot be unsigned. For float types, the refult +is float unless one of the inputs is double, in which case the result is +double. For complex types the result is FMTCOMPLEX, unless one of the inputs +is FMTDPCOMPLEX, in which case the output is FMTDPCOMPLEX. + +.SH BUGS +None of the functions checks the result for over/underflow. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_lintra(3), im_multiply(3). +.SH COPYRIGHT +National Gallery, 1995 diff --git a/libsrc/arithmetic/man3/im_tantra.3 b/libsrc/arithmetic/man3/im_tantra.3 new file mode 100644 index 00000000..83ac049e --- /dev/null +++ b/libsrc/arithmetic/man3/im_tantra.3 @@ -0,0 +1 @@ +.so man3/im_costra.3 diff --git a/libsrc/boolean/Makefile.am b/libsrc/boolean/Makefile.am new file mode 100644 index 00000000..a56c9b60 --- /dev/null +++ b/libsrc/boolean/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libboolean.la + +libboolean_la_SOURCES = \ + bool_dispatch.c \ + boolean.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/boolean/bool_dispatch.c b/libsrc/boolean/bool_dispatch.c new file mode 100644 index 00000000..ae2cbb50 --- /dev/null +++ b/libsrc/boolean/bool_dispatch.c @@ -0,0 +1,316 @@ +/* VIPS function dispatch tables for conversion. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* One image plus one constant in, one image out. + */ +static im_arg_desc const_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "c" ) +}; + +/* One image plus doublevec in, one image out. + */ +static im_arg_desc vec_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "vec" ) +}; + +/* Call im_and via arg vector. + */ +static int +and_vec( im_object *argv ) +{ + return( im_andimage( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_and. + */ +static im_function and_desc = { + "im_andimage", /* Name */ + "bitwise and of two images", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + and_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_andconst via arg vector. + */ +static int +andconst_vec( im_object *argv ) +{ + int c = *((int *) argv[2]); + + return( im_andconst( argv[0], argv[1], c ) ); +} + +/* Description of im_andconst. + */ +static im_function andconst_desc = { + "im_andimageconst", /* Name */ + "bitwise and of an image with a constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + andconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_and_vec via arg vector. + */ +static int +and_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_and_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_andconst. + */ +static im_function and_vec_desc = { + "im_andimage_vec", /* Name */ + "bitwise and of an image with a vector constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + and_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_or via arg vector. + */ +static int +or_vec( im_object *argv ) +{ + return( im_orimage( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_or. + */ +static im_function or_desc = { + "im_orimage", /* Name */ + "bitwise or of two images", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + or_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_orconst via arg vector. + */ +static int +orconst_vec( im_object *argv ) +{ + int c = *((int *) argv[2]); + + return( im_orconst( argv[0], argv[1], c ) ); +} + +/* Description of im_orconst. + */ +static im_function orconst_desc = { + "im_orimageconst", /* Name */ + "bitwise or of an image with a constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + orconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_or_vec via arg vector. + */ +static int +or_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_or_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_orconst. + */ +static im_function or_vec_desc = { + "im_orimage_vec", /* Name */ + "bitwise or of an image with a vector constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + or_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_eor via arg vector. + */ +static int +eor_vec( im_object *argv ) +{ + return( im_eorimage( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_eor. + */ +static im_function eor_desc = { + "im_eorimage", /* Name */ + "bitwise eor of two images", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + eor_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_eorconst via arg vector. + */ +static int +eorconst_vec( im_object *argv ) +{ + int c = *((int *) argv[2]); + + return( im_eorconst( argv[0], argv[1], c ) ); +} + +/* Description of im_eorconst. + */ +static im_function eorconst_desc = { + "im_eorimageconst", /* Name */ + "bitwise eor of an image with a constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + eorconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_eor_vec via arg vector. + */ +static int +eor_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_eor_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_eorconst. + */ +static im_function eor_vec_desc = { + "im_eorimage_vec", /* Name */ + "bitwise eor of an image with a vector constant", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + eor_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_shiftleft via arg vector. + */ +static int +shiftleft_vec( im_object *argv ) +{ + int n = *((int *) argv[2]); + + return( im_shiftleft( argv[0], argv[1], n ) ); +} + +/* Description of im_shiftleft. + */ +static im_function shiftleft_desc = { + "im_shiftleft", /* Name */ + "shift integer image n bits to left", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + shiftleft_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_shiftright via arg vector. + */ +static int +shiftright_vec( im_object *argv ) +{ + int n = *((int *) argv[2]); + + return( im_shiftright( argv[0], argv[1], n ) ); +} + +/* Description of im_shiftright. + */ +static im_function shiftright_desc = { + "im_shiftright", /* Name */ + "shift integer image n bits to right", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + shiftright_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *bool_list[] = { + &and_desc, + &andconst_desc, + &and_vec_desc, + &or_desc, + &orconst_desc, + &or_vec_desc, + &eor_desc, + &eorconst_desc, + &eor_vec_desc, + &shiftleft_desc, + &shiftright_desc +}; + +/* Package of functions. + */ +im_package im__boolean = { + "boolean", + IM_NUMBER( bool_list ), + bool_list +}; diff --git a/libsrc/boolean/boolean.c b/libsrc/boolean/boolean.c new file mode 100644 index 00000000..1653beb6 --- /dev/null +++ b/libsrc/boolean/boolean.c @@ -0,0 +1,668 @@ +/* @(#) Bitwise operations on VASARI images. Inputs must be some + * @(#) integer type and have the same size and number of bands. Use + * @(#) im_eorconst( in, out, -1 ) for im_not. + * @(#) + * @(#) int im_andimage( a, b, out ) int im_andconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) unsigned char c; + * @(#) + * @(#) int im_orimage( a, b, out ) int im_orconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) unsigned char c; + * @(#) + * @(#) int im_eorimage( a, b, out ) int im_eorconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) unsigned char c; + * @(#) + * @(#) int im_shiftleft( in, out, n ) int im_shiftright( in, out, n ) + * @(#) IMAGE *in, *out; IMAGE *in, *out; + * @(#) int n; int n; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail). + * + * Modified: + * 15/12/94 JC + * - ANSIfied + * - adapted to partials with im_wrap... + * 25/1/95 JC + * - added check1ary(), check2ary() + * 8/2/95 JC + * - new im_wrapmany + * 19/7/95 JC + * - added im_shiftleft() and im_shiftright() + * 6/7/98 JC + * - added _vec forms + * - removed *p++ stuff + * 10/9/99 JC + * - and/or/eor now do all int types + * 10/10/02 JC + * - renamed im_and() etc. as im_andimage() to remove breakage in the C++ + * layer if operator names are turned on + * 30/6/04 + * - now cast float/complex args to int + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Save a bit of typing. + */ +#define UC IM_BANDFMT_UCHAR +#define C IM_BANDFMT_CHAR +#define US IM_BANDFMT_USHORT +#define S IM_BANDFMT_SHORT +#define UI IM_BANDFMT_UINT +#define I IM_BANDFMT_INT +#define F IM_BANDFMT_FLOAT +#define M IM_BANDFMT_COMPLEX +#define D IM_BANDFMT_DOUBLE +#define DM IM_BANDFMT_DPCOMPLEX + +/* Type conversions for boolean. + */ +static int iformat[10][10] = { + /* UC C US S UI I F M D DM */ +/* UC */ { UC, C, US, S, UI, I, I, I, I, I }, +/* C */ { C, C, S, S, I, I, I, I, I, I }, +/* US */ { US, S, US, S, UI, I, I, I, I, I }, +/* S */ { S, S, S, S, I, I, I, I, I, I }, +/* UI */ { UI, I, UI, I, UI, I, I, I, I, I }, +/* I */ { I, I, I, I, I, I, I, I, I, I }, +/* F */ { I, I, I, I, I, I, I, I, I, I }, +/* M */ { I, I, I, I, I, I, I, I, I, I }, +/* D */ { I, I, I, I, I, I, I, I, I, I }, +/* DM */ { I, I, I, I, I, I, I, I, I, I } +}; + +/* Check args. Cast inputs to matching integer format. + */ +static int +check( char *name, IMAGE **in, IMAGE *out ) +{ + int i, n; + + /* Count args. + */ + for( n = 0; in[n]; n++ ) { + if( in[n]->Coding != IM_CODING_NONE ) { + im_errormsg( "%s: uncoded images only", name ); + return( -1 ); + } + } + + /* Check sizes match. + */ + for( i = 1; i < n; i++ ) + if( in[0]->Bands != in[i]->Bands || + in[0]->Xsize != in[i]->Xsize || + in[0]->Ysize != in[i]->Ysize ) { + im_errormsg( "%s: images differ in size", name ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_desc_array( out, in ) ) + return( -1 ); + + /* Calculate type conversion ... just 1ary and 2ary. + */ + switch( n ) { + case 1: + out->BandFmt = iformat[0][in[0]->BandFmt]; + break; + + case 2: + out->BandFmt = iformat[in[1]->BandFmt][in[0]->BandFmt]; + break; + + default: + assert( FALSE ); + } + + for( i = 0; i < n; i++ ) { + IMAGE *t = im_open_local( out, name, "p" ); + + if( !t || im_clip2fmt( in[i], t, out->BandFmt ) ) + return( -1 ); + in[i] = t; + } + + return( 0 ); +} + +/* A selection of main loops. As with im_add(), only implement monotype + * operations. TYPE is some integer type, signed or unsigned. + */ +#define AND2( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + tq[x] = tp1[x] & tp2[x]; \ +} + +#define OR2( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + tq[x] = tp1[x] | tp2[x]; \ +} + +#define EOR2( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + tq[x] = tp1[x] ^ tp2[x]; \ +} + +#define ANDCONST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp = (TYPE *) p; \ + TYPE *tc = (TYPE *) c; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < bands; b++, i++ ) \ + tq[i] = tp[i] & tc[b]; \ +} + +#define ORCONST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp = (TYPE *) p; \ + TYPE *tc = (TYPE *) c; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < bands; b++, i++ ) \ + tq[i] = tp[i] | tc[b]; \ +} + +#define EORCONST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + TYPE *tp = (TYPE *) p; \ + TYPE *tc = (TYPE *) c; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < bands; b++, i++ ) \ + tq[i] = tp[i] ^ tc[b]; \ +} + +/* The above, wrapped up as buffer processing functions. + */ +static void +and_buffer( PEL **p, PEL *q, int n, IMAGE *im ) +{ + int x; + int bands = im->Bands; + int ne = n * bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + + switch( im->BandFmt ) { + case IM_BANDFMT_CHAR: AND2( signed char ); break; + case IM_BANDFMT_UCHAR: AND2( unsigned char ); break; + case IM_BANDFMT_SHORT: AND2( signed short ); break; + case IM_BANDFMT_USHORT: AND2( unsigned short ); break; + case IM_BANDFMT_INT: AND2( signed int ); break; + case IM_BANDFMT_UINT: AND2( unsigned int ); break; + + default: + error_exit( "im_and: internal error" ); + } +} + +static void +or_buffer( PEL **p, PEL *q, int n, IMAGE *in1 ) +{ + int x; + int bands = in1->Bands; + int ne = n * bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + + switch( in1->BandFmt ) { + case IM_BANDFMT_CHAR: OR2( signed char ); break; + case IM_BANDFMT_UCHAR: OR2( unsigned char ); break; + case IM_BANDFMT_SHORT: OR2( signed short ); break; + case IM_BANDFMT_USHORT: OR2( unsigned short ); break; + case IM_BANDFMT_INT: OR2( signed int ); break; + case IM_BANDFMT_UINT: OR2( unsigned int ); break; + + default: + error_exit( "im_or: internal error" ); + } +} + +static void +eor_buffer( PEL **p, PEL *q, int n, IMAGE *in1 ) +{ + int x; + int bands = in1->Bands; + int ne = n * bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + + switch( in1->BandFmt ) { + case IM_BANDFMT_CHAR: EOR2( signed char ); break; + case IM_BANDFMT_UCHAR: EOR2( unsigned char ); break; + case IM_BANDFMT_SHORT: EOR2( signed short ); break; + case IM_BANDFMT_USHORT: EOR2( unsigned short ); break; + case IM_BANDFMT_INT: EOR2( signed int ); break; + case IM_BANDFMT_UINT: EOR2( unsigned int ); break; + + default: + error_exit( "im_eor: internal error" ); + } +} + +static void +andconst_buffer( PEL *p, PEL *q, int n, IMAGE *in, PEL *c ) +{ + int x, i, b; + int bands = in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: ANDCONST( signed char ); break; + case IM_BANDFMT_UCHAR: ANDCONST( unsigned char ); break; + case IM_BANDFMT_SHORT: ANDCONST( signed short ); break; + case IM_BANDFMT_USHORT: ANDCONST( unsigned short ); break; + case IM_BANDFMT_INT: ANDCONST( signed int ); break; + case IM_BANDFMT_UINT: ANDCONST( unsigned int ); break; + + default: + error_exit( "im_andconst: internal error" ); + } +} + +static void +orconst_buffer( PEL *p, PEL *q, int n, IMAGE *in, PEL *c ) +{ + int x, i, b; + int bands = in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: ORCONST( signed char ); break; + case IM_BANDFMT_UCHAR: ORCONST( unsigned char ); break; + case IM_BANDFMT_SHORT: ORCONST( signed short ); break; + case IM_BANDFMT_USHORT: ORCONST( unsigned short ); break; + case IM_BANDFMT_INT: ORCONST( signed int ); break; + case IM_BANDFMT_UINT: ORCONST( unsigned int ); break; + + default: + error_exit( "im_orconst: internal error" ); + } +} + +static void +eorconst_buffer( PEL *p, PEL *q, int n, IMAGE *in, PEL *c ) +{ + int x, i, b; + int bands = in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: EORCONST( signed char ); break; + case IM_BANDFMT_UCHAR: EORCONST( unsigned char ); break; + case IM_BANDFMT_SHORT: EORCONST( signed short ); break; + case IM_BANDFMT_USHORT: EORCONST( unsigned short ); break; + case IM_BANDFMT_INT: EORCONST( signed int ); break; + case IM_BANDFMT_UINT: EORCONST( unsigned int ); break; + + default: + error_exit( "im_eorconst: internal error" ); + } +} + +/* The above, wrapped up as im_*() functions. + */ +int +im_andimage( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check images. + */ + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( check( "im_andimage", invec, out ) ) + return( -1 ); + + /* Process! + */ + if( im_wrapmany( invec, out, (im_wrapmany_fn) and_buffer, out, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_orimage( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( check( "im_orimage", invec, out ) ) + return( -1 ); + + if( im_wrapmany( invec, out, (im_wrapmany_fn) or_buffer, out, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_eorimage( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( check( "im_eorimage", invec, out ) ) + return( -1 ); + + if( im_wrapmany( invec, out, (im_wrapmany_fn) eor_buffer, out, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Cast a vector of double to a vector of TYPE. + */ +#define CAST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + \ + for( i = 0; i < out->Bands; i++ ) \ + tq[i] = (TYPE) p[i]; \ +} + +/* Make a pixel of output type from a realvec. + */ +static PEL * +make_pixel( IMAGE *out, double *p ) +{ + PEL *q; + int i; + + if( !(q = IM_ARRAY( out, IM_IMAGE_SIZEOF_PEL( out ), PEL )) ) + return( NULL ); + + switch( out->BandFmt ) { + case IM_BANDFMT_CHAR: CAST( signed char ); break; + case IM_BANDFMT_UCHAR: CAST( unsigned char ); break; + case IM_BANDFMT_SHORT: CAST( signed short ); break; + case IM_BANDFMT_USHORT: CAST( unsigned short ); break; + case IM_BANDFMT_INT: CAST( signed int ); break; + case IM_BANDFMT_UINT: CAST( unsigned int ); break; + + default: + error_exit( "make_pixel: internal error" ); + } + + return( q ); +} + +int +im_and_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + PEL *cb; + + invec[0] = in; invec[1] = NULL; + if( check( "im_andconst", invec, out ) ) + return( -1 ); + in = invec[0]; + if( n != in->Bands ) { + im_errormsg( "im_and_vec: vec size does not match bands" ); + return( -1 ); + } + if( !(cb = make_pixel( out, c )) ) + return( -1 ); + + if( im_wrapone( in, out, + (im_wrapone_fn) andconst_buffer, (void *) in, (void *) cb ) ) + return( -1 ); + + return( 0 ); +} + +int +im_or_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + PEL *cb; + + invec[0] = in; invec[1] = NULL; + if( check( "im_orconst", invec, out ) ) + return( -1 ); + in = invec[0]; + if( n != in->Bands ) { + im_errormsg( "im_or_vec: vec size does not match bands" ); + return( -1 ); + } + if( !(cb = make_pixel( out, c )) ) + return( -1 ); + + if( im_wrapone( in, out, + (im_wrapone_fn) orconst_buffer, (void *) in, (void *) cb ) ) + return( -1 ); + + return( 0 ); +} + +int +im_eor_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + PEL *cb; + + invec[0] = in; invec[1] = NULL; + if( check( "im_eorconst", invec, out ) ) + return( -1 ); + in = invec[0]; + if( n != in->Bands ) { + im_errormsg( "im_eor_vec: vec size does not match bands" ); + return( -1 ); + } + if( !(cb = make_pixel( out, c )) ) + return( -1 ); + + if( im_wrapone( in, out, + (im_wrapone_fn) eorconst_buffer, (void *) in, (void *) cb ) ) + return( -1 ); + + return( 0 ); +} + +/* Cast a double to a vector of TYPE. + */ +#define CCAST( TYPE ) \ +{ \ + TYPE *tq = (TYPE *) q; \ + \ + for( i = 0; i < in->Bands; i++ ) \ + tq[i] = (TYPE) p; \ +} + +/* Make a pixel of output type from a single double. + */ +static double * +make_pixel_const( IMAGE *in, IMAGE *out, double p ) +{ + double *q; + int i; + + if( !(q = IM_ARRAY( out, in->Bands, double )) ) + return( NULL ); + for( i = 0; i < in->Bands; i++ ) + q[i] = p; + + return( q ); +} + +int +im_andconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v = make_pixel_const( in, out, c ); + + return( !v || im_and_vec( in, out, in->Bands, v ) ); +} + +int +im_orconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v = make_pixel_const( in, out, c ); + + return( !v || im_or_vec( in, out, in->Bands, v ) ); +} + +int +im_eorconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v = make_pixel_const( in, out, c ); + + return( !v || im_eor_vec( in, out, in->Bands, v ) ); +} + +/* Assorted shift operations. + */ +#define SHIFTL( TYPE ) \ +{\ + TYPE *pt = (TYPE *) p;\ + TYPE *qt = (TYPE *) q;\ + \ + for( x = 0; x < ne; x++ )\ + qt[x] = pt[x] << n;\ +} + +#define SHIFTR( TYPE ) \ +{\ + TYPE *pt = (TYPE *) p;\ + TYPE *qt = (TYPE *) q;\ + \ + for( x = 0; x < ne; x++ )\ + qt[x] = pt[x] >> n;\ +} + +/* The above as buffer ops. + */ +static void +shiftleft_buffer( PEL *p, PEL *q, int len, IMAGE *in, int n ) +{ + int x; + int ne = len * in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: SHIFTL(unsigned char); break; + case IM_BANDFMT_CHAR: SHIFTL(signed char); break; + case IM_BANDFMT_USHORT: SHIFTL(unsigned short); break; + case IM_BANDFMT_SHORT: SHIFTL(signed short); break; + case IM_BANDFMT_UINT: SHIFTL(unsigned int); break; + case IM_BANDFMT_INT: SHIFTL(signed int); break; + + default: + error_exit( "im_shiftleft: internal error" ); + /*NOTREACHED*/ + } +} + +static void +shiftright_buffer( PEL *p, PEL *q, int len, IMAGE *in, int n ) +{ + int x; + int ne = len * in->Bands; + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: SHIFTR(unsigned char); break; + case IM_BANDFMT_CHAR: SHIFTR(signed char); break; + case IM_BANDFMT_USHORT: SHIFTR(unsigned short); break; + case IM_BANDFMT_SHORT: SHIFTR(signed short); break; + case IM_BANDFMT_UINT: SHIFTR(unsigned int); break; + case IM_BANDFMT_INT: SHIFTR(signed int); break; + + default: + error_exit( "im_shiftright: internal error" ); + /*NOTREACHED*/ + } +} + +/* The above as im_*() functions. + */ +int +im_shiftleft( IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *invec[2]; + + invec[0] = in; invec[1] = NULL; + if( check( "im_shiftleft", invec, out ) ) + return( -1 ); + in = invec[0]; + + if( im_wrapone( in, out, + (im_wrapone_fn) shiftleft_buffer, in, GINT_TO_POINTER( n ) ) ) + return( -1 ); + + return( 0 ); +} + +int +im_shiftright( IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *invec[2]; + + invec[0] = in; invec[1] = NULL; + if( check( "im_shiftleft", invec, out ) ) + return( -1 ); + in = invec[0]; + + if( im_wrapone( in, out, + (im_wrapone_fn) shiftright_buffer, in, GINT_TO_POINTER( n ) ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/boolean/man3/Makefile.am b/libsrc/boolean/man3/Makefile.am new file mode 100644 index 00000000..9ddfef43 --- /dev/null +++ b/libsrc/boolean/man3/Makefile.am @@ -0,0 +1,16 @@ +man_MANS = \ + im_andimage.3 \ + im_andconst.3 \ + im_eorimage.3 \ + im_eorconst.3 \ + im_orimage.3 \ + im_orconst.3 \ + im_shiftleft.3 \ + im_shiftright.3 \ + im_eor_vec.3 \ + im_or_vec.3 \ + im_and_vec.3 + + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/boolean/man3/im_and_vec.3 b/libsrc/boolean/man3/im_and_vec.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_and_vec.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_andconst.3 b/libsrc/boolean/man3/im_andconst.3 new file mode 100644 index 00000000..46ff7aff --- /dev/null +++ b/libsrc/boolean/man3/im_andconst.3 @@ -0,0 +1 @@ +.so man3/im_andimage.3 diff --git a/libsrc/boolean/man3/im_andimage.3 b/libsrc/boolean/man3/im_andimage.3 new file mode 100644 index 00000000..63ad74d1 --- /dev/null +++ b/libsrc/boolean/man3/im_andimage.3 @@ -0,0 +1,91 @@ +.TH IM_ANDIMAGE 3 "30 October 1992" +.SH NAME +im_andimage, im_andconst, im_and_vec, im_orimage, im_orconst, im_or_vec, +im_eorimage, im_eorconst, im_eor_vec \- boolean +operations on unsigned char images +.SH SYNOPSIS +.B #include + +.B int im_andimage(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_andconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_and_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_orimage(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_orconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_or_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_eorimage(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_eorconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_eor_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.SH DESCRIPTION +Perform bitwise logical operations on integer images. + +.B im_andimage(3) +performs bitwise and between corresponding pixels in a and b, writing the +result to out. Both images must be the same size and have the same number +of bands. They can have any integer type. + +.B im_andconst(3) +performs bitwise and between pixels in a and a single +constant value. +.B im_and_vec(3) +lets you specify n constants, one per band. + +.B im_orimage(3) +and +.B im_eorimage(3) +behave similarly. Use im_eorconst( in, out, -1 ) or +.B im_invert(3) +as a not operator. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_ifthenelse(3), im_equal(3). +.SH COPYRIGHT +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/boolean/man3/im_eor_vec.3 b/libsrc/boolean/man3/im_eor_vec.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_eor_vec.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_eorconst.3 b/libsrc/boolean/man3/im_eorconst.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_eorconst.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_eorimage.3 b/libsrc/boolean/man3/im_eorimage.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_eorimage.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_or_vec.3 b/libsrc/boolean/man3/im_or_vec.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_or_vec.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_orconst.3 b/libsrc/boolean/man3/im_orconst.3 new file mode 100644 index 00000000..dfcd881e --- /dev/null +++ b/libsrc/boolean/man3/im_orconst.3 @@ -0,0 +1,2 @@ +.so man3/im_andimage.3 + diff --git a/libsrc/boolean/man3/im_orimage.3 b/libsrc/boolean/man3/im_orimage.3 new file mode 100644 index 00000000..46ff7aff --- /dev/null +++ b/libsrc/boolean/man3/im_orimage.3 @@ -0,0 +1 @@ +.so man3/im_andimage.3 diff --git a/libsrc/boolean/man3/im_shiftleft.3 b/libsrc/boolean/man3/im_shiftleft.3 new file mode 100644 index 00000000..711f6093 --- /dev/null +++ b/libsrc/boolean/man3/im_shiftleft.3 @@ -0,0 +1,30 @@ +.TH SHIFT 3 "30 October 1992" +.SH NAME +im_shiftleft, im_shiftright \- bit shift operations +.SH SYNOPSIS +.B #include + +.B int im_shiftleft( in, out, n ) +.br +.B IMAGE *in, *out; +.br +.B int n; + +.B int im_shiftright( in, out, n ) +.br +.B IMAGE *in, *out; +.br +.B int n; + +.SH DESCRIPTION +Shift integer images n bits left or right. Shifts on signed images are +sign-preserving. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_ifthenelse(3), im_equal(3), im_andimage(3). +.SH COPYRIGHT +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/boolean/man3/im_shiftright.3 b/libsrc/boolean/man3/im_shiftright.3 new file mode 100644 index 00000000..3551b625 --- /dev/null +++ b/libsrc/boolean/man3/im_shiftright.3 @@ -0,0 +1 @@ +.so man3/im_shiftleft.3 diff --git a/libsrc/colour/Makefile.am b/libsrc/colour/Makefile.am new file mode 100644 index 00000000..0d10d472 --- /dev/null +++ b/libsrc/colour/Makefile.am @@ -0,0 +1,32 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libcolour.la + +libcolour_la_SOURCES = \ + colour.c \ + colour_dispatch.c \ + derived.c \ + im_icc_transform.c \ + im_LCh2Lab.c \ + im_LCh2UCS.c \ + im_Lab2LCh.c \ + im_Lab2LabQ.c \ + im_Lab2LabS.c \ + im_Lab2XYZ.c \ + im_LabQ2Lab.c \ + im_LabQ2LabS.c \ + im_LabQ2disp.c \ + im_LabS2LabQ.c \ + im_LabS2Lab.c \ + im_lab_morph.c \ + im_UCS2LCh.c \ + im_XYZ2Lab.c \ + im_XYZ2Yxy.c \ + im_Yxy2XYZ.c \ + im_XYZ2disp.c \ + im_dE00_fromLab.c \ + im_dECMC_fromLab.c \ + im_dE_fromLab.c \ + im_disp2XYZ.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/colour/colour.c b/libsrc/colour/colour.c new file mode 100644 index 00000000..158a2897 --- /dev/null +++ b/libsrc/colour/colour.c @@ -0,0 +1,1118 @@ +/* Convert colours in various ways. + * Written: January 1990 + * Modified .. innumerable times + * Code by: DS, JC, J-Ph.L. + * 18/7/93 JC + * - final tidies before v7 release + * - ANSIfied + * - code for samples removed + * 5/5/94 JC + * - nint() -> rint() to make ANSI easier + * 14/3/96 JC + * - new display characterisation + * - speed-up to im_col_XYZ2rgb() and im_col_rgb2XYZ() + * 4/3/98 JC + * - new display profile for ultra2 + * - new sRGB profile + * 17/8/98 JC + * - error_exit() removed, now clips + * 26/11/03 Andrey Kiselev + * - tiny clean-up for calcul_tables() + * - some reformatting + * 23/7/07 + * - tiny cleanup for make_hI() prevents cond jump on ui in valgrind + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Values for IM_TYPE_sRGB. + */ +static struct im_col_display srgb_profile = { + "sRGB", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { 3.2410, -1.5374, -0.4986 }, + { -0.9692, 1.8760, 0.0416 }, + { 0.0556, -0.2040, 1.0570 } + }, + + 80.0, /* Luminosity of reference white */ + .3127, .3291, /* x, y for reference white */ + 100, 100, 100, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 1, 1, 1, /* Residual light o/p for black pixel */ + 2.4, 2.4, 2.4, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for my Ultra2, 20/2/98. Figures from a Minolta CA-100 CRT analyser. + * Contrast at max, brightness at 42, room lights out. + */ +static struct im_col_display ultra2 = { + "ultra2-20/2/98", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .704, -0.302, -.103 }, + { -.708, 1.317, .032 }, + { .005, -.015, .071 } + }, + + 64.0, /* Luminosity of reference white */ + .2137, .3291, /* x, y for reference white */ + 14.4, 44.0, 5.4, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.03, 0.03, 0.03, /* Residual light o/p for black pixel */ + 2.5, 2.5, 2.4, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for our display. These were obtained with a TV analyser in late + * Feb. 1990. The reference white is simply r=g=b=255. + */ +static struct im_col_display im_col_screen_white = { + "Screen", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .660, -0.276, -.10 }, + { -.663, 1.293, .0265 }, + { .003, -.017, .0734 } + }, + + 58.7, /* Luminosity of reference white */ + .284, .273, /* x, y for reference white */ + 14.2, 38.4, 6.1, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.0, 0.0, 0.0, /* Residual light o/p for black pixel */ + 2.8, 2.9, 2.9, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Adjusted version of above for SPARCstation2 screens. Turn down the gamma + * to make blacks blacker. + */ +static struct im_col_display im_col_SPARC_white = { + "SPARC", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .660, -0.276, -.10 }, + { -.663, 1.293, .0265 }, + { .003, -.017, .0734 } + }, + + 58.7, /* Luminosity of reference white */ + .284, .273, /* x, y for reference white */ + 14.2, 38.4, 4, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.0, 0.0, 0.0, /* Residual light o/p for black pixel */ + 2.0, 2.0, 2.0, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for D65 white. This gives a smaller range of colours than + * screen_white. + */ +static struct im_col_display im_col_D65_white = { + "D65", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .660, -0.276, -.10 }, + { -.663, 1.293, .0265 }, + { .003, -.017, .0734 } + }, + + 49.9, /* Luminosity of reference white */ + .3127, .3290, /* x, y for reference white */ + 11.6, 35.0, 3.3, /* Light o/p for reference white */ + 241, 255, 177, /* Pixel values for ref. white */ + 0.1, 0.1, 0.1, /* Residual light o/p for black pixel */ + 2.8, 2.9, 2.7, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for Barco calibrator monitor + */ +static struct im_col_display im_col_barco_white = { + "Barco", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .749, -0.322, -.123 }, + { -.755, 1.341, .033 }, + { .007, -.019, .0898 } + }, + + 80.0, /* Luminosity of reference white */ + .3128, .3292, /* x, y for reference white */ + 20.45, 52.73, 6.81, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.02, 0.053, 0.007, /* Residual light o/p for black pixel */ + 2.23, 2.13, 2.12, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Values for Mitsubishi dye-sub colour printer. + */ +static struct im_col_display im_col_mitsubishi = { + "Mitsubishi_3_colour", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { 1.1997, -0.6296, -0.2755 }, + { -1.1529, 1.7383, -0.1074 }, + { -0.047, -0.109, 0.3829 } + }, + + 95, /* Luminosity of reference white */ + .3152, .3316, /* x, y for reference white */ + 25.33, 42.57, 15.85, /* Y all red, Y all green, Y all blue */ + 255, 255, 255, /* Pixel values for ref. white */ + 1.0, 1.0, 1.0, /* Residual light o/p for black pixel */ + 1.0, 1.0, 1.0, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +/* Display LAB of 100, 0, 0 as 255, 255, 255. + */ +static struct im_col_display im_col_relative = { + "relative", + DISP_DUMB, + { /* XYZ -> luminance matrix */ + { .660, -0.276, -.10 }, + { -.663, 1.293, .0265 }, + { .003, -.017, .0734 } + }, + + 100.0, /* Luminosity of reference white */ + .284, .273, /* x, y for reference white */ + 24.23, 69.20, 6.57, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 0.0, 0.0, 0.0, /* Residual light o/p for black pixel */ + 2.3, 2.3, 2.3, /* Gamma values for the three guns */ + 100, /* 'Background' (like brightness) */ + 100 /* 'Picture' (like contrast) */ +}; + +struct im_col_display * +im_col_displays( int n ) +{ + static struct im_col_display *displays[] = { + &im_col_screen_white, /* index 0 */ + &im_col_SPARC_white, /* index 1 */ + &im_col_D65_white, /* index 2 */ + &im_col_barco_white, /* index 3 */ + &im_col_mitsubishi, /* index 4 */ + &im_col_relative, /* index 5 */ + &ultra2, /* index 6 */ + &srgb_profile, /* index 7 */ + NULL + }; + + if( n < 0 || n > IM_NUMBER( displays ) ) + return( NULL ); + + return( displays[n] ); +} + +struct im_col_display * +im_col_display_name( const char *name ) +{ + int i; + struct im_col_display *d; + + for( i = 0; (d = im_col_displays( i )); i++ ) + if( g_ascii_strcasecmp( d->d_name, name ) == 0 ) + return( d ); + + return( NULL ); +} + +/* Have the tables been made? + */ +static int made_ucs_tables = 0; + +/* Arrays for lookup tables. + */ +static float LI[ 1001 ]; +static float CI[ 3001 ]; +static float hI[ 101 ][ 361 ]; + +/* Calculate Ch from ab, h in degrees. + */ +void +im_col_ab2Ch( float a, float b, float *C, float *h ) +{ + float in[3], out[3]; + + in[1] = a; + in[2] = b; + imb_Lab2LCh( in, out, 1 ); + *C = out[1]; + *h = out[2]; +} + +/* Calculate ab from Ch, h in degrees. + */ +void +im_col_Ch2ab( float C, float h, float *a, float *b ) +{ + float in[3], out[3]; + + in[1] = C; + in[2] = h; + imb_LCh2Lab( in, out, 1 ); + *a = out[1]; + *b = out[2]; +} + +/* Calculate Lab from XYZ. + */ +void +im_col_XYZ2Lab( float X, float Y, float Z, float *L, float *a, float *b ) +{ + float in[3], out[3]; + im_colour_temperature temp; + + in[0] = X; + in[1] = Y; + in[2] = Z; + temp.X0 = IM_D65_X0; + temp.Y0 = IM_D65_Y0; + temp.Z0 = IM_D65_Z0; + imb_XYZ2Lab( in, out, 1, &temp ); + *L = out[0]; + *a = out[1]; + *b = out[2]; +} + +/* Calculate XYZ from Lab. + */ +void +im_col_Lab2XYZ( float L, float a, float b, float *X, float *Y, float *Z ) +{ + float in[3], out[3]; + im_colour_temperature temp; + + in[0] = L; + in[1] = a; + in[2] = b; + temp.X0 = IM_D65_X0; + temp.Y0 = IM_D65_Y0; + temp.Z0 = IM_D65_Z0; + imb_Lab2XYZ( in, out, 1, &temp ); + *X = out[0]; + *Y = out[1]; + *Z = out[2]; +} + +/* Pythagorean distance between two points in colour space. Lab/XYZ/UCS etc. + */ +float +im_col_pythagoras( float L1, float a1, float b1, float L2, float a2, float b2 ) +{ + float dL = L1 - L2; + float da = a1 - a2; + float db = b1 - b2; + + return( sqrt( dL*dL + da*da + db*db ) ); +} + +/* Make look_up tables for the Yr,Yb,Yg <=> r,g,b conversions. + */ +static void +calcul_tables( struct im_col_display *d, struct im_col_tab_disp *table ) +{ + int i; + float a, ga_i, ga, c, f, yo, p; + float maxr, maxg, maxb; + + c = (d->d_B - 100.0) / 500.0; + + /**** Red ****/ + yo = d->d_Y0R; + a = d->d_YCR - yo; + ga = d->d_gammaR; + ga_i = 1.0 / ga; + p = d->d_P / 100.0; + f = d->d_Vrwr / p; + + maxr = (float) d->d_Vrwr; + table->ristep = maxr / 1500.0; + table->rstep = a / 1500.0; + + for( i = 0; i < 1501; i++ ) + table->t_Yr2r[i] = f * (pow( i * table->rstep / a, ga_i ) - c); + + for( i = 0; i < 1501; i++ ) + table->t_r2Yr[i] = yo + + a * pow( i * table->ristep / f + c, ga ); + + /**** Green ****/ + yo = d->d_Y0G; + a = d->d_YCG - yo; + ga = d->d_gammaG; + ga_i = 1.0 / ga; + p = d->d_P / 100.0; + f = d->d_Vrwg / p; + + maxg = (float)d->d_Vrwg; + table->gistep = maxg / 1500.0; + table->gstep = a / 1500.0; + + for( i = 0; i < 1501; i++ ) + table->t_Yg2g[i] = f * (pow( i * table->gstep / a, ga_i ) - c); + + for( i = 0; i < 1501; i++ ) + table->t_g2Yg[i] = yo + + a * pow( i * table->gistep / f + c, ga ); + + /**** Blue ****/ + yo = d->d_Y0B; + a = d->d_YCB - yo; + ga = d->d_gammaB; + ga_i = 1.0 / ga; + p = d->d_P / 100.0; + f = d->d_Vrwb / p; + + maxb = (float)d->d_Vrwb; + table->bistep = maxb / 1500.0; + table->bstep = a / 1500.0; + + for( i = 0; i < 1501; i++ ) + table->t_Yb2b[i] = f * (pow( i * table->bstep / a, ga_i ) - c); + + for( i = 0; i < 1501; i++ ) + table->t_b2Yb[i] = yo + + a * pow( i * table->bistep / f + c, ga ); +} + +/* Make the lookup tables for rgb. Pass an IMAGE to allocate memory from. + */ +struct im_col_tab_disp * +im_col_make_tables_RGB( IMAGE *im, struct im_col_display *d ) +{ + struct im_col_tab_disp *table; + double **temp; + int i, j; + + if( !(table = IM_NEW( im, struct im_col_tab_disp )) ) + return( NULL ); + + if( d->d_type == DISP_DUMB ) + calcul_tables( d, table ); + + if( !(temp = im_dmat_alloc( 0, 2, 0, 2 )) ) + return( NULL ); + + for( i = 0; i < 3; i++ ) + for( j = 0; j < 3; j++ ) { + table->mat_XYZ2lum[i][j] = d->d_mat[i][j]; + temp[i][j] = d->d_mat[i][j]; + } + + if( im_invmat( temp, 3 ) ) { + im_free_dmat( temp, 0, 2, 0, 2 ); + return( NULL ); + } + + for( i = 0; i < 3; i++ ) + for( j = 0; j < 3; j++ ) + table->mat_lum2XYZ[i][j] = temp[i][j]; + + im_free_dmat( temp, 0, 2, 0, 2 ); + + return( table ); +} + +/* Computes the transform: r,g,b => Yr,Yg,Yb. It finds Y values in + * lookup tables and calculates X, Y, Z. + */ +int +im_col_rgb2XYZ( struct im_col_display *d, struct im_col_tab_disp *table, + int r, int g, int b, float *X, float *Y, float *Z ) +{ + float Yr, Yg, Yb; + float *mat = &table->mat_lum2XYZ[0][0]; + int i; + + if( r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 ) { + im_errormsg( "im_col_rgb2XYZ: out of range [0,255]" ); + return( -1 ); + } + + switch( d->d_type ) { + case DISP_DUMB: + /* Convert rgb to Yr, Yg, Yb. 3 times: r, g, b. + */ + i = r / table->ristep; + Yr = table->t_r2Yr[i]; + + i = g / table->gistep; + Yg = table->t_g2Yg[i]; + + i = b / table->bistep; + Yb = table->t_b2Yb[i]; + + break; + + case DISP_BARCO: + Yr = d->d_Y0R + r*(d->d_YCR-d->d_Y0R)/255.0; + Yg = d->d_Y0G + g*(d->d_YCG-d->d_Y0G)/255.0; + Yb = d->d_Y0B + b*(d->d_YCB-d->d_Y0B)/255.0; + break; + + default: + im_errormsg( "im_col_rgb2XYZ: bad display type" ); + return( -1 ); + } + + /* Multiply through the inverse matrix to get XYZ values. + */ + *X = mat[0] * Yr + mat[1] * Yg + mat[2] * Yb; + *Y = mat[3] * Yr + mat[4] * Yg + mat[5] * Yb; + *Z = mat[6] * Yr + mat[7] * Yg + mat[8] * Yb; + + return( 0 ); +} + +/* Turn XYZ into display colour. Return or=1 for out of gamut - rgb will + * contain an approximation of the right colour. + */ +int +im_col_XYZ2rgb( struct im_col_display *d, struct im_col_tab_disp *table, + float X, float Y, float Z, + int *r_ret, int *g_ret, int *b_ret, + int *or_ret ) +{ + float *mat = &table->mat_XYZ2lum[0][0]; + int or = 0; /* Out of range flag */ + + float Yr, Yg, Yb; + int Yint; + int r, g, b; + + /* Multiply through the matrix to get luminosity values. + */ + Yr = mat[0] * X + mat[1] * Y + mat[2] * Z; + Yg = mat[3] * X + mat[4] * Y + mat[5] * Z; + Yb = mat[6] * X + mat[7] * Y + mat[8] * Z; + + /* Any negatives? If yes, set the out-of-range flag and bump up. + */ + if( Yr < d->d_Y0R ) { + or = 1; + Yr = d->d_Y0R; + } + if( Yg < d->d_Y0G ) { + or = 1; + Yg = d->d_Y0G; + } + if( Yb < d->d_Y0B ) { + or = 1; + Yb = d->d_Y0B; + } + + /* Work out colour value (0-Vrw) to feed the tube to get that + * luminosity. Easy for BARCOs, harder for others. + */ + switch( d->d_type ) { + case DISP_DUMB: + Yint = (Yr - d->d_Y0R) / table->rstep; + if( Yint > 1500 ) { + or = 1; + Yint = 1500; + } + r = IM_RINT( table->t_Yr2r[Yint] ); + + Yint = (Yg - d->d_Y0G) / table->gstep; + if( Yint > 1500 ) { + or = 1; + Yint = 1500; + } + g = IM_RINT( table->t_Yg2g[Yint] ); + + Yint = (Yb - d->d_Y0B) / table->bstep; + if( Yint > 1500 ) { + or = 1; + Yint = 1500; + } + b = IM_RINT( table->t_Yb2b[Yint] ); + + break; + + case DISP_BARCO: + r = IM_RINT( ((Yr - d->d_Y0R) / (d->d_YCR - d->d_Y0R)) * 255 ); + g = IM_RINT( ((Yg - d->d_Y0G) / (d->d_YCG - d->d_Y0G)) * 255 ); + b = IM_RINT( ((Yb - d->d_Y0B) / (d->d_YCB - d->d_Y0B)) * 255 ); + + /* Any silly values? Set out of range and adjust. + */ + if( r > d->d_Vrwr ) { + or = 1; + r = d->d_Vrwr; + } + if( g > d->d_Vrwg ) { + or = 1; + g = d->d_Vrwg; + } + if( b > d->d_Vrwb ) { + or = 1; + b = d->d_Vrwb; + } + if( r < 0 ) { + or = 1; + r = 0; + } + if( g < 0 ) { + or = 1; + g = 0; + } + if( b < 0 ) { + or = 1; + b = 0; + } + + break; + + default: + im_errormsg("XYZ2rgb: display unknown"); + return( -1 ); + /*NOTREACHED*/ + } + + *r_ret = r; + *g_ret = g; + *b_ret = b; + + *or_ret = or; + + return( 0 ); +} + +/* Functions to convert from Lab to uniform colour space and back. + */ + +/* Constants for Lucs. + */ +#define c1 21.75 +#define c2 0.3838 +#define c3 38.54 + +/* Calculate Lucs from L. + */ +float +im_col_L2Lucs( float L ) +{ + float Lucs; + + if( L >= 16.0 ) + Lucs = (c1 * log( L ) + c2 * L - c3); + else + Lucs = 1.744 * L; + + return( Lucs ); +} + +/* Generate Ll and LI (inverse) tables. Don't call the above for speed. + */ +static void +make_LI( void ) +{ + int i, j=0; + float L, Ll[ 1001 ]; + + for( i = 0; i < 1001; i++ ) + { + L = i / 10.0; + if( L >= 16.0 ) + Ll[ i ] = (c1 * log( L ) + c2 * L - c3); + else + Ll[ i ] = 1.744 * L; + } + + for( i = 0; i < 1001; i++ ) + { + while ( (Ll[j]<=i/10.0) && ( j<1001) ) j++; + LI[i] = (j-1)/10.0 + (i/10.0-Ll[j-1]) / ((Ll[j]-Ll[j-1])*10.0); + } +} + + +/* Inverse of above using table. + */ +float +im_col_Lucs2L( float Lucs ) +{ + int known; /* nearest input value in the table, <= Lucs */ + + known = floor(Lucs*10.0); + if( known < 0 ) + known = 0; + if( known > 1000 ) + known = 1000; + + return( LI[known] + (LI[known+1]-LI[known])*(Lucs*10.0-known) ); +} + +/* Constants for Cucs. + */ +#define c4 0.162 +#define c5 10.92 +#define c6 0.638 +#define c7 0.07216 +#define c8 4.907 + +/* Calculate Cucs from C. + */ +float +im_col_C2Cucs( float C ) +{ + float Cucs; + + Cucs = (c4 * C + c5 * (log( c6 + c7 * C )) + c8); + if ( Cucs<0 ) Cucs = 0; + + return( Cucs ); +} + +/* Generate Cucs table. Again, inline the code above. + */ +static void +make_CI( void ) +{ + int i, j=0; + float C, Cl[ 3001]; + + for( i = 0; i < 3001; i++ ) { + C = i / 10.0; + Cl[ i ] = (c4 * C + c5 * (log( c6 + c7 * C )) + c8); + } + + for( i = 0; i < 3001; i++ ) + { + while ( (Cl[j]<=i/10.0) && ( j<3001) ) j++; + CI[i] = (j-1)/10.0 + (i/10.0-Cl[j-1]) / ((Cl[j]-Cl[j-1])*10.0); + } + +} + +/* Inverse of above using table. + */ +float +im_col_Cucs2C( float Cucs ) +{ + int known; /* nearest input value in the table, <= Cucs */ + + known = floor(Cucs*10.0); + if( known < 0 ) + known = 0; + if( known > 3000 ) + known = 3000; + + return( CI[known] + (CI[known+1]-CI[known])*(Cucs*10.0-known) ); +} + +/* Calculate hucs from h and C. + */ +float +im_col_Ch2hucs( float C, float h ) +{ + float P, D, f, g; + float k4, k5, k6, k7, k8; + float hucs; + + if( h < 49.1 ) { + k4 = 133.87; + k5 = -134.5; + k6 = -.924; + k7 = 1.727; + k8 = 340.0; + } + else if( h < 110.1 ) { + k4 = 11.78; + k5 = -12.7; + k6 = -.218; + k7 = 2.12; + k8 = 333.0; + } + else if( h < 269.6 ) { + k4 = 13.87; + k5 = 10.93; + k6 = 0.14; + k7 = 1.0; + k8 = -83.0; + } + else { + k4 = .14; + k5 = 5.23; + k6 = .17; + k7 = 1.61; + k8 = 233.0; + } + + P = cos( IM_RAD( k8 + k7 * h ) ); + D = k4 + k5 * P * pow( fabs( P ), k6 ); + g = C * C * C * C; + f = sqrt( g / (g + 1900.0) ); + hucs = h + D * f; + + return( hucs ); +} + +/* The difficult one: hucs. Again, inline. + */ +static void +make_hI( void ) +{ + int i, j, k; + float P, D, C, f, hl[101][361]; + float k4, k5, k6, k7, k8; + + for( i = 0; i < 361; i++ ) { + if( i < 49.1 ) { + k4 = 133.87; + k5 = -134.5; + k6 = -.924; + k7 = 1.727; + k8 = 340.0; + } + else if( i < 110.1 ) { + k4 = 11.78; + k5 = -12.7; + k6 = -.218; + k7 = 2.12; + k8 = 333.0; + } + else if( i < 269.6 ) { + k4 = 13.87; + k5 = 10.93; + k6 = 0.14; + k7 = 1.0; + k8 = -83.0; + } + else { + k4 = .14; + k5 = 5.23; + k6 = .17; + k7 = 1.61; + k8 = 233.0; + } + + P = cos( IM_RAD( k8 + k7 * i ) ); + D = k4 + k5 * P * pow( fabs( P ), k6 ); + + for( j = 0; j < 101; j++ ) { + float g; + + C = j * 2.0; + g = C * C * C * C; + f = sqrt( g / (g + 1900.0) ); + + hl[j][i] = i + D * f; + } + + } + + for( j = 0; j < 101; j++ ) { + k = 0; + for( i = 0; i < 361; i++ ) { + while( k < 361 && hl[j][k] <= i ) + k++; + hI[j][i] = k - 1 + (i - hl[j][k - 1]) / + (hl[j][k] - hl[j][k - 1]); + } + } +} + +/* Inverse of above using table. + */ +float +im_col_Chucs2h( float C, float hucs ) +{ + int r, known; /* nearest input value in the table, <= hucs */ + + /* Which row of the table? + */ + r = (int) ((C + 1.0) / 2.0); + if( r < 0 ) + r = 0; + if( r > 100 ) + r = 100; + + known = floor(hucs); + if( known < 0 ) + known = 0; + if( known > 360 ) + known = 360; + + return( hI[r][known] + (hI[r][known+1]-hI[r][known])*(hucs-known) ); +} + +/* Make the lookup tables for ucs. + */ +void +im_col_make_tables_UCS( void ) +{ + if( !made_ucs_tables ) { + make_LI(); + make_CI(); + make_hI(); + made_ucs_tables = -1; + } +} + +/* CMC colour difference using the above. + */ +float +im_col_dECMC( float L1, float a1, float b1, + float L2, float a2, float b2 ) +{ + float h1, C1; + float h2, C2; + float Lucs1, Cucs1, hucs1; + float Lucs2, Cucs2, hucs2; + float aucs1, bucs1; + float aucs2, bucs2; + + /* Turn to LCh. + */ + im_col_ab2Ch( a1, b1, &C1, &h1 ); + im_col_ab2Ch( a2, b2, &C2, &h2 ); + + /* Turn to LCh in CMC space. + */ + Lucs1 = im_col_L2Lucs( L1 ); + Cucs1 = im_col_C2Cucs( C1 ); + hucs1 = im_col_Ch2hucs( C1, h1 ); + + Lucs2 = im_col_L2Lucs( L2 ); + Cucs2 = im_col_C2Cucs( C2 ); + hucs2 = im_col_Ch2hucs( C2, h2 ); + + /* Turn to Lab in CMC space. + */ + im_col_Ch2ab( Cucs1, hucs1, &aucs1, &bucs1 ); + im_col_Ch2ab( Cucs2, hucs2, &aucs2, &bucs2 ); + + /* Find difference. + */ + return( im_col_pythagoras( Lucs1, aucs1, bucs1, Lucs2, aucs2, bucs2 ) ); +} + +/* Find h in degrees from a/b. + */ +double +im_col_ab2h( double a, double b ) +{ + double h; + + /* We have to be careful we have the right quadrant! + */ + if( a == 0 ) { + if( b < 0.0 ) + h = 270; + else if( b == 0.0 ) + h = 0; + else + h = 90; + } + else { + double t = atan( b / a ); + + if( a > 0.0 ) + if( b < 0.0 ) + h = IM_DEG( t + IM_PI * 2.0 ); + else + h = IM_DEG( t ); + else + h = IM_DEG( t + IM_PI ); + } + + return( h ); +} + +/* CIEDE2000 ... from + +Luo, Cui, Rigg, "The Development of the CIE 2000 Colour-Difference +Formula: CIEDE2000", COLOR research and application, pp 340 + + */ +float +im_col_dE00( float L1, float a1, float b1, + float L2, float a2, float b2 ) +{ +/* Code if you want XYZ params and the colour temp used in the reference + + float + im_col_dE00( float X1, float Y1, float Z1, + float X2, float Y2, float Z2 ) + { + const double X0 = 94.811; + const double Y0 = 100.0; + const double Z0 = 107.304; + +#define f(I) ((I) > 0.008856 ? \ + cbrt( (I), 1.0 / 3.0 ) : 7.7871 * (I) + (16.0 / 116.0)) + + double nX1 = f( X1 / X0 ); + double nY1 = f( Y1 / Y0 ); + double nZ1 = f( Z1 / Z0 ); + + double L1 = 116 * nY1 - 16; + double a1 = 500 * (nX1 - nY1); + double b1 = 200 * (nY1 - nZ1); + + double nX2 = f( X2 / X0 ); + double nY2 = f( Y2 / Y0 ); + double nZ2 = f( Z2 / Z0 ); + + double L2 = 116 * nY2 - 16; + double a2 = 500 * (nX2 - nY2); + double b2 = 200 * (nY2 - nZ2); + */ + + /* Chroma and mean chroma (C bar) + */ + double C1 = sqrt( a1 * a1 + b1 * b1 ); + double C2 = sqrt( a2 * a2 + b2 * b2 ); + double Cb = (C1 + C2) / 2; + + /* G + */ + double Cb7 = Cb * Cb * Cb * Cb * Cb * Cb * Cb; + double G = 0.5 * (1 - sqrt( Cb7 / (Cb7 + pow( 25, 7 )) )); + + /* L', a', b', C', h' + */ + double L1d = L1; + double a1d = (1 + G) * a1; + double b1d = b1; + double C1d = sqrt( a1d * a1d + b1d * b1d ); + double h1d = im_col_ab2h( a1d, b1d ); + + double L2d = L2; + double a2d = (1 + G) * a2; + double b2d = b2; + double C2d = sqrt( a2d * a2d + b2d * b2d ); + double h2d = im_col_ab2h( a2d, b2d ); + + /* L' bar, C' bar, h' bar + */ + double Ldb = (L1d + L2d) / 2; + double Cdb = (C1d + C2d) / 2; + double hdb = fabs( h1d - h2d ) < 180 ? + (h1d + h2d) / 2 : + fabs( h1d + h2d - 360 ) / 2; + + /* dtheta, RC + */ + double hdbd = (hdb - 275) / 25; + double dtheta = 30 * exp( -(hdbd * hdbd) ); + double Cdb7 = Cdb * Cdb * Cdb * Cdb * Cdb * Cdb * Cdb; + double RC = 2 * sqrt( Cdb7 / (Cdb7 + pow( 25, 7 )) ); + + /* RT, T. + */ + double RT = -sin( IM_RAD( 2 * dtheta ) ) * RC; + double T = 1 - + 0.17 * cos( IM_RAD( hdb - 30 ) ) + + 0.24 * cos( IM_RAD( 2 * hdb ) ) + + 0.32 * cos( IM_RAD( 3 * hdb + 6 ) ) - + 0.20 * cos( IM_RAD( 4 * hdb - 63 ) ); + + /* SL, SC, SH + */ + double Ldb50 = Ldb - 50; + double SL = 1 + (0.015 * Ldb50 * Ldb50) / sqrt( 20 + Ldb50 * Ldb50); + double SC = 1 + 0.045 * Cdb; + double SH = 1 + 0.015 * Cdb * T; + + /* hue difference ... careful! + */ + double dhd = fabs( h1d - h2d ) < 180 ? + h1d - h2d : + 360 - (h1d - h2d); + + /* dLd, dCd dHd + */ + double dLd = L1d - L2d; + double dCd = C1d - C2d; + double dHd = 2 * sqrt( C1d * C2d ) * sin( IM_RAD( dhd / 2 ) ); + + /* Parametric factors for viewing parameters. + */ + const double kL = 1.0; + const double kC = 1.0; + const double kH = 1.0; + + /* Normalised terms. + */ + double nL = dLd / (kL * SL); + double nC = dCd / (kC * SC); + double nH = dHd / (kH * SH); + + /* dE00!! + */ + double dE00 = sqrt( nL * nL + nC * nC + nH * nH + RT * nC * nH ); + + /* + printf( "X1 = %g, Y1 = %g, Z1 = %g\n", X1, Y1, Z1 ); + printf( "X2 = %g, Y2 = %g, Z2 = %g\n", X2, Y2, Z2 ); + printf( "L1 = %g, a1 = %g, b1 = %g\n", L1, a1, b1 ); + printf( "L2 = %g, a2 = %g, b2 = %g\n", L2, a2, b2 ); + printf( "L1d = %g, a1d = %g, b1d = %g, C1d = %g, h1d = %g\n", + L1d, a1d, b1d, C1d, h1d ); + printf( "L2d = %g, a2d = %g, b2d = %g, C2d = %g, h2d = %g\n", + L2d, a2d, b2d, C2d, h2d ); + printf( "G = %g, T = %g, SL = %g, SC = %g, SH = %g, RT = %g\n", + G, T, SL, SC, SH, RT ); + printf( "dE00 = %g\n", dE00 ); + */ + + return( dE00 ); +} + diff --git a/libsrc/colour/colour_dispatch.c b/libsrc/colour/colour_dispatch.c new file mode 100644 index 00000000..032993de --- /dev/null +++ b/libsrc/colour/colour_dispatch.c @@ -0,0 +1,1012 @@ +/* Function dispatch tables for arithmetic. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_sRGB2XYZ via arg vector. + */ +static int +sRGB2XYZ_vec( im_object *argv ) +{ + return( im_sRGB2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_sRGB2XYZ. + */ +static im_function sRGB2XYZ_desc = { + "im_sRGB2XYZ", /* Name */ + "convert sRGB to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + sRGB2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_XYZ2sRGB via arg vector. + */ +static int +XYZ2sRGB_vec( im_object *argv ) +{ + return( im_XYZ2sRGB( argv[0], argv[1] ) ); +} + +/* Description of im_XYZ2sRGB. + */ +static im_function XYZ2sRGB_desc = { + "im_XYZ2sRGB", /* Name */ + "convert XYZ to sRGB", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2sRGB_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LCh2Lab via arg vector. + */ +static int +LCh2Lab_vec( im_object *argv ) +{ + return( im_LCh2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_LCh2Lab. + */ +static im_function LCh2Lab_desc = { + "im_LCh2Lab", /* Name */ + "convert LCh to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + LCh2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabQ2XYZ via arg vector. + */ +static int +LabQ2XYZ_vec( im_object *argv ) +{ + return( im_LabQ2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_LabQ2XYZ. + */ +static im_function LabQ2XYZ_desc = { + "im_LabQ2XYZ", /* Name */ + "convert LabQ to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + LabQ2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LCh2UCS via arg vector. + */ +static int +LCh2UCS_vec( im_object *argv ) +{ + return( im_LCh2UCS( argv[0], argv[1] ) ); +} + +/* Description of im_LCh2UCS. + */ +static im_function LCh2UCS_desc = { + "im_LCh2UCS", /* Name */ + "convert LCh to UCS", /* Description */ + IM_FN_PIO, /* Flags */ + LCh2UCS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Lab2LCh via arg vector. + */ +static int +Lab2LCh_vec( im_object *argv ) +{ + return( im_Lab2LCh( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2LCh. + */ +static im_function Lab2LCh_desc = { + "im_Lab2LCh", /* Name */ + "convert Lab to LCh", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2LCh_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Lab2LabQ() via arg vector. + */ +static int +Lab2LabQ_vec( im_object *argv ) +{ + return( im_Lab2LabQ( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2LabQ. + */ +static im_function Lab2LabQ_desc = { + "im_Lab2LabQ", /* Name */ + "convert Lab to LabQ", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2LabQ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Lab2XYZ() via arg vector. + */ +static int +Lab2XYZ_vec( im_object *argv ) +{ + return( im_Lab2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2XYZ. + */ +static im_function Lab2XYZ_desc = { + "im_Lab2XYZ", /* Name */ + "convert D65 Lab to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +static int +icc_present_vec( im_object *argv ) +{ + int *present = ((int *) argv[0]); + + *present = im_icc_present(); + + return( 0 ); +} + +static im_arg_desc icc_present_args[] = { + IM_OUTPUT_INT( "present" ) +}; + +/* Description of im_icc_present. + */ +static im_function icc_present_desc = { + "im_icc_present", /* Name */ + "test for presence of ICC library", /* Description */ + 0, /* Flags */ + icc_present_vec, /* Dispatch function */ + IM_NUMBER( icc_present_args ), /* Size of arg list */ + icc_present_args /* Arg list */ +}; + +static int +icc_transform_vec( im_object *argv ) +{ + int intent = *((int *) argv[4]); + + return( im_icc_transform( argv[0], argv[1], + argv[2], argv[3], intent ) ); +} + +static im_arg_desc icc_transform_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "input_profile" ), + IM_INPUT_STRING( "output_profile" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_transform. + */ +static im_function icc_transform_desc = { + "im_icc_transform", /* Name */ + "convert between two device images with a pair of ICC profiles", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_transform_vec, /* Dispatch function */ + IM_NUMBER( icc_transform_args ), /* Size of arg list */ + icc_transform_args /* Arg list */ +}; + +static int +icc_import_embedded_vec( im_object *argv ) +{ + int intent = *((int *) argv[2]); + + return( im_icc_import_embedded( argv[0], argv[1], intent ) ); +} + +static im_arg_desc icc_import_embedded_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_import_embedded. + */ +static im_function icc_import_embedded_desc = { + "im_icc_import_embedded", /* Name */ + "convert a device image to float LAB using the embedded profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_import_embedded_vec, /* Dispatch function */ + IM_NUMBER( icc_import_embedded_args ), /* Size of arg list */ + icc_import_embedded_args /* Arg list */ +}; + +static int +icc_import_vec( im_object *argv ) +{ + int intent = *((int *) argv[3]); + + return( im_icc_import( argv[0], argv[1], + argv[2], intent ) ); +} + +static im_arg_desc icc_import_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "input_profile" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_import. + */ +static im_function icc_import_desc = { + "im_icc_import", /* Name */ + "convert a device image to float LAB with an ICC profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_import_vec, /* Dispatch function */ + IM_NUMBER( icc_import_args ), /* Size of arg list */ + icc_import_args /* Arg list */ +}; + +static int +icc_export_depth_vec( im_object *argv ) +{ + int intent = *((int *) argv[4]); + int depth = *((int *) argv[2]); + + return( im_icc_export_depth( argv[0], argv[1], + depth, argv[3], intent ) ); +} + +static im_arg_desc icc_export_depth_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "depth" ), + IM_INPUT_STRING( "output_profile" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_export_depth. + */ +static im_function icc_export_depth_desc = { + "im_icc_export_depth", /* Name */ + "convert a float LAB to device space with an ICC profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_export_depth_vec, /* Dispatch function */ + IM_NUMBER( icc_export_depth_args ), /* Size of arg list */ + icc_export_depth_args /* Arg list */ +}; + +static int +icc_export_vec( im_object *argv ) +{ + int intent = *((int *) argv[3]); + + return( im_icc_export( argv[0], argv[1], + argv[2], intent ) ); +} + +static im_arg_desc icc_export_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "output_profile" ), + IM_INPUT_INT( "intent" ) +}; + +/* Description of im_icc_export. + */ +static im_function icc_export_desc = { + "im_icc_export", /* Name */ + "convert a float LAB to an 8-bit device image with an ICC profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_export_vec, /* Dispatch function */ + IM_NUMBER( icc_export_args ), /* Size of arg list */ + icc_export_args /* Arg list */ +}; + +static int +icc_ac2rc_vec( im_object *argv ) +{ + return( im_icc_ac2rc( argv[0], argv[1], argv[2] ) ); +} + +static im_arg_desc icc_ac2rc_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "profile" ) +}; + +/* Description of im_icc_ac2rc. + */ +static im_function icc_ac2rc_desc = { + "im_icc_ac2rc", /* Name */ + "convert LAB from AC to RC using an ICC profile", + /* Description */ + IM_FN_PIO, /* Flags */ + icc_ac2rc_vec, /* Dispatch function */ + IM_NUMBER( icc_ac2rc_args ), /* Size of arg list */ + icc_ac2rc_args /* Arg list */ +}; + +static int +Lab2XYZ_temp_vec( im_object *argv ) +{ + double X0 = *((double *) argv[2]); + double Y0 = *((double *) argv[3]); + double Z0 = *((double *) argv[4]); + + return( im_Lab2XYZ_temp( argv[0], argv[1], X0, Y0, Z0 ) ); +} + +static im_arg_desc temp_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "X0" ), + IM_INPUT_DOUBLE( "Y0" ), + IM_INPUT_DOUBLE( "Z0" ) +}; + +/* Description of im_Lab2XYZ_temp. + */ +static im_function Lab2XYZ_temp_desc = { + "im_Lab2XYZ_temp", /* Name */ + "convert Lab to XYZ, with a specified colour temperature", + /* Description */ + IM_FN_PIO, /* Flags */ + Lab2XYZ_temp_vec, /* Dispatch function */ + IM_NUMBER( temp_args ), /* Size of arg list */ + temp_args /* Arg list */ +}; + +/* Call im_Lab2UCS() via arg vector. + */ +static int +Lab2UCS_vec( im_object *argv ) +{ + return( im_Lab2UCS( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2UCS. + */ +static im_function Lab2UCS_desc = { + "im_Lab2UCS", /* Name */ + "convert Lab to UCS", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2UCS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabQ2Lab() via arg vector. + */ +static int +LabQ2Lab_vec( im_object *argv ) +{ + return( im_LabQ2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_LabQ2Lab. + */ +static im_function LabQ2Lab_desc = { + "im_LabQ2Lab", /* Name */ + "convert LabQ to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + LabQ2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabQ2LabS() via arg vector. + */ +static int +LabQ2LabS_vec( im_object *argv ) +{ + return( im_LabQ2LabS( argv[0], argv[1] ) ); +} + +/* Description of im_LabQ2LabS. + */ +static im_function LabQ2LabS_desc = { + "im_LabQ2LabS", /* Name */ + "convert LabQ to LabS", /* Description */ + IM_FN_PIO, /* Flags */ + LabQ2LabS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Lab2LabS() via arg vector. + */ +static int +Lab2LabS_vec( im_object *argv ) +{ + return( im_Lab2LabS( argv[0], argv[1] ) ); +} + +/* Description of im_Lab2LabS. + */ +static im_function Lab2LabS_desc = { + "im_Lab2LabS", /* Name */ + "convert Lab to LabS", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2LabS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabS2Lab() via arg vector. + */ +static int +LabS2Lab_vec( im_object *argv ) +{ + return( im_LabS2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_LabS2Lab. + */ +static im_function LabS2Lab_desc = { + "im_LabS2Lab", /* Name */ + "convert LabS to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + LabS2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_LabS2LabQ() via arg vector. + */ +static int +LabS2LabQ_vec( im_object *argv ) +{ + return( im_LabS2LabQ( argv[0], argv[1] ) ); +} + +/* Description of im_LabS2LabQ. + */ +static im_function LabS2LabQ_desc = { + "im_LabS2LabQ", /* Name */ + "convert LabS to LabQ", /* Description */ + IM_FN_PIO, /* Flags */ + LabS2LabQ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_UCS2XYZ() via arg vector. + */ +static int +UCS2XYZ_vec( im_object *argv ) +{ + return( im_UCS2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_UCS2XYZ. + */ +static im_function UCS2XYZ_desc = { + "im_UCS2XYZ", /* Name */ + "convert UCS to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + UCS2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_UCS2LCh() via arg vector. + */ +static int +UCS2LCh_vec( im_object *argv ) +{ + return( im_UCS2LCh( argv[0], argv[1] ) ); +} + +/* Description of im_UCS2LCh. + */ +static im_function UCS2LCh_desc = { + "im_UCS2LCh", /* Name */ + "convert UCS to LCh", /* Description */ + IM_FN_PIO, /* Flags */ + UCS2LCh_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_UCS2Lab() via arg vector. + */ +static int +UCS2Lab_vec( im_object *argv ) +{ + return( im_UCS2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_UCS2Lab. + */ +static im_function UCS2Lab_desc = { + "im_UCS2Lab", /* Name */ + "convert UCS to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + UCS2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_Yxy2XYZ via arg vector. + */ +static int +Yxy2XYZ_vec( im_object *argv ) +{ + return( im_Yxy2XYZ( argv[0], argv[1] ) ); +} + +/* Description of im_Yxy2XYZ. + */ +static im_function Yxy2XYZ_desc = { + "im_Yxy2XYZ", /* Name */ + "convert Yxy to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + Yxy2XYZ_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_XYZ2Yxy via arg vector. + */ +static int +XYZ2Yxy_vec( im_object *argv ) +{ + return( im_XYZ2Yxy( argv[0], argv[1] ) ); +} + +/* Description of im_XYZ2Yxy. + */ +static im_function XYZ2Yxy_desc = { + "im_XYZ2Yxy", /* Name */ + "convert XYZ to Yxy", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2Yxy_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_XYZ2Lab via arg vector. + */ +static int +XYZ2Lab_vec( im_object *argv ) +{ + return( im_XYZ2Lab( argv[0], argv[1] ) ); +} + +/* Description of im_XYZ2Lab. + */ +static im_function XYZ2Lab_desc = { + "im_XYZ2Lab", /* Name */ + "convert D65 XYZ to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2Lab_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +static int +XYZ2Lab_temp_vec( im_object *argv ) +{ + double X0 = *((double *) argv[2]); + double Y0 = *((double *) argv[3]); + double Z0 = *((double *) argv[4]); + + return( im_XYZ2Lab_temp( argv[0], argv[1], X0, Y0, Z0 ) ); +} + +/* Description of im_XYZ2Lab_temp. + */ +static im_function XYZ2Lab_temp_desc = { + "im_XYZ2Lab_temp", /* Name */ + "convert XYZ to Lab, with a specified colour temperature", + /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2Lab_temp_vec, /* Dispatch function */ + IM_NUMBER( temp_args ), /* Size of arg list */ + temp_args /* Arg list */ +}; + +/* Call im_XYZ2UCS() via arg vector. + */ +static int +XYZ2UCS_vec( im_object *argv ) +{ + return( im_XYZ2UCS( argv[0], argv[1] ) ); +} + +/* Description of im_XYZ2UCS. + */ +static im_function XYZ2UCS_desc = { + "im_XYZ2UCS", /* Name */ + "convert XYZ to UCS", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2UCS_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args to XYZ2disp and disp2XYZ. + */ +static im_arg_desc XYZ2disp_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DISPLAY( "disp" ) +}; + +/* Call im_XYZ2disp() via arg vector. + */ +static int +XYZ2disp_vec( im_object *argv ) +{ + return( im_XYZ2disp( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_XYZ2disp. + */ +static im_function XYZ2disp_desc = { + "im_XYZ2disp", /* Name */ + "convert XYZ to displayble", /* Description */ + IM_FN_PIO, /* Flags */ + XYZ2disp_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +/* Call im_Lab2disp() via arg vector. + */ +static int +Lab2disp_vec( im_object *argv ) +{ + return( im_Lab2disp( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_Lab2disp. + */ +static im_function Lab2disp_desc = { + "im_Lab2disp", /* Name */ + "convert Lab to displayable", /* Description */ + IM_FN_PIO, /* Flags */ + Lab2disp_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +/* Call im_LabQ2disp() via arg vector. + */ +static int +LabQ2disp_vec( im_object *argv ) +{ + return( im_LabQ2disp( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_LabQ2disp. + */ +static im_function LabQ2disp_desc = { + "im_LabQ2disp", /* Name */ + "convert LabQ to displayable", /* Description */ + IM_FN_PIO, /* Flags */ + LabQ2disp_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +/* Call im_dE00_fromLab() via arg vector. + */ +static int +dE00_fromLab_vec( im_object *argv ) +{ + return( im_dE00_fromLab( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_dE00_fromLab. + */ +static im_function dE00_fromLab_desc = { + "im_dE00_fromLab", /* Name */ + "calculate delta-E CIE2000 for two Lab images", + IM_FN_PIO, /* Flags */ + dE00_fromLab_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_dECMC_fromLab() via arg vector. + */ +static int +dECMC_fromLab_vec( im_object *argv ) +{ + return( im_dECMC_fromLab( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_dECMC_fromLab. + */ +static im_function dECMC_fromLab_desc = { + "im_dECMC_fromLab", /* Name */ + "calculate delta-E CMC(1:1) for two Lab images", + IM_FN_PIO, /* Flags */ + dECMC_fromLab_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_dE_fromXYZ() via arg vector. + */ +static int +dE_fromXYZ_vec( im_object *argv ) +{ + return( im_dE_fromXYZ( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_dE_fromXYZ. + */ +static im_function dE_fromXYZ_desc = { + "im_dE_fromXYZ", /* Name */ + "calculate delta-E for two XYZ images", + IM_FN_PIO, /* Flags */ + dE_fromXYZ_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_dE_fromLab() via arg vector. + */ +static int +dE_fromLab_vec( im_object *argv ) +{ + return( im_dE_fromLab( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_dE_fromLab. + */ +static im_function dE_fromLab_desc = { + "im_dE_fromLab", /* Name */ + "calculate delta-E for two Lab images", + IM_FN_PIO, /* Flags */ + dE_fromLab_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Two images in, one out. + */ +static im_arg_desc dE_fromdisp_args[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DISPLAY( "disp" ) +}; + +/* Call im_dE_fromdisp() via arg vector. + */ +static int +dE_fromdisp_vec( im_object *argv ) +{ + return( im_dE_fromdisp( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_dE_fromdisp. + */ +static im_function dE_fromdisp_desc = { + "im_dE_fromdisp", /* Name */ + "calculate delta-E for two displayable images", + IM_FN_PIO, /* Flags */ + dE_fromdisp_vec, /* Dispatch function */ + IM_NUMBER( dE_fromdisp_args ), /* Size of arg list */ + dE_fromdisp_args /* Arg list */ +}; + +/* Call im_dECMC_fromdisp() via arg vector. + */ +static int +dECMC_fromdisp_vec( im_object *argv ) +{ + return( im_dECMC_fromdisp( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_dECMC_fromdisp. + */ +static im_function dECMC_fromdisp_desc = { + "im_dECMC_fromdisp", /* Name */ + "calculate delta-E CMC(1:1) for two displayable images", + IM_FN_PIO, /* Flags */ + dECMC_fromdisp_vec, /* Dispatch function */ + IM_NUMBER( dE_fromdisp_args ), /* Size of arg list */ + dE_fromdisp_args /* Arg list */ +}; + +/* Call im_disp2XYZ() via arg vector. + */ +static int +disp2XYZ_vec( im_object *argv ) +{ + return( im_disp2XYZ( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_disp2XYZ. + */ +static im_function disp2XYZ_desc = { + "im_disp2XYZ", /* Name */ + "convert displayable to XYZ", /* Description */ + IM_FN_PIO, /* Flags */ + disp2XYZ_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +/* Call im_disp2Lab() via arg vector. + */ +static int +disp2Lab_vec( im_object *argv ) +{ + return( im_disp2Lab( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_disp2Lab. + */ +static im_function disp2Lab_desc = { + "im_disp2Lab", /* Name */ + "convert displayable to Lab", /* Description */ + IM_FN_PIO, /* Flags */ + disp2Lab_vec, /* Dispatch function */ + IM_NUMBER( XYZ2disp_args ), /* Size of arg list */ + XYZ2disp_args /* Arg list */ +}; + +static im_arg_desc morph_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DMASK( "greyscale" ), + IM_INPUT_DOUBLE( "L_offset" ), + IM_INPUT_DOUBLE( "L_scale" ), + IM_INPUT_DOUBLE( "a_scale" ), + IM_INPUT_DOUBLE( "b_scale" ) +}; + +static int +morph_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + double L_offset = *((double *) argv[3]); + double L_scale = *((double *) argv[4]); + double a_scale = *((double *) argv[5]); + double b_scale = *((double *) argv[6]); + + return( im_lab_morph( argv[0], argv[1], + mo->mask, L_offset, L_scale, a_scale, b_scale ) ); +} + +static im_function morph_desc = { + "im_lab_morph", /* Name */ + "morph colourspace of a LAB image", + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + morph_vec, /* Dispatch function */ + IM_NUMBER( morph_args ), /* Size of arg list */ + morph_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *colour_list[] = { + &LCh2Lab_desc, + &LCh2UCS_desc, + &Lab2LCh_desc, + &Lab2LabQ_desc, + &Lab2LabS_desc, + &Lab2UCS_desc, + &Lab2XYZ_desc, + &Lab2XYZ_temp_desc, + &Lab2disp_desc, + &LabQ2LabS_desc, + &LabQ2Lab_desc, + &LabQ2XYZ_desc, + &LabQ2disp_desc, + &LabS2LabQ_desc, + &LabS2Lab_desc, + &UCS2LCh_desc, + &UCS2Lab_desc, + &UCS2XYZ_desc, + &XYZ2Lab_desc, + &XYZ2Lab_temp_desc, + &XYZ2UCS_desc, + &XYZ2Yxy_desc, + &XYZ2disp_desc, + &XYZ2sRGB_desc, + &Yxy2XYZ_desc, + &dE00_fromLab_desc, + &dECMC_fromLab_desc, + &dECMC_fromdisp_desc, + &dE_fromLab_desc, + &dE_fromXYZ_desc, + &dE_fromdisp_desc, + &disp2Lab_desc, + &disp2XYZ_desc, + &icc_ac2rc_desc, + &icc_export_desc, + &icc_export_depth_desc, + &icc_import_desc, + &icc_import_embedded_desc, + &icc_present_desc, + &icc_transform_desc, + &morph_desc, + &sRGB2XYZ_desc +}; + +/* Package of functions. + */ +im_package im__colour = { + "colour", + IM_NUMBER( colour_list ), + colour_list +}; diff --git a/libsrc/colour/derived.c b/libsrc/colour/derived.c new file mode 100644 index 00000000..92109672 --- /dev/null +++ b/libsrc/colour/derived.c @@ -0,0 +1,214 @@ +/* Derived colour space functions. + * + * 14/9/95 JC + * - horrible error killed im_dE_fromXYZ() and im_dE_fromdisp() + * 4/3/98 JC + * - sRGB added + * 17/6/99 JC + * - minor reformatting + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* LabQ to XYZ. + */ +int +im_LabQ2XYZ( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_LabQ2XYZ:1", "p" )) || + im_LabQ2Lab( in, t1 ) || + im_Lab2XYZ( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +/* Lab to UCS. + */ +int +im_Lab2UCS( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_Lab2UCS:1", "p" )) || + im_Lab2LCh( in, t1 ) || + im_LCh2UCS( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_UCS2Lab( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_UCS2Lab intermediate", "p" )) || + im_UCS2LCh( in, t1 ) || + im_LCh2Lab( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_UCS2XYZ( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_UCS2XYZ intermediate", "p" )) || + im_UCS2Lab( in, t1 ) || + im_Lab2XYZ( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_XYZ2UCS( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_XYZ2UCS intermediate", "p" )) || + im_XYZ2Lab( in, t1 ) || + im_Lab2UCS( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_Lab2disp( IMAGE *in, IMAGE *out, struct im_col_display *disp ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_Lab2disp:1", "p" )) || + im_Lab2XYZ( in, t1 ) || + im_XYZ2disp( t1, out, disp ) ) + return( -1 ); + + return( 0 ); +} + +int +im_XYZ2sRGB( IMAGE *in, IMAGE *out ) +{ + if( im_XYZ2disp( in, out, im_col_displays( 7 ) ) ) + return( -1 ); + + out->Type = IM_TYPE_sRGB; + + return( 0 ); +} + +int +im_sRGB2XYZ( IMAGE *in, IMAGE *out ) +{ + if( im_disp2XYZ( in, out, im_col_displays( 7 ) ) ) + return( -1 ); + + return( 0 ); +} + +int +im_dECMC_fromdisp( IMAGE *im1, IMAGE *im2, + IMAGE *out, struct im_col_display *d ) +{ + IMAGE *t1, *t2, *t3, *t4; + + if( !(t1 = im_open_local( out, "im_dECMC_fromdisp:1", "p" )) || + !(t2 = im_open_local( out, "im_dECMC_fromdisp:2", "p" )) || + !(t3 = im_open_local( out, "im_dECMC_fromdisp:3", "p" )) || + !(t4 = im_open_local( out, "im_dECMC_fromdisp:4", "p" )) || + im_disp2XYZ( im1, t1, d ) || + im_XYZ2Lab( t1, t2 ) || + im_disp2XYZ( im2, t3, d ) || + im_XYZ2Lab( t3, t4 ) || + im_dECMC_fromLab( t2, t4, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_dE_fromXYZ( IMAGE *im1, IMAGE *im2, IMAGE *out ) +{ + IMAGE *t1, *t2; + + if( !(t1 = im_open_local( out, "im_dE_fromXYZ:1", "p" )) || + !(t2 = im_open_local( out, "im_dE_fromXYZ:2", "p" )) || + im_XYZ2Lab( im1, t1 ) || + im_XYZ2Lab( im2, t2 ) || + im_dE_fromLab( t1, t2, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_dE_fromdisp( IMAGE *im1, IMAGE *im2, IMAGE *out, struct im_col_display *d ) +{ + IMAGE *t1, *t2; + + if( !(t1 = im_open_local( out, "im_dE_fromdisp:1", "p" )) || + !(t2 = im_open_local( out, "im_dE_fromdisp:2", "p" )) || + im_disp2XYZ( im1, t1, d ) || + im_disp2XYZ( im2, t2, d ) || + im_dE_fromXYZ( t1, t2, out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_disp2Lab( IMAGE *in, IMAGE *out, struct im_col_display *d ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_disp2Lab:1", "p" )) || + im_disp2XYZ( in, t1, d ) || + im_XYZ2Lab( t1, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LCh2Lab.c b/libsrc/colour/im_LCh2Lab.c new file mode 100644 index 00000000..4e7b1c2b --- /dev/null +++ b/libsrc/colour/im_LCh2Lab.c @@ -0,0 +1,108 @@ +/* @(#) Turn LCh to Lab. + * @(#) + * @(#) Usage: + * @(#) im_LCh2Lab( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * 15/11/94 JC + * - error messages added + * - memory leak fixed + * 16/11/94 JC + * - uses im_wrap_oneonebuf() now + * 8/2/95 JC + * - im_wrap v2 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_LCh2Lab( float *p, float *q, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float L = p[0]; + float C = p[1]; + float h = p[2]; + float a, b; + + p += 3; + + a = C * cos( IM_RAD( h ) ); + b = C * sin( IM_RAD( h ) ); + + q[0] = L; + q[1] = a; + q[2] = b; + q += 3; + } +} + +int +im_LCh2Lab( IMAGE *in, IMAGE *out ) +{ + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_LCh2Lab: 3-band float uncoded input only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_LCh2Lab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LCh2UCS.c b/libsrc/colour/im_LCh2UCS.c new file mode 100644 index 00000000..a826ff47 --- /dev/null +++ b/libsrc/colour/im_LCh2UCS.c @@ -0,0 +1,99 @@ +/* @(#) Turn LCh to UCS. + * @(#) + * @(#) Usage: + * @(#) im_LCh2UCS( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our main loop. + */ +void +imb_LCh2UCS( float *p, float *q, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float L = p[0]; + float C = p[1]; + float h = p[2]; + p += 3; + + /* Turn to UCS. + */ + q[0] = im_col_L2Lucs( L ); + q[1] = im_col_C2Cucs( C ); + q[2] = im_col_Ch2hucs( C, h ); + q += 3; + } +} + +int +im_LCh2UCS( IMAGE *in, IMAGE *out ) +{ + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_LCh2UCS: 3-band uncoded float input only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_UCS; + + /* Process! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_LCh2UCS, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Lab2LCh.c b/libsrc/colour/im_Lab2LCh.c new file mode 100644 index 00000000..8cbdf471 --- /dev/null +++ b/libsrc/colour/im_Lab2LCh.c @@ -0,0 +1,102 @@ +/* @(#) Turn Lab to LCh + * @(#) + * @(#) Usage: + * @(#) im_Lab2LCh( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our main loop. + */ +void +imb_Lab2LCh( float *p, float *q, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float L = p[0]; + float a = p[1]; + float b = p[2]; + float C, h; + + p += 3; + + C = sqrt( a * a + b * b ); + h = im_col_ab2h( a, b ); + + q[0] = L; + q[1] = C; + q[2] = h; + + q += 3; + } +} + +int +im_Lab2LCh( IMAGE *in, IMAGE *out ) +{ + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Lab2LCh: 3-band uncoded float input only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LCH; + + /* Process! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_Lab2LCh, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Lab2LabQ.c b/libsrc/colour/im_Lab2LabQ.c new file mode 100644 index 00000000..70dc4254 --- /dev/null +++ b/libsrc/colour/im_Lab2LabQ.c @@ -0,0 +1,158 @@ +/* @(#) im_Lab2LabQ: quantise FLOAT Lab image into 10 11 11 format + * 4 bytes per pel: l a b lsbs + * this is an image wrapper which calls line-wise packing + * Copyright K.Martinez 3/5/93 + * Modified: + * 7/6/93 JC + * - adapted for partial v2 + * 5/5/94 JC + * - some nint->+0.5, for speed and to ease portability + * - other nint->rint + * - now inclues ! + * 15/11/94 JC + * - all nint(), rint() removed for speed + * - now -128 rather than -127 for a, b + * - checks input type properly + * 16/11/94 JC + * - uses new im_wrap_oneonebuf() + * 22/5/95 JC + * - changed L to scale by 10.24, not 10.23 + * 11/7/95 JC + * - now uses IM_RINT() for rounding + * 4/9/97 JC + * - L* = 100.0 now allowed + * 5/11/00 JC + * - go int earlier for speed up + * 20/6/02 JC + * - oops, were not clipping a/b range correctly + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* @(#) convert float Lab to packed Lab32 format 10 11 11 bits + * works only on buffers, not IMAGEs + * Copyright 1993 K.Martinez + * Modified: 3/5/93, 16/6/93 + */ +void +imb_Lab2LabQ( float *inp, unsigned char *outbuf, int n ) +{ + float *f, fval; + int lsbs, intv; + int Xc; + unsigned char *out; + + out = outbuf; + f = inp; + for( Xc = 0; Xc < n; Xc++) { + /* Scale L up to 10 bits. Add 0.5 rather than call IM_RINT for + * speed. This will not round negatives correctly! But this + * does not matter, since L is >0. L*=100.0 -> 1023. + */ + intv = 10.23 * f[0] + 0.5; /* scale L up to 10 bits */ + if( intv > 1023 ) + intv = 1023; + if( intv < 0 ) + intv = 0; + lsbs = (intv & 0x3) << 6; /* 00000011 -> 11000000 */ + out[0] = (intv >> 2); /* drop bot 2 bits and store */ + + fval = 8.0 * f[1]; /* do a */ + intv = IM_RINT( fval ); + if( intv > 1023 ) + intv = 1023; + else if( intv < -1024 ) + intv = -1024; + + /* Break into bits. + */ + lsbs |= (intv & 0x7) << 3; /* 00000111 -> 00111000 */ + out[1] = (intv >> 3); /* drop bot 3 bits & store */ + + fval = 8.0 * f[2]; /* do b */ + intv = IM_RINT( fval ); + if( intv > 1023 ) + intv = 1023; + else if( intv < -1024 ) + intv = -1024; + + lsbs |= (intv & 0x7); + out[2] = (intv >> 3); + + out[3] = lsbs; /* store lsb band */ + + f += 3; + out += 4; + } +} + +int +im_Lab2LabQ( IMAGE *labim, IMAGE *outim ) +{ + /* Check for uncoded Lab type + */ + if( labim->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Lab2LabQ: uncoded input only" ); + return( -1 ); + } + if( labim->BandFmt != IM_BANDFMT_FLOAT || labim->Bands != 3 ) { + im_errormsg( "im_Lab2LabQ: three-band float input only" ); + return( -1 ); + } + + /* Set up output image. + */ + if( im_cp_desc( outim, labim ) ) + return( -1 ); + outim->Bands = 4; + outim->Type = IM_TYPE_LAB; + outim->BandFmt = IM_BANDFMT_UCHAR; + outim->Bbits = 8; + outim->Coding = IM_CODING_LABQ; + + /* Process. + */ + if( im_wrapone( labim, outim, + (im_wrapone_fn) imb_Lab2LabQ, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Lab2LabS.c b/libsrc/colour/im_Lab2LabS.c new file mode 100644 index 00000000..d83ba931 --- /dev/null +++ b/libsrc/colour/im_Lab2LabS.c @@ -0,0 +1,92 @@ +/* @(#) im_Lab2LabS: quantise FLOAT Lab image into signed short format + * 12/12/02 JC + * - from im_Lab2LabQ + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void +imb_Lab2LabS( float *in, signed short *out, int n ) +{ + float *p = in; + signed short *q = out; + int c; + + for( c = 0; c < n; c++ ) { + q[0] = p[0] * (32767.0 / 100.0); + q[1] = p[1] * (32768.0 / 128.0); + q[2] = p[2] * (32768.0 / 128.0); + + q += 3; + p += 3; + } +} + +int +im_Lab2LabS( IMAGE *labim, IMAGE *outim ) +{ + /* Check for uncoded Lab type + */ + if( labim->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Lab2LabS: uncoded input only" ); + return( -1 ); + } + if( labim->BandFmt != IM_BANDFMT_FLOAT || labim->Bands != 3 ) { + im_errormsg( "im_Lab2LabS: three-band float input only" ); + return( -1 ); + } + + /* Set up output image. + */ + if( im_cp_desc( outim, labim ) ) + return( -1 ); + outim->Type = IM_TYPE_LABS; + outim->BandFmt = IM_BANDFMT_SHORT; + outim->Bbits = IM_BBITS_SHORT; + + /* Process. + */ + if( im_wrapone( labim, outim, + (im_wrapone_fn) imb_Lab2LabS, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Lab2XYZ.c b/libsrc/colour/im_Lab2XYZ.c new file mode 100644 index 00000000..ce5361fe --- /dev/null +++ b/libsrc/colour/im_Lab2XYZ.c @@ -0,0 +1,144 @@ +/* @(#) Turn Lab to XYZ colourspace. + * @(#) + * @(#) Usage: + * @(#) im_Lab2XYZ( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 15/11/94 JC + * - ANSIfied + * - sets Type of output + * - better error messages + * 16/11/94 JC + * - partialed + * - in-line conversion + * 8/2/95 JC + * - new im_wrapone function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_Lab2XYZ( float *p, float *q, int n, im_colour_temperature *temp ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float L = p[0]; + float a = p[1]; + float b = p[2]; + float X, Y, Z; + double cby, tmp; + p += 3; + + if( L < 8.0 ) { + Y = (L * temp->Y0) / 903.3; + cby = 7.787 * (Y / temp->Y0) + 16.0 / 116.0; + } + else { + cby = (L + 16.0) / 116.0; + Y = temp->Y0 * cby * cby * cby; + } + + tmp = a / 500.0 + cby; + if( tmp < 0.2069 ) + X = temp->X0 * (tmp - 0.13793) / 7.787; + else + X = temp->X0 * tmp * tmp * tmp; + + tmp = cby - b / 200.0; + if( tmp < 0.2069 ) + Z = temp->Z0 * (tmp - 0.13793) / 7.787; + else + Z = temp->Z0 * tmp * tmp * tmp; + + /* Write. + */ + q[0] = X; + q[1] = Y; + q[2] = Z; + q += 3; + } +} + +int +im_Lab2XYZ_temp( IMAGE *in, IMAGE *out, double X0, double Y0, double Z0 ) +{ + im_colour_temperature *temp = IM_NEW( out, im_colour_temperature ); + + /* Check input image. + */ + if( !temp ) + return( -1 ); + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Lab2XYZ: 3-band uncoded float input only" ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_XYZ; + + /* Process! + */ + temp->X0 = X0; + temp->Y0 = Y0; + temp->Z0 = Z0; + if( im_wrapone( in, out, + (im_wrapone_fn) imb_Lab2XYZ, temp, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_Lab2XYZ( IMAGE *in, IMAGE *out ) +{ + return( im_Lab2XYZ_temp( in, out, IM_D65_X0, IM_D65_Y0, IM_D65_Z0 ) ); +} diff --git a/libsrc/colour/im_LabQ2Lab.c b/libsrc/colour/im_LabQ2Lab.c new file mode 100644 index 00000000..327ec18d --- /dev/null +++ b/libsrc/colour/im_LabQ2Lab.c @@ -0,0 +1,133 @@ +/* @(#) LabQ2Lab convert Lab 32bit packed format to float Lab + @(#) uses imb_LabQ2Lab +Copyright Kirk Martinez 2/5/1993 +Modified: 16/6/93 + * 7/6/93 JC + * - adapted for partial v2 + * 16/11/94 JC + * - adapted to new im_wrap_oneonebuf() function. + * 9/2/95 JC + * - new im_wrapone function + * 22/5/95 JC + * - changed char to unsigned char for RS/6000 + * - small tidies and speed-ups + * 4/9/97 JC + * - L* = 100.0 now handled correctly + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* imb_LabQ2Lab: CONVERT n pels from packed 32bit Lab to float values + * in a buffer + * ARGS: PEL *inp pointer to first byte of Lab32 buffer + * float *outbuf destination buffer + * int n number of pels to process + * (C) K.Martinez 2/5/93 + */ +void +imb_LabQ2Lab( PEL *inp, float *outbuf, int n ) +{ + signed char *b; /* to read input bytes */ + int l; + int lsbs; /* for lsbs byte */ + int c; /* counter */ + float *out; + + /* Read input with a signed pointer to get signed ab easily. + */ + b = (signed char *) inp; + out = outbuf; + for( c = 0; c < n; c++ ) { + /* Get extra bits. + */ + lsbs = ((unsigned char *) b)[3]; + + /* Build L. + */ + l = ((unsigned char *)b)[0]; + l = (l << 2) | (lsbs >> 6); + out[0] = (float) l * (100.0 / 1023.0); + + /* Build a. + */ + l = (b[1] << 3) | ((lsbs >> 3) & 0x7); + out[1] = (float) l * 0.125; + + /* And b. + */ + l = (b[2] << 3) | (lsbs & 0x7); + out[2] = (float) l * 0.125; + + b += 4; + out += 3; + } +} + +/* unpack a Lab32 ie 10,11,11 Lab image into float image +this is a wrapper around the buffer processing function +(C) K.Martinez 2/5/93 +*/ + +int +im_LabQ2Lab( IMAGE *labim, IMAGE *outim ) +{ + /* check for coded Lab type + */ + if( labim->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_LabQ2Lab: not a packed Lab image" ); + return( -1 ); + } + + /* set up output image + */ + if( im_cp_desc( outim, labim ) ) + return( -1 ); + outim->Bands = 3; + outim->Type = IM_TYPE_LAB; + outim->BandFmt = IM_BANDFMT_FLOAT; + outim->Bbits = 32; + outim->Coding = IM_CODING_NONE; + + if( im_wrapone( labim, outim, + (im_wrapone_fn) imb_LabQ2Lab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LabQ2LabS.c b/libsrc/colour/im_LabQ2LabS.c new file mode 100644 index 00000000..f2fe3a0d --- /dev/null +++ b/libsrc/colour/im_LabQ2LabS.c @@ -0,0 +1,125 @@ +/* @(#) im_LabQ2LabS() - convert IM_CODING_LABQ format to three band signed short. + * @(#) Justify to get msb in bit 15. + * @(#) + * @(#) int im_LabQ2LabS( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) + * + * 17/11/93 JC + * - adapted from im_LabQ2Lab() + * 16/11/94 JC + * - uses new im_wrap_oneonebuf() fn + * 9/2/95 JC + * - new im_wrapone function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* CONVERT n pels from packed 32bit Lab to signed short. + */ +void +imb_LabQ2LabS( unsigned char *in, signed short *out, int n ) +{ + int c; + unsigned char *p = in; + unsigned char ext; + signed short *q = out; + signed short l, a, b; + + for( c = 0; c < n; c++ ) { + /* Get most significant 8 bits of lab. + */ + l = p[0] << 7; + a = p[1] << 8; + b = p[2] << 8; + + /* Get x-tra bits. + */ + ext = p[3]; + p += 4; + + /* Shift and mask in to lab. + */ + l |= (unsigned char) (ext & 0xc0) >> 1; + a |= (ext & 0x38) << 2; + b |= (ext & 0x7) << 5; + + /* Write! + */ + q[0] = l; + q[1] = a; + q[2] = b; + q += 3; + } +} + +/* unpack a Lab32 ie 10,11,11 Lab image into signed short image +this is a wrapper around the buffer processing function +(C) K.Martinez 2/5/93 +*/ + +int +im_LabQ2LabS( IMAGE *labim, IMAGE *outim ) +{ + /* check for coded Lab type + */ + if( labim->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_LabQ2LabS: not a packed Lab image" ); + return( -1 ); + } + + /* set up output image + */ + if( im_cp_desc( outim, labim ) ) + return( -1 ); + outim->Bands = 3; + outim->Type = IM_TYPE_LABS; + outim->BandFmt = IM_BANDFMT_SHORT; + outim->Bbits = IM_BBITS_SHORT; + outim->Coding = IM_CODING_NONE; + + /* Produce output. + */ + if( im_wrapone( labim, outim, + (im_wrapone_fn) imb_LabQ2LabS, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LabQ2disp.c b/libsrc/colour/im_LabQ2disp.c new file mode 100644 index 00000000..88fac6ec --- /dev/null +++ b/libsrc/colour/im_LabQ2disp.c @@ -0,0 +1,209 @@ +/* @(#) Turn Lab 32bit packed format into displayable rgb. Fast, but very + * @(#) inaccurate: for display only! + * @(#) + * @(#) Usage: + * @(#) int im_LabQ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ) + * @(#) + * @(#) Returns: -1 on error, else 0 + * + * 5/11/97 Steve Perry + * - adapted from old ip code + * 7/11/97 JC + * - small tidies, added to VIPS + * - LUT build split into separate function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold a display characterisation, and a set of tables + * computed from that. + */ +typedef struct { + struct im_col_display *disp; + struct im_col_tab_disp *dtab; + PEL red[ 64 * 64 * 64 ]; + PEL green[ 64 * 64 * 64 ]; + PEL blue[ 64 * 64 * 64 ]; +} CalibrateInfo; + +/* Do our own indexing of the arrays, to make sure we get efficient mults. + */ +#define index( L, A, B ) (L + (A << 6) + (B << 12)) + +/* Process a buffer of data. + */ +static void +imb_LabQ2disp( PEL *p, PEL *q, int n, CalibrateInfo *cal ) +{ + int x, t; + + /* Current error. + */ + int le = 0; + int ae = 0; + int be = 0; + + for( x = 0; x < n; x++ ) { + /* Get colour, add in error from previous pixel. + */ + int L = p[0] + le; + int A = (signed char) p[1] + ae; + int B = (signed char) p[2] + be; + p += 4; + + /* Look out for overflow. + */ + L = IM_MIN( 255, L ); + A = IM_MIN( 127, A ); + B = IM_MIN( 127, B ); + + /* Find new quant error. This will always be +ve. + */ + le = L & 3; + ae = A & 3; + be = B & 3; + + /* Scale to 0-63. + */ + L = (L >> 2) & 63; + A = (A >> 2) & 63; + B = (B >> 2) & 63; + + /* Convert to RGB. + */ + t = index( L, A, B ); + q[0] = cal->red[t]; + q[1] = cal->green[t]; + q[2] = cal->blue[t]; + q += 3; + } +} + +/* Build Lab->disp tables. + */ +void * +im_LabQ2disp_build_table( IMAGE *out, struct im_col_display *d ) +{ + CalibrateInfo *cal; + int l, a, b; + int t; + + if( !(cal = IM_NEW( out, CalibrateInfo )) ) + return( NULL ); + cal->disp = d; + if( !(cal->dtab = im_col_make_tables_RGB( out, d ) ) ) { + if( !out ) + im_free( cal ); + return( NULL ); + } + + /* Build our tables. + */ + for( l = 0; l < 64; l++ ) { + for( a = 0; a < 64; a++ ) { + for( b = 0; b < 64; b++ ) { + /* Scale to lab space. + */ + float L = (l << 2) * (100.0/256.0); + float A = (signed char) (a << 2); + float B = (signed char) (b << 2); + float X, Y, Z; + int rb, gb, bb; + int oflow; + + /* Convert to XYZ. + */ + im_col_Lab2XYZ( L, A, B, &X, &Y, &Z ); + + /* Convert to display. + */ + im_col_XYZ2rgb( cal->disp, cal->dtab, + X, Y, Z, &rb, &gb, &bb, &oflow ); + + /* Save RGB. + */ + t = index( l, a, b ); + cal->red[t] = rb; + cal->green[t] = gb; + cal->blue[t] = bb; + } + } + } + + return( (void *) cal ); +} + +int +im_LabQ2disp_table( IMAGE *in, IMAGE *out, void *table ) +{ + CalibrateInfo *cal = (CalibrateInfo *) table; + + if ( in->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_LabQ2Lab: not a packed Lab image" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = 3; + out->Bbits = IM_BBITS_BYTE; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_RGB; + + if( im_wrapone( in, out, (im_wrapone_fn) imb_LabQ2disp, cal, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_LabQ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ) +{ + void *table; + + if( !(table = im_LabQ2disp_build_table( out, d )) || + im_LabQ2disp_table( in, out, table ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LabS2Lab.c b/libsrc/colour/im_LabS2Lab.c new file mode 100644 index 00000000..58c8b431 --- /dev/null +++ b/libsrc/colour/im_LabS2Lab.c @@ -0,0 +1,96 @@ +/* @(#) im_LabS2Lab() - convert short LAB format to Lab. + * @(#) + * @(#) int im_LabS2Lab( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) + * + * 12/12/02 JC + * - adapted from im_LabS2LabQ() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Convert n pels from signed short to Lab. + */ +void +imb_LabS2Lab( signed short *in, float *out, int n ) +{ + signed short *p = in; + float *q = out; + int c; + + for( c = 0; c < n; c++ ) { + q[0] = p[0] / (32767.0 / 100.0); + q[1] = p[1] / (32768.0 / 128.0); + q[2] = p[2] / (32768.0 / 128.0); + + p += 3; + q += 3; + } +} + +int +im_LabS2Lab( IMAGE *in, IMAGE *out ) +{ + /* Check type. + */ + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_LabS2Lab: not an uncoded image" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_SHORT || in->Bands != 3 ) { + im_errormsg( "im_LabS2Lab: not a 3-band signed short image" ); + return( -1 ); + } + + /* Set up output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Bbits = IM_BBITS_FLOAT; + + if( im_wrapone( in, out, + (im_wrapone_fn) imb_LabS2Lab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_LabS2LabQ.c b/libsrc/colour/im_LabS2LabQ.c new file mode 100644 index 00000000..5e47764a --- /dev/null +++ b/libsrc/colour/im_LabS2LabQ.c @@ -0,0 +1,151 @@ +/* @(#) im_LabS2LabQ() - convert short LAB format to IM_CODING_LABQ. + * @(#) + * @(#) int im_LabS2LabQ( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) + * + * 17/11/93 JC + * - adapted from im_LabQ2LabS() + * 16/11/94 JC + * - adapted to new im_wrap_oneonebuf() function + * 15/6/95 JC + * - oops! rounding was broken + * 6/6/95 JC + * - added round-to-nearest + * - somewhat slower ... + * 21/12/99 JC + * - a/b ==0 rounding was broken + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Convert n pels from signed short to IM_CODING_LABQ. + */ +void +imb_LabS2LabQ( signed short *in, unsigned char *out, int n ) +{ + int c; + signed short *p = in; + int l, a, b; + unsigned char *q = out; + unsigned char ext; + + for( c = 0; c < n; c++ ) { + /* Get LAB, rounding to 10, 11, 11. + */ + l = p[0] + 16; + if( l < 0 ) + l = 0; + else if( l > 32767 ) + l = 32767; + l >>= 5; + + /* Make sure we round -ves in the right direction! + */ + a = p[1]; + if( a >= 0 ) + a += 16; + else + a -= 16; + if( a < -32768 ) + a = -32768; + else if( a > 32767 ) + a = 32767; + a >>= 5; + + b = p[2]; + if( b >= 0 ) + b += 16; + else + b -= 16; + if( b < -32768 ) + b = -32768; + else if( b > 32767 ) + b = 32767; + b >>= 5; + + p += 3; + + /* Extract top 8 bits. + */ + q[0] = l >> 2; + q[1] = a >> 3; + q[2] = b >> 3; + + /* Form extension byte. + */ + ext = (l << 6) & 0xc0; + ext |= (a << 3) & 0x38; + ext |= b & 0x7; + q[3] = ext; + q += 4; + } +} + +int +im_LabS2LabQ( IMAGE *in, IMAGE *out ) +{ + /* Check type. + */ + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_LabS2LabQ: not an uncoded image" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_SHORT || in->Bands != 3 ) { + im_errormsg( "im_LabS2LabQ: not a 3-band signed short image" ); + return( -1 ); + } + + /* Set up output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = 4; + out->Type = IM_TYPE_LAB; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = 8; + out->Coding = IM_CODING_LABQ; + + if( im_wrapone( in, out, + (im_wrapone_fn) imb_LabS2LabQ, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_UCS2LCh.c b/libsrc/colour/im_UCS2LCh.c new file mode 100644 index 00000000..84085e02 --- /dev/null +++ b/libsrc/colour/im_UCS2LCh.c @@ -0,0 +1,109 @@ +/* @(#) Turn UCS to LCh + * @(#) + * @(#) Usage: + * @(#) im_UCS2LCh( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * 15/11/94 JC + * - error messages added + * - memory leak fixed + * 16/11/94 JC + * - uses im_wrap_oneonebuf() now + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_UCS2LCh( float *p, float *q, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float Lucs = p[0]; + float Cucs = p[1]; + float hucs = p[2]; + + /* Turn from UCS. + */ + float C = im_col_Cucs2C( Cucs ); + float h = im_col_Chucs2h( C, hucs ); + float L = im_col_Lucs2L( Lucs ); + + p += 3; + + q[0] = L; + q[1] = C; + q[2] = h; + q += 3; + } +} + +int +im_UCS2LCh( IMAGE *in, IMAGE *out ) +{ + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_UCS2LCh: 3-band float uncoded input only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in) ) + return( -1 ); + out->Type = IM_TYPE_LCH; + + /* Do the processing. + */ + im_col_make_tables_UCS(); + if( im_wrapone( in, out, + (im_wrapone_fn) imb_UCS2LCh, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_XYZ2Lab.c b/libsrc/colour/im_XYZ2Lab.c new file mode 100644 index 00000000..bbd0a038 --- /dev/null +++ b/libsrc/colour/im_XYZ2Lab.c @@ -0,0 +1,185 @@ +/* @(#) Turn XYZ to Lab colourspace. + * @(#) + * @(#) Usage: + * @(#) im_XYZ2Lab( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modifed: + * 16/11/94 JC + * - uses im_wrapone() + * - in-line conversion + * 27/1/03 JC + * - swapped cbrt() for pow(), more portable + * 12/11/04 + * - swapped pow() for cbrt() again, pow() is insanely slow on win32 + * - added a configure test for cbrt(). + * 23/11/04 + * - use a large LUT instead, about 5x faster + * 23/11/06 + * - ahem, build the LUT outside the eval thread + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifndef HAVE_CBRT +#define cbrt( X ) pow( (X), 1.0 / 3.0 ) +#endif /*!HAVE_CBRT*/ + +/* Lookup table size. + */ +#define QUANT_ELEMENTS (100000) + +float cbrt_table[QUANT_ELEMENTS]; + +void +imb_XYZ2Lab_tables( void ) +{ + static int built_tables = 0; + + int i; + + if( built_tables ) + return; + + for( i = 0; i < QUANT_ELEMENTS; i++ ) { + float Y = (double) i / QUANT_ELEMENTS; + + if( Y < 0.008856 ) + cbrt_table[i] = 7.787 * Y + (16.0 / 116.0); + else + cbrt_table[i] = cbrt( Y ); + } + + built_tables = 1; +} + +/* Process a buffer of data. + */ +void +imb_XYZ2Lab( float *p, float *q, int n, im_colour_temperature *temp ) +{ + int x; + + for( x = 0; x < n; x++ ) { + float nX, nY, nZ; + int i; + float f; + float cbx, cby, cbz; + + nX = QUANT_ELEMENTS * p[0] / temp->X0; + nY = QUANT_ELEMENTS * p[1] / temp->Y0; + nZ = QUANT_ELEMENTS * p[2] / temp->Z0; + p += 3; + + i = (int) nX; + if( i < 0 ) + i = 0; + if( i > QUANT_ELEMENTS - 2 ) + i = QUANT_ELEMENTS - 2; + f = nX - i; + cbx = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); + + i = (int) nY; + if( i < 0 ) + i = 0; + if( i > QUANT_ELEMENTS - 2 ) + i = QUANT_ELEMENTS - 2; + f = nY - i; + cby = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); + + i = (int) nZ; + if( i < 0 ) + i = 0; + if( i > QUANT_ELEMENTS - 2 ) + i = QUANT_ELEMENTS - 2; + f = nZ - i; + cbz = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); + + q[0] = 116.0 * cby - 16.0; + q[1] = 500.0 * (cbx - cby); + q[2] = 200.0 * (cby - cbz); + q += 3; + } +} + +int +im_XYZ2Lab_temp( IMAGE *in, IMAGE *out, + double X0, double Y0, double Z0 ) +{ + im_colour_temperature *temp = IM_NEW( out, im_colour_temperature ); + + /* Check input image. + */ + if( !temp ) + return( -1 ); + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_XYZ2Lab: 3-band uncoded float only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + + /* Do the processing. + */ + imb_XYZ2Lab_tables(); + temp->X0 = X0; + temp->Y0 = Y0; + temp->Z0 = Z0; + if( im_wrapone( in, out, + (im_wrapone_fn) imb_XYZ2Lab, temp, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_XYZ2Lab( IMAGE *in, IMAGE *out ) +{ + return( im_XYZ2Lab_temp( in, out, IM_D65_X0, IM_D65_Y0, IM_D65_Z0 ) ); +} diff --git a/libsrc/colour/im_XYZ2Yxy.c b/libsrc/colour/im_XYZ2Yxy.c new file mode 100644 index 00000000..9c8b520d --- /dev/null +++ b/libsrc/colour/im_XYZ2Yxy.c @@ -0,0 +1,101 @@ +/* @(#) Turn XYZ to Yxy colourspace. + * @(#) + * @(#) Usage: + * @(#) im_XYZ2Yxy( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 29/5/02 JC + * - from lab2xyz + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_XYZ2Yxy( float *p, float *q, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) { + float X = p[0]; + float Y = p[1]; + float Z = p[2]; + double total = X + Y + Z; + + float x, y; + + p += 3; + + x = X / total; + y = Y / total; + + q[0] = Y; + q[1] = x; + q[2] = y; + q += 3; + } +} + +int +im_XYZ2Yxy( IMAGE *in, IMAGE *out ) +{ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_XYZ2Yxy: 3-band uncoded float input only" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_YXY; + + if( im_wrapone( in, out, + (im_wrapone_fn) imb_XYZ2Yxy, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/colour/im_XYZ2disp.c b/libsrc/colour/im_XYZ2disp.c new file mode 100644 index 00000000..24477b3d --- /dev/null +++ b/libsrc/colour/im_XYZ2disp.c @@ -0,0 +1,164 @@ +/* @(#) Turn XYZ files into displayable rgb. + * @(#) + * @(#) Usage: + * @(#) int im_XYZ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ) + * @(#) + * @(#) Returns: -1 on error, else 0 + * + * Author: J-P. Laurent + * Modified: + * 15/11/94 JC + * - error message added + * - out->Type set to IM_TYPE_RGB + * - memory leak fixed + * 16/11/94 JC + * - uses im_wrapone() + * 15/2/95 JC + * - oops! now uses PEL, not float for output pointer + * 2/1/96 JC + * - sometimes produced incorrect result at extrema + * - reformatted + * - now uses IM_RINT() and clip() + * 18/9/96 JC + * - some speed-ups ... 3x faster + * - slightly less accurate, but who cares + * - added out-of-mem check for table build + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_XYZ2disp( float *p, PEL *q, int n, + struct im_col_tab_disp *table, struct im_col_display *d ) +{ + int x; + float rstep = (d->d_YCR - d->d_Y0R) / 1500.0; + float gstep = (d->d_YCG - d->d_Y0G) / 1500.0; + float bstep = (d->d_YCB - d->d_Y0B) / 1500.0; + + for( x = 0; x < n; x++ ) { + float Yr, Yg, Yb; + int i; + int r, g, b; + + float X = p[0]; + float Y = p[1]; + float Z = p[2]; + + p += 3; + + /* Multiply through the matrix to get luminosity values. + */ + Yr = table->mat_XYZ2lum[0][0] * X + + table->mat_XYZ2lum[0][1] * Y + + table->mat_XYZ2lum[0][2] * Z; + Yg = table->mat_XYZ2lum[1][0] * X + + table->mat_XYZ2lum[1][1] * Y + + table->mat_XYZ2lum[1][2] * Z; + Yb = table->mat_XYZ2lum[2][0] * X + + table->mat_XYZ2lum[2][1] * Y + + table->mat_XYZ2lum[2][2] * Z; + + /* Clip -ves. + */ + Yr = IM_MAX( Yr, d->d_Y0R ); + Yg = IM_MAX( Yg, d->d_Y0G ); + Yb = IM_MAX( Yb, d->d_Y0B ); + + /* Turn luminosity to colour value. + */ + i = IM_MIN( 1500, (Yr - d->d_Y0R) / rstep ); + r = table->t_Yr2r[i]; + + i = IM_MIN( 1500, (Yg - d->d_Y0G) / gstep ); + g = table->t_Yg2g[i]; + + i = IM_MIN( 1500, (Yb - d->d_Y0B) / bstep ); + b = table->t_Yb2b[i]; + + /* Clip output. + */ + r = IM_MIN( r, d->d_Vrwr ); + g = IM_MIN( g, d->d_Vrwg ); + b = IM_MIN( b, d->d_Vrwb ); + + q[0] = r; + q[1] = g; + q[2] = b; + + q += 3; + } +} + +int +im_XYZ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ) +{ + struct im_col_tab_disp *table; + + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_XYZ2disp: 3-band uncoded float only" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_BYTE; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Type = IM_TYPE_RGB; + + /* Build the lookup tables + */ + if( !(table = im_col_make_tables_RGB( out, d )) ) + return( -1 ); + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_XYZ2disp, table, d ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_Yxy2XYZ.c b/libsrc/colour/im_Yxy2XYZ.c new file mode 100644 index 00000000..6dd1ea09 --- /dev/null +++ b/libsrc/colour/im_Yxy2XYZ.c @@ -0,0 +1,102 @@ +/* @(#) Turn Yxy to XYZ colourspace. + * @(#) + * @(#) Usage: + * @(#) im_Yxy2XYZ( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Float in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 29/5/02 JC + * - from lab2xyz + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer of data. + */ +void +imb_Yxy2XYZ( float *p, float *q, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) { + float Y = p[0]; + float x = p[1]; + float y = p[2]; + + double total; + float X, Z; + + p += 3; + + total = Y / y; + X = x * total; + Z = (X - x * X - x * Y) / x; + + q[0] = X; + q[1] = Y; + q[2] = Z; + q += 3; + } +} + +int +im_Yxy2XYZ( IMAGE *in, IMAGE *out ) +{ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_Yxy2XYZ: 3-band uncoded float input only" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_XYZ; + + if( im_wrapone( in, out, + (im_wrapone_fn) imb_Yxy2XYZ, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/colour/im_dE00_fromLab.c b/libsrc/colour/im_dE00_fromLab.c new file mode 100644 index 00000000..a01b3fdb --- /dev/null +++ b/libsrc/colour/im_dE00_fromLab.c @@ -0,0 +1,110 @@ +/* @(#) Calculate dE00 from two Lab images + * @(#) + * @(#) Usage: + * @(#) im_dE00_fromLab( im1, im2, im_out ) + * @(#) IMAGE *im1, *im2, *im_out; + * @(#) + * @(#) float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * 10/10/02 JC + * - from dECMC + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer. + */ +void +imb_dE00_fromLab( float **p, float *q, int n ) +{ + float *p1 = p[0]; + float *p2 = p[1]; + int x; + + for( x = 0; x < n; x++ ) { + float L1 = p1[0]; + float a1 = p1[1]; + float b1 = p1[2]; + float L2 = p2[0]; + float a2 = p2[1]; + float b2 = p2[2]; + + p1 += 3; + p2 += 3; + + q[x] = im_col_dE00( L1, a1, b1, L2, a2, b2 ); + } +} + +int +im_dE00_fromLab( IMAGE *im1, IMAGE *im2, IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check input types. + */ + if( im1->Bands != 3 || im1->BandFmt != IM_BANDFMT_FLOAT || + im1->Coding != IM_CODING_NONE || + im2->Bands != 3 || im2->BandFmt != IM_BANDFMT_FLOAT || + im2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_dE00_fromLab: 3-band float only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_descv( out, im1, im2, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->Bands = 1; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Type = IM_TYPE_B_W; + + /* Do the processing. + */ + invec[0] = im1; invec[1] = im2; invec[2] = NULL; + if( im_wrapmany( invec, out, + (im_wrapmany_fn) imb_dE00_fromLab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_dECMC_fromLab.c b/libsrc/colour/im_dECMC_fromLab.c new file mode 100644 index 00000000..70a6761b --- /dev/null +++ b/libsrc/colour/im_dECMC_fromLab.c @@ -0,0 +1,110 @@ +/* @(#) Calculate dECMC from two Lab images + * @(#) + * @(#) Usage: + * @(#) im_dECMC_fromLab( im1, im2, im_out ) + * @(#) IMAGE *im1, *im2, *im_out; + * @(#) + * @(#) float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * 5/8/98 JC + * - oops, wasn't testing input args correctly + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Process a buffer. + */ +void +imb_dECMC_fromLab( float **p, float *q, int n ) +{ + float *p1 = p[0]; + float *p2 = p[1]; + int x; + + for( x = 0; x < n; x++ ) { + float L1 = p1[0]; + float a1 = p1[1]; + float b1 = p1[2]; + float L2 = p2[0]; + float a2 = p2[1]; + float b2 = p2[2]; + + p1 += 3; + p2 += 3; + + q[x] = im_col_dECMC( L1, a1, b1, L2, a2, b2 ); + } +} + +int +im_dECMC_fromLab( IMAGE *im1, IMAGE *im2, IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check input types. + */ + if( im1->Bands != 3 || im1->BandFmt != IM_BANDFMT_FLOAT || + im1->Coding != IM_CODING_NONE || + im2->Bands != 3 || im2->BandFmt != IM_BANDFMT_FLOAT || + im2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_dECMC_fromLab: 3-band float only" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_descv( out, im1, im2, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->Bands = 1; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Type = IM_TYPE_B_W; + + /* Do the processing. + */ + invec[0] = im1; invec[1] = im2; invec[2] = NULL; + if( im_wrapmany( invec, out, + (im_wrapmany_fn) imb_dECMC_fromLab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_dE_fromLab.c b/libsrc/colour/im_dE_fromLab.c new file mode 100644 index 00000000..27e07574 --- /dev/null +++ b/libsrc/colour/im_dE_fromLab.c @@ -0,0 +1,116 @@ +/* @(#) Calculate dE (CIELAB standard) from two LAB images. + * @(#) + * @(#) Usage: + * @(#) im_dE_fromLab( im1, *im2, im_out ) + * @(#) IMAGE *im1, *im2, *im_out; + * @(#) + * @(#) float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 16/11/94 JC + * - partialed! + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Find the difference between two buffers of LAB data. + */ +void +imb_dE_fromLab( float **p, float *q, int n ) +{ + float *p1 = p[0]; + float *p2 = p[1]; + int x; + + for( x = 0; x < n; x++ ) { + float L1 = p1[0]; + float a1 = p1[1]; + float b1 = p1[2]; + float L2 = p2[0]; + float a2 = p2[1]; + float b2 = p2[2]; + float dL, da, db; + + p1 += 3; + p2 += 3; + + dL = L1 - L2; + da = a1 - a2; + db = b1 - b2; + + *q++ = sqrt( dL*dL + da*da + db*db ); + } +} + +int +im_dE_fromLab( IMAGE *im1, IMAGE *im2, IMAGE *out ) +{ + IMAGE *invec[3]; + + /* Check input image. + */ + if( im1->Bands != 3 || im1->BandFmt != IM_BANDFMT_FLOAT || + im1->Coding != IM_CODING_NONE || + im2->Bands != 3 || im2->BandFmt != IM_BANDFMT_FLOAT || + im2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_dE_fromLab: inputs should be 3 band float"); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_descv( out, im1, im2, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->Bands = 1; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Type = IM_TYPE_B_W; + + /* Do the processing. + */ + invec[0] = im1; invec[1] = im2; invec[2] = NULL; + if( im_wrapmany( invec, out, + (im_wrapmany_fn) imb_dE_fromLab, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_disp2XYZ.c b/libsrc/colour/im_disp2XYZ.c new file mode 100644 index 00000000..233a72f9 --- /dev/null +++ b/libsrc/colour/im_disp2XYZ.c @@ -0,0 +1,115 @@ +/* @(#) Turn displayable rgb files to XYZ. + * @(#) + * @(#) Usage: + * @(#) im_disp2XYZ( imagein, imageout, display ) + * @(#) IMAGE *imagein, *imageout; + * @(#) struct im_col_display *display; + * @(#) + * @(#) uchar in, float out. + * @(#) + * @(#) Returns: -1 on error, else 0 + * Modified: + * 15/11/94 JC + * - memory leak fixed + * - error message added + * 16/11/94 JC + * - partialed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Convert a buffer. + */ +void +imb_disp2XYZ( PEL *p, float *q, int n, + struct im_col_display *d, struct im_col_tab_disp *table ) +{ + int x; + + for( x = 0; x < n; x++ ) { + int r = p[0]; + int g = p[1]; + int b = p[2]; + float X, Y, Z; + p += 3; + + im_col_rgb2XYZ(d, table, r, g, b, &X, &Y, &Z); + + q[0] = X; + q[1] = Y; + q[2] = Z; + q += 3; + } +} + +int +im_disp2XYZ( IMAGE *in, IMAGE *out, struct im_col_display *d ) +{ + struct im_col_tab_disp *table; /* pointer to the lookup tables */ + + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_UCHAR || + in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_disp2XYZ: input not 3-band uncoded char" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Type = IM_TYPE_XYZ; + + /* Prepare the lookup tables + */ + table = im_col_make_tables_RGB( out, d ); + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) imb_disp2XYZ, d, table ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/im_icc_transform.c b/libsrc/colour/im_icc_transform.c new file mode 100644 index 00000000..609480ec --- /dev/null +++ b/libsrc/colour/im_icc_transform.c @@ -0,0 +1,796 @@ +/* @(#) Transform images with little cms + * @(#) + * + * 26/4/02 JC + * 26/8/05 + * - attach profiles and intents to output images + * - added im_icc_import_embedded() to import with an embedded profile + * 12/5/06 + * - lock around cmsDoTransform + * 23/1/07 + * - set RGB16 on 16-bit RGB export + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_LCMS + +#include +#include + +int +im_icc_present( void ) +{ + return( 0 ); +} + +int +im_icc_transform( IMAGE *in, IMAGE *out, + const char *input_profile_filename, + const char *output_profile_filename, + int intent ) +{ + im_error( "im_icc_transform", + _( "lcms library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_import( IMAGE *in, IMAGE *out, + const char *input_profile_filename, int intent ) +{ + im_error( "im_icc_import", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_import_embedded( IMAGE *in, IMAGE *out, int intent ) +{ + im_error( "im_icc_import", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_export_depth( IMAGE *in, IMAGE *out, int depth, + const char *output_profile_filename, int intent ) +{ + im_error( "im_icc_export_depth", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_export( IMAGE *in, IMAGE *out, + const char *output_profile_filename, int intent ) +{ + im_error( "im_icc_export", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +int +im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename ) +{ + im_error( "im_icc_ac2rc", + _( "lmcs library not linked to this VIPS" ) ); + + return( -1 ); +} + +#else + +#include +#include +#include + +/* Has to be before VIPS to avoid nameclashes. + */ +#include +#include + +#include +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Call lcms with up to this many pixels at once. + */ +#define PIXEL_BUFFER_SIZE (10000) + +static const char * +decode_intent( int intent ) +{ + switch( intent ) { + case IM_INTENT_PERCEPTUAL: return( "PERCEPTUAL" ); + case IM_INTENT_RELATIVE_COLORIMETRIC: return( "RELATIVE" ); + case IM_INTENT_SATURATION: return( "SATURATION" ); + case IM_INTENT_ABSOLUTE_COLORIMETRIC: return( "ABSOLUTE" ); + default: return( "" ); + } +} + +int +im_icc_present( void ) +{ + return( 1 ); +} + +/* Global state for a transform. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + const char *input_profile_filename; + const char *output_profile_filename; + int intent; + + cmsHPROFILE in_profile; + cmsHPROFILE out_profile; + cmsHTRANSFORM trans; + + /* We need to single-thread calls to LCMS. + */ + GMutex *lock; +} Icc; + +static int +icc_destroy( Icc *icc ) +{ + IM_FREEF( cmsDeleteTransform, icc->trans ); + IM_FREEF( cmsCloseProfile, icc->in_profile ); + IM_FREEF( cmsCloseProfile, icc->out_profile ); + IM_FREEF( g_mutex_free, icc->lock ); + + return( 0 ); +} + +static Icc * +icc_new( IMAGE *in, IMAGE *out, int intent ) +{ + Icc *icc; + + /* Ask lcms not to abort on error. + */ + cmsErrorAction( LCMS_ERROR_IGNORE ); + + if( !(icc = IM_NEW( out, Icc )) ) + return( NULL ); + + icc->in = in; + icc->out = out; + icc->input_profile_filename = NULL; + icc->output_profile_filename = NULL; + icc->intent = intent; + icc->in_profile = 0; + icc->out_profile = 0; + icc->trans = 0; + icc->lock = g_mutex_new(); + + if( im_add_close_callback( out, + (im_callback_fn) icc_destroy, icc, NULL ) ) + return( NULL ); + + return( icc ); +} + +static Icc * +icc_new_file( IMAGE *in, IMAGE *out, + const char *input_profile_filename, + const char *output_profile_filename, + int intent ) +{ + Icc *icc; + + if( !(icc = icc_new( in, out, intent )) ) + return( NULL ); + + if( input_profile_filename ) { + icc->input_profile_filename = + im_strdup( out, input_profile_filename ); + if( !(icc->in_profile = cmsOpenProfileFromFile( + input_profile_filename, "r" )) ) + im_error( "im_icc_transform", + _( "unable to open profile \"%s\"" ), + input_profile_filename ); + } + + if( output_profile_filename ) { + icc->output_profile_filename = + im_strdup( out, output_profile_filename ); + if( !(icc->out_profile = cmsOpenProfileFromFile( + output_profile_filename, "r" )) ) + im_error( "im_icc_transform", + _( "unable to open profile \"%s\"" ), + output_profile_filename ); + } + + if( !output_profile_filename ) + icc->out_profile = cmsCreateLabProfile( NULL ); + if( !input_profile_filename ) + icc->in_profile = cmsCreateLabProfile( NULL ); + + if( !icc->in_profile || !icc->out_profile ) { + im_error( "im_icc_transform", + _( "unable to create profiles" ) ); + return( NULL ); + } + + return( icc ); +} + +static Icc * +icc_new_mem( IMAGE *in, IMAGE *out, + void *data, int data_length, + int intent ) +{ + Icc *icc; + + if( !(icc = icc_new( in, out, intent )) ) + return( NULL ); + + if( !(icc->in_profile = cmsOpenProfileFromMem( data, data_length )) ) { + im_error( "im_icc_transform", _( "unable to read profile" ) ); + return( NULL ); + } + icc->out_profile = cmsCreateLabProfile( NULL ); + + return( icc ); +} + +/* Pack a buffer of floats into lcms's fixed-point formats. Cut from + * lcms-1.0.8. + */ +static void +encode_lab( float *lab, WORD *fixed, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) { + float L = lab[0]; + float a = lab[1]; + float b = lab[2]; + + if( L < 0 ) + L = 0; + if( L > 100. ) + L = 100.; + + if( a < -128. ) + a = -128; + if( a > 127.9961 ) + a = 127.9961; + if( b < -128. ) + b = -128; + if( b > 127.9961 ) + b = 127.9961; + + fixed[0] = L * 652.800 + 0.5; + fixed[1] = (a + 128.0) * 256.0 + 0.5; + fixed[2] = (b + 128.0) * 256.0 + 0.5; + + lab += 3; + fixed += 3; + } +} + +static void +decode_lab( WORD *fixed, float *lab, int n ) +{ + int i; + + for( i = 0; i < n; i++ ) { + lab[0] = (double) fixed[0] / 652.800; + lab[1] = ((double) fixed[1] / 256.0) - 128.0; + lab[2] = ((double) fixed[2] / 256.0) - 128.0; + + lab += 3; + fixed += 3; + } +} + +/* + + lcms calls this on error ... but only for dynamically linked + programs :-( + +void +cmsSignalError( int code, const char *fmt, ... ) +{ + va_list ap; + + im_error( "im_icc", "error code #%d from little cms: " ); + + va_start( ap, fmt ); + im_verrormsg( fmt, ap ); + va_end( ap ); +} + */ + +static void +transform_buf( PEL *in, PEL *out, int n, Icc *icc ) +{ + g_mutex_lock( icc->lock ); + cmsDoTransform( icc->trans, in, out, n ); + g_mutex_unlock( icc->lock ); +} + +static int +attach_profile( IMAGE *im, const char *filename ) +{ + char *data; + unsigned int data_length; + + if( !(data = im__file_read_name( filename, &data_length )) ) + return( -1 ); + if( im_meta_set_blob( im, IM_META_ICC_NAME, + (im_callback_fn) im_free, data, data_length ) ) { + im_free( data ); + return( -1 ); + } + + return( 0 ); +} + +int +im_icc_transform( IMAGE *in, IMAGE *out, + const char *input_profile_filename, + const char *output_profile_filename, + int intent ) +{ + Icc *icc; + DWORD in_icc_format; + DWORD out_icc_format; + + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_icc_transform", _( "uncoded input only" ) ); + return( -1 ); + } + + if( !(icc = icc_new_file( in, out, + input_profile_filename, output_profile_filename, intent )) ) + return( -1 ); + + if( !cmsIsIntentSupported( icc->in_profile, + intent, LCMS_USED_AS_INPUT ) ) + im_warning( "im_icc_transform: intent %d (%s) not supported by " + "profile \"%s\"; falling back to default intent " + "(usually PERCEPTUAL)", + intent, decode_intent( intent ), + input_profile_filename ); + + if( !cmsIsIntentSupported( icc->out_profile, + intent, LCMS_USED_AS_OUTPUT ) ) + im_warning( "im_icc_transform: intent %d (%s) not supported by " + "profile \"%s\"; falling back to default intent " + "(usually PERCEPTUAL)", + intent, decode_intent( intent ), + output_profile_filename ); + + switch( cmsGetColorSpace( icc->in_profile ) ) { + case icSigCmykData: + if( in->Bands != 4 ) { + im_error( "im_icc_transform", _( "CMYK input profile " + "needs a 4 band input image" ) ); + return( -1 ); + } + in_icc_format = COLORSPACE_SH( PT_CMYK ) | CHANNELS_SH( 4 ); + break; + + case icSigRgbData: + if( in->Bands != 3 ) { + im_error( "im_icc_transform", _( "RGB input profile " + "needs a 3 band input image" ) ); + return( -1 ); + } + in_icc_format = COLORSPACE_SH( PT_RGB ) | CHANNELS_SH( 3 ); + break; + + default: + im_error( "im_icc_transform", _( "unimplemented input colour " + "space 0x%x" ), cmsGetColorSpace( icc->in_profile ) ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + switch( cmsGetColorSpace( icc->out_profile ) ) { + case icSigCmykData: + out->Type = IM_TYPE_CMYK; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + out->Bands = 4; + out_icc_format = TYPE_CMYK_8; + break; + + case icSigRgbData: + out->Type = IM_TYPE_RGB; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + out->Bands = 3; + out_icc_format = TYPE_RGB_8; + break; + + default: + im_error( "im_icc_transform", _( "unimplemented output colour " + "space 0x%x" ), cmsGetColorSpace( icc->out_profile ) ); + return( -1 ); + } + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + in_icc_format |= BYTES_SH( 1 ); + break; + + case IM_BANDFMT_USHORT: + in_icc_format |= BYTES_SH( 2 ); + break; + + default: + im_error( "im_icc_transform", + _( "uchar or ushort input only" ) ); + return( -1 ); + } + + if( !(icc->trans = cmsCreateTransform( icc->in_profile, in_icc_format, + icc->out_profile, out_icc_format, intent, 0 )) ) + return( -1 ); + + /* Process! + */ + if( im_wrapone( in, out, + (im_wrapone_fn) transform_buf, icc, NULL ) ) + return( -1 ); + + if( attach_profile( out, output_profile_filename ) ) + return( -1 ); + + return( 0 ); +} + +static void +import_buf( PEL *in, float *out, int n, Icc *icc ) +{ + /* Buffer of encoded 16-bit pixels we write to. + */ + WORD encoded[3 * PIXEL_BUFFER_SIZE]; + + while( n > 0 ) { + const int chunk = IM_MIN( n, PIXEL_BUFFER_SIZE ); + + g_mutex_lock( icc->lock ); + cmsDoTransform( icc->trans, in, encoded, chunk ); + g_mutex_unlock( icc->lock ); + decode_lab( encoded, out, chunk ); + + in += chunk * IM_IMAGE_SIZEOF_PEL( icc->in ); + out += chunk * 3; + n -= chunk; + } +} + +static int +icc_import( IMAGE *in, IMAGE *out, Icc *icc ) +{ + DWORD icc_format; + + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_icc_import", _( "uncoded input only" ) ); + return( -1 ); + } + + if( !cmsIsIntentSupported( icc->in_profile, + icc->intent, LCMS_USED_AS_INPUT ) ) + im_warn( "im_icc_import", _( "intent %d (%s) not supported by " + "profile; falling back to default intent " + "(usually PERCEPTUAL)" ), + icc->intent, decode_intent( icc->intent ) ); + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Bbits = IM_BBITS_FLOAT; + out->Bands = 3; + + switch( cmsGetColorSpace( icc->in_profile ) ) { + case icSigCmykData: + if( in->Bands != 4 ) { + im_error( "im_icc_import", _( "CMYK profile needs a " + "4 band input image" ) ); + return( -1 ); + } + icc_format = COLORSPACE_SH( PT_CMYK ) | CHANNELS_SH( 4 ); + break; + + case icSigRgbData: + if( in->Bands != 3 ) { + im_error( "im_icc_import", _( "RGB profile needs a " + "3 band input image" ) ); + return( -1 ); + } + icc_format = COLORSPACE_SH( PT_RGB ) | CHANNELS_SH( 3 ); + break; + + default: + im_error( "im_icc_import", _( "unimplemented input colour " + "space 0x%x" ), cmsGetColorSpace( icc->in_profile ) ); + return( -1 ); + } + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + icc_format |= BYTES_SH( 1 ); + break; + + case IM_BANDFMT_USHORT: + icc_format |= BYTES_SH( 2 ); + break; + + default: + im_error( "im_icc_transform", + _( "uchar or ushort input only" ) ); + return( -1 ); + } + + if( !(icc->trans = cmsCreateTransform( icc->in_profile, icc_format, + icc->out_profile, TYPE_Lab_16, icc->intent, 0 )) ) + return( -1 ); + + /* Process! + */ + if( im_wrapone( in, out, (im_wrapone_fn) import_buf, icc, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_icc_import( IMAGE *in, IMAGE *out, + const char *input_profile_filename, int intent ) +{ + Icc *icc; + + if( !(icc = icc_new_file( in, out, + input_profile_filename, NULL, intent )) || + icc_import( in, out, icc ) ) + return( -1 ); + + return( 0 ); +} + +int +im_icc_import_embedded( IMAGE *in, IMAGE *out, int intent ) +{ + Icc *icc; + void *data; + size_t data_length; + + if( im_header_get_type( in, IM_META_ICC_NAME ) == 0 ) { + im_error( "im_icc_import_embedded", + _( "no embedded profile" ) ); + return( -1 ); + } + + if( im_meta_get_blob( in, IM_META_ICC_NAME, &data, &data_length ) || + !(icc = icc_new_mem( in, out, data, data_length, intent )) || + icc_import( in, out, icc ) ) + return( -1 ); + + return( 0 ); +} + +static void +export_buf( float *in, PEL *out, int n, Icc *icc ) +{ + /* Buffer of encoded 16-bit pixels we transform. + */ + WORD encoded[3 * PIXEL_BUFFER_SIZE]; + + while( n > 0 ) { + const int chunk = IM_MIN( n, PIXEL_BUFFER_SIZE ); + + encode_lab( in, encoded, chunk ); + g_mutex_lock( icc->lock ); + cmsDoTransform( icc->trans, encoded, out, chunk ); + g_mutex_unlock( icc->lock ); + + in += chunk * 3; + out += chunk * IM_IMAGE_SIZEOF_PEL( icc->out ); + n -= chunk; + } +} + +int +im_icc_export_depth( IMAGE *in, IMAGE *out, int depth, + const char *output_profile_filename, int intent ) +{ + Icc *icc; + DWORD icc_format; + + /* Do IM_CODING_LABQ too. + */ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "im_icc_export:1", "p" ); + + if( !t1 || im_LabQ2Lab( in, t1 ) ) + return( -1 ); + + in = t1; + } + + /* Check input image. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_error( "im_icc_export", + _( "3-band uncoded Lab float only" ) ); + return( -1 ); + } + + if( depth != 8 && depth != 16 ) { + im_error( "im_icc_export", _( "unsupported bit depth" ) ); + return( -1 ); + } + + if( !(icc = icc_new_file( in, out, + NULL, output_profile_filename, intent )) ) + return( -1 ); + + if( !cmsIsIntentSupported( icc->out_profile, + intent, LCMS_USED_AS_OUTPUT ) ) + im_warning( "im_icc_export: intent %d (%s) not supported by " + "profile \"%s\"; falling back to default intent " + "(usually PERCEPTUAL)", + intent, decode_intent( intent ), + output_profile_filename ); + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + switch( cmsGetColorSpace( icc->out_profile ) ) { + case icSigCmykData: + out->Type = IM_TYPE_CMYK; + out->BandFmt = depth == 8 ? + IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT; + out->Bbits = depth == 8 ? IM_BBITS_BYTE : IM_BBITS_SHORT; + out->Bands = 4; + icc_format = depth == 8 ? TYPE_CMYK_8 : TYPE_CMYK_16; + break; + + case icSigRgbData: + out->Type = depth == 8 ? + IM_TYPE_RGB : IM_TYPE_RGB16; + out->BandFmt = depth == 8 ? + IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT; + out->Bbits = depth == 8 ? IM_BBITS_BYTE : IM_BBITS_SHORT; + out->Bands = 3; + icc_format = depth == 8 ? TYPE_RGB_8 : TYPE_RGB_16; + break; + + default: + im_error( "im_icc_export", _( "unimplemented output colour " + "space 0x%x" ), cmsGetColorSpace( icc->out_profile ) ); + return( -1 ); + } + + if( !(icc->trans = cmsCreateTransform( icc->in_profile, TYPE_Lab_16, + icc->out_profile, icc_format, intent, 0 )) ) + return( -1 ); + + /* Process! + */ + if( im_wrapone( in, out, (im_wrapone_fn) export_buf, icc, NULL ) ) + return( -1 ); + + if( attach_profile( out, output_profile_filename ) ) + return( -1 ); + + return( 0 ); +} + +int +im_icc_export( IMAGE *in, IMAGE *out, + const char *output_profile_filename, int intent ) +{ + return( im_icc_export_depth( in, out, + 8, output_profile_filename, intent ) ); +} + +int +im_icc_ac2rc( IMAGE *in, IMAGE *out, const char *profile_filename ) +{ + cmsHPROFILE profile; + cmsCIEXYZ media; + + double add[3]; + double mul[3]; + + IMAGE *t[2]; + + if( !(profile = cmsOpenProfileFromFile( profile_filename, "r" )) ) + return( -1 ); + + if( !cmsTakeMediaWhitePoint( &media, profile ) ) { + im_error( "im_icc_ac2rc", _( "unable to get media " + "white point" ) ); + return( -1 ); + } + + cmsCloseProfile( profile ); + + add[0] = 0.0; + add[1] = 0.0; + add[2] = 0.0; + + mul[0] = IM_D50_X0 / (media.X * 100.0); + mul[1] = IM_D50_Y0 / (media.Y * 100.0); + mul[2] = IM_D50_Z0 / (media.Z * 100.0); + + /* Do IM_CODING_LABQ too. + */ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "im_icc_ac2rc-1", "p" ); + + if( !t1 || im_LabQ2Lab( in, t1 ) ) + return( -1 ); + + in = t1; + } + + if( im_open_local_array( out, t, 2, "im_icc_ac2rc-2", "p" ) || + im_Lab2XYZ_temp( in, t[0], IM_D50_X0, IM_D50_Y0, IM_D50_Z0 ) || + im_lintra_vec( 3, mul, t[0], add, t[1] ) || + im_XYZ2Lab_temp( t[1], out, IM_D50_X0, IM_D50_Y0, IM_D50_Z0 ) ) + return( -1 ); + + return( 0 ); +} + +#endif /*HAVE_LCMS*/ diff --git a/libsrc/colour/im_lab_morph.c b/libsrc/colour/im_lab_morph.c new file mode 100644 index 00000000..e4ac277c --- /dev/null +++ b/libsrc/colour/im_lab_morph.c @@ -0,0 +1,257 @@ +/* Morph a lab image ... adjust: + * + * - cast + * Pass in a MASK containing CIELAB readings for a neutral greyscale ... + * eg. + * + * 3 4 + * 14.23 4.8 -3.95 + * 18.74 2.76 -2.62 + * 23.46 1.4 -1.95 + * 27.53 1.76 -2.01 + * + * interpolation from this makes cast corrector ... interpolate top and + * tail towards [0,0,0] and [100,0,0] ... can be in any order (ie. need + * not be sorted on L*) + * + * - L* + * Pass in scale and offset for L* ... L*' = (L* + offset) * scale + * + * - saturation + * scale a and b by these amounts ... eg. 1.5 increases saturation ... + * useful for some gammut mapping + * + * Find the top two by generating and printing a greyscale ... find the bottom + * by printing a Macbeth and looking at a/b spread + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct { + IMAGE *in, *out; + + double L_scale, L_offset; + + double a_offset[101], b_offset[101]; + double a_scale, b_scale; + +} Params; + +static int +morph_init( Params *parm, + IMAGE *in, IMAGE *out, + double L_scale, double L_offset, + DOUBLEMASK *mask, double a_scale, double b_scale ) +{ + int i, j; + + parm->in = in; + parm->out = out; + parm->L_scale = L_scale; + parm->L_offset = L_offset; + parm->a_scale = a_scale; + parm->b_scale = b_scale; + + if( mask->xsize != 3 || mask->ysize < 1 || mask->ysize > 100 ) { + im_errormsg( "im_lab_morph: bad greyscale mask size" ); + return( -1 ); + } + for( i = 0; i < mask->ysize; i++ ) { + double L = mask->coeff[i*3]; + double a = mask->coeff[i*3 + 1]; + double b = mask->coeff[i*3 + 2]; + + if( L < 0 || L > 100 || a < -120 || a > 120 || + b < -120 || b > 120 ) { + im_errormsg( "im_lab_morph: bad greyscale mask " + "value, row %d", i ); + return( -1 ); + } + } + + /* Generate a/b offsets. + */ + for( i = 0; i <= 100; i++ ) { + double L_low = 0; + double a_low = 0; + double b_low = 0; + + double L_high = 100; + double a_high = 0; + double b_high = 0; + + /* Search for greyscale L just below i. Don't assume sorted by + * L*. + */ + for( j = 0; j < mask->ysize; j++ ) { + double L = mask->coeff[j*3]; + double a = mask->coeff[j*3 + 1]; + double b = mask->coeff[j*3 + 2]; + + if( L < i && L > L_low ) { + L_low = L; + a_low = a; + b_low = b; + } + } + + /* Search for greyscale L just above i. + */ + for( j = mask->ysize - 1; j >= 0; j-- ) { + double L = mask->coeff[j*3]; + double a = mask->coeff[j*3 + 1]; + double b = mask->coeff[j*3 + 2]; + + if( L >= i && L < L_high ) { + L_high = L; + a_high = a; + b_high = b; + } + } + + /* Interpolate. + */ + parm->a_offset[i] = a_low + + (a_high - a_low) * ((i - L_low) / (L_high - L_low)); + parm->b_offset[i] = b_low + + (b_high - b_low) * ((i - L_low) / (L_high - L_low)); + } + + return( 0 ); +} + +#define loop( TYPE ) \ +{ \ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + \ + for( x = 0; x < width; x++ ) { \ + double L = p[0]; \ + double a = p[1]; \ + double b = p[2]; \ + \ + L = IM_CLIP( 0, L, 100 ); \ + a -= parm->a_offset[(int) L]; \ + b -= parm->b_offset[(int) L]; \ + \ + L = (L + parm->L_offset) * parm->L_scale; \ + L = IM_CLIP( 0, L, 100 ); \ + \ + a *= parm->a_scale; \ + b *= parm->b_scale; \ + \ + q[0] = L; \ + q[1] = a; \ + q[2] = b; \ + \ + p += 3; \ + q += 3; \ + } \ +} + +static void +morph_buffer( float *in, float *out, int width, Params *parm ) +{ + int x; + + switch( parm->in->BandFmt ) { + case IM_BANDFMT_FLOAT: loop( float ); break; + case IM_BANDFMT_DOUBLE: loop( double ); break; + default: assert( 0 ); + } +} + +/* Morph an image. + */ +int +im_lab_morph( IMAGE *in, IMAGE *out, + DOUBLEMASK *mask, + double L_offset, double L_scale, + double a_scale, double b_scale ) +{ + Params *parm; + + /* Recurse for coded images. + */ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "im_lab_morph:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_lab_morph:2", "p" ); + + if( !t1 || !t2 || + im_LabQ2Lab( in, t1 ) || + im_lab_morph( t1, t2, + mask, L_offset, L_scale, a_scale, b_scale ) || + im_Lab2LabQ( t2, out ) ) + return( -1 ); + + return( 0 ); + } + + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_lab_morph: must be uncoded or IM_CODING_LABQ" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_FLOAT && in->BandFmt != IM_BANDFMT_DOUBLE ) { + im_errormsg( "im_lab_morph: must be uncoded float or double" ); + return( -1 ); + } + if( in->Bands != 3 ) { + im_errormsg( "im_lab_morph: must be 3 bands" ); + return( -1 ); + } + + if( !(parm = IM_NEW( out, Params )) || + morph_init( parm, + in, out, L_scale, L_offset, mask, a_scale, b_scale ) ) + return( -1 ); + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_LAB; + + if( im_wrapone( in, out, + (im_wrapone_fn) morph_buffer, parm, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/colour/man3/Makefile.am b/libsrc/colour/man3/Makefile.am new file mode 100644 index 00000000..ff5338ac --- /dev/null +++ b/libsrc/colour/man3/Makefile.am @@ -0,0 +1,63 @@ +man_MANS = \ + im_LCh2Lab.3 \ + im_LCh2UCS.3 \ + im_Lab2LCh.3 \ + im_Lab2LabQ.3 \ + im_Lab2LabS.3 \ + im_Lab2UCS.3 \ + im_Lab2XYZ.3 \ + im_XYZ2Yxy.3 \ + im_Yxy2XYZ.3 \ + im_Lab2disp.3 \ + im_LabQ2Lab.3 \ + im_LabQ2LabS.3 \ + im_LabQ2XYZ.3 \ + im_LabQ2disp.3 \ + im_LabQ2disp_build_table.3 \ + im_LabQ2disp_table.3 \ + im_LabS2LabQ.3 \ + im_LabS2Lab.3 \ + im_UCS2LCh.3 \ + im_UCS2Lab.3 \ + im_UCS2XYZ.3 \ + im_XYZ2Lab.3 \ + im_XYZ2UCS.3 \ + im_XYZ2disp.3 \ + im_col_C2Cucs.3 \ + im_col_Ch2ab.3 \ + im_col_Ch2hucs.3 \ + im_col_Chucs2h.3 \ + im_col_Cucs2C.3 \ + im_col_L2Lucs.3 \ + im_col_Lab2XYZ.3 \ + im_col_Lucs2L.3 \ + im_col_XYZ2Lab.3 \ + im_col_XYZ2rgb.3 \ + im_col_ab2Ch.3 \ + im_col_dECMC.3 \ + im_col_display.3 \ + im_col_make_tables_RGB.3 \ + im_col_make_tables_UCS.3 \ + im_col_pythagoras.3 \ + im_col_rgb2XYZ.3 \ + im_lab_morph.3 \ + im_dE_fromLab.3 \ + im_dE00_fromLab.3 \ + im_dECMC_fromLab.3 \ + im_dECMC_fromdisp.3 \ + im_dE_fromXYZ.3 \ + im_dE_fromdisp.3 \ + im_disp2Lab.3 \ + im_disp2XYZ.3 \ + im_XYZ2sRGB.3 \ + im_icc_present.3 \ + im_icc_transform.3 \ + im_icc_import.3 \ + im_icc_import_embedded.3 \ + im_icc_export.3 \ + im_icc_export_depth.3 \ + im_icc_ac2rc.3 \ + im_sRGB2XYZ.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/colour/man3/im_LCh2Lab.3 b/libsrc/colour/man3/im_LCh2Lab.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_LCh2Lab.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_LCh2UCS.3 b/libsrc/colour/man3/im_LCh2UCS.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_LCh2UCS.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_Lab2LCh.3 b/libsrc/colour/man3/im_Lab2LCh.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_Lab2LCh.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_Lab2LabQ.3 b/libsrc/colour/man3/im_Lab2LabQ.3 new file mode 100644 index 00000000..97ddbcbb --- /dev/null +++ b/libsrc/colour/man3/im_Lab2LabQ.3 @@ -0,0 +1,61 @@ +.TH IM_XYZ2disp 3 "2 Decemder 1992" +.SH NAME +im_LabQ2Lab, im_Lab2LabQ, im_LabQ2LabS, im_LabS2LabQ, im_Lab2LabS, im_LabS2Lab \- pack and unpack LABPACK images. +.SH SYNOPSIS +#include +.br +#include + +int im_LabQ2Lab(in, out) +.br +IMAGE *in, *out; + +int im_Lab2LabQ(in, out) +.br +IMAGE *in, *out; + +int im_Lab2LabS(in, out) +.br +IMAGE *in, *out; + +int im_LabS2LabQ(in, out) +.br +IMAGE *in, *out; + +int im_LabS2Lab(in, out) +.br +IMAGE *in, *out; + +int im_LabQ2LabS(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +These functions pack and unpack LAB images. + +LabQ is Lab packed in to 4 unsigned chars, with the Coding field set to +LABPACK. It counts as a coded type, since most operations will not give the +correct result on an image of this type. This is the MARC image type. Bits +are allocated as 10 for L and 11 for each of a and b. The first three bytes +contain the 8 most significant bits of Lab respectively, the final byte has +2/3/3 bits (MSB on left) of Lab respectively. + +im_LabQ2Lab() and im_Lab2LabQ() convert LABPACK images to three band float +images, scaled to look sensible to humans. This is the most convenient LAB +format for development work, but is rather slow. + +im_LabQ2LabS() and im_LabS2LabQ() convert LABPACK to and from three band +signed short images. L is shifted and masked to be in the range [0,32767], a +and b are shifted and masked to lie in [-32768,32767]. This is the best +computational LAB format, combining precision and speed. Programs such as +conv(1X) and similarity(1X), which can operate directly on LABPACK images, +unpack to LabS for computation. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_dE_fromdisp(3), im_XYZ2disp(3). +.SH COPYRIGHT +National Gallery, 1990 - 1993 +.SH AUTHOR +J. Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_Lab2LabS.3 b/libsrc/colour/man3/im_Lab2LabS.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_Lab2LabS.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_Lab2UCS.3 b/libsrc/colour/man3/im_Lab2UCS.3 new file mode 100644 index 00000000..df1c0963 --- /dev/null +++ b/libsrc/colour/man3/im_Lab2UCS.3 @@ -0,0 +1,72 @@ +.TH IM_LAB2UCS 3 "2 December 1992" +.SH NAME +im_Lab2UCS, im_LabQ2XYZ, im_UCS2Lab, im_Lab2disp, im_disp2Lab, im_UCS2XYZ, +im_XYZ2UCS \- derived colour space conversion functions +.SH SYNOPSIS +#include +.br +#include + +int im_Lab2UCS(in, out) +.br +IMAGE *in, *out; + +int im_LabQ2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_UCS2Lab(in, out) +.br +IMAGE *in, *out; + +int im_Lab2disp(in, out, display) +.br +IMAGE *in, *out; +.br +struct im_col_display *display; + +int im_disp2Lab(in, out, display) +.br +IMAGE *in, *out; +.br +struct im_col_display *display; + +int im_UCS2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_XYZ2UCS(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +These functions are built on the basic VIPS colour space transformations as a +convenience for the programmer. See im_Lab2XYZ(3) for an explanation of the +colour spaces and the basic conversion functions. + +im_Lab2UCS(3), for example, is defined as: + + int + im_Lab2UCS( IMAGE *in, IMAGE *out ) + { + IMAGE *t1 = im_open_local( out, + "im_Lab2UCS intermediate", "p" ); + + if( !t1 || + im_Lab2LCh( in, t1 ) || + im_LCh2UCS( t1, out ) ) + return( -1 ); + + return( 0 ); + } + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_dE_fromLab(3), im_LabQ2Lab(3), im_Lab2disp(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1990 - 1993 +.SH AUTHOR +K. Martinez \- 2/12/1992 +.br +J. Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_Lab2XYZ.3 b/libsrc/colour/man3/im_Lab2XYZ.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_Lab2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_Lab2disp.3 b/libsrc/colour/man3/im_Lab2disp.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_Lab2disp.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_LabQ2Lab.3 b/libsrc/colour/man3/im_LabQ2Lab.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2Lab.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_LabQ2LabS.3 b/libsrc/colour/man3/im_LabQ2LabS.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2LabS.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_LabQ2XYZ.3 b/libsrc/colour/man3/im_LabQ2XYZ.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_LabQ2disp.3 b/libsrc/colour/man3/im_LabQ2disp.3 new file mode 100644 index 00000000..0743fe39 --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2disp.3 @@ -0,0 +1,34 @@ +.TH IM_LABQ2DISP 3 "2 Decemder 1997" +.SH NAME +im_LabQ2disp, im_LabQ2disp_build_table, im_LabQ2disp_table \- convert LabQ to display rgb quickly and badly +.SH SYNOPSIS +#include +.br +#include + +int im_LabQ2disp( IMAGE *in, IMAGE *out, struct im_col_display *d ); + +void *im_LabQ2disp_build_table( IMAGE *out, struct im_col_display *d ); + +int im_LabQ2disp_table( IMAGE *in, IMAGE *out, void *table ); + +.SH DESCRIPTION +These functions convert LabQ images to displayable RGB as quickly as possible. + +im_LabQ2disp() converts in to out using the display profile d. It has to build +a large lookup table, so takes a while to start. + +im_LabQ2disp_build_table() is just the table-build phase of im_LabQ2disp(). It +returns a handle to the built table (or NULL for error). The memory for the +table is allocated local to out (ie is freed when out is closed). + +im_LabQ2disp_table() converts in to out using the supplied table. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_LabQ2Lab(3), im_Lab2XYZ(3), im_XYZ2disp(3) +.SH COPYRIGHT +National Gallery, 1990 - 1997 +.SH AUTHOR +J. Cupitt \- 21/7/97 diff --git a/libsrc/colour/man3/im_LabQ2disp_build_table.3 b/libsrc/colour/man3/im_LabQ2disp_build_table.3 new file mode 100644 index 00000000..ce04ce4a --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2disp_build_table.3 @@ -0,0 +1 @@ +.so man3/im_LabQ2disp.3 diff --git a/libsrc/colour/man3/im_LabQ2disp_table.3 b/libsrc/colour/man3/im_LabQ2disp_table.3 new file mode 100644 index 00000000..ce04ce4a --- /dev/null +++ b/libsrc/colour/man3/im_LabQ2disp_table.3 @@ -0,0 +1 @@ +.so man3/im_LabQ2disp.3 diff --git a/libsrc/colour/man3/im_LabS2Lab.3 b/libsrc/colour/man3/im_LabS2Lab.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_LabS2Lab.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_LabS2LabQ.3 b/libsrc/colour/man3/im_LabS2LabQ.3 new file mode 100644 index 00000000..9c69e1f5 --- /dev/null +++ b/libsrc/colour/man3/im_LabS2LabQ.3 @@ -0,0 +1 @@ +.so man3/im_Lab2LabQ.3 diff --git a/libsrc/colour/man3/im_UCS2LCh.3 b/libsrc/colour/man3/im_UCS2LCh.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_UCS2LCh.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_UCS2Lab.3 b/libsrc/colour/man3/im_UCS2Lab.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_UCS2Lab.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_UCS2XYZ.3 b/libsrc/colour/man3/im_UCS2XYZ.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_UCS2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_XYZ2Lab.3 b/libsrc/colour/man3/im_XYZ2Lab.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2Lab.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_XYZ2UCS.3 b/libsrc/colour/man3/im_XYZ2UCS.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2UCS.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_XYZ2Yxy.3 b/libsrc/colour/man3/im_XYZ2Yxy.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2Yxy.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_XYZ2disp.3 b/libsrc/colour/man3/im_XYZ2disp.3 new file mode 100644 index 00000000..3bfe3879 --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2disp.3 @@ -0,0 +1,144 @@ +.TH IM_XYZ2disp 3 "2 Decemder 1992" +.SH NAME +im_XYZ2disp, im_disp2XYZ, im_Lab2XYZ, im_XYZ2Lab, im_XYZ2Yxy, im_Yxy2XYZ, +im_XYZ2sRGB, im_sRGB2XYZ, +im_Lab2LCh, im_LCh2Lab, im_LCh2UCS, im_UCS2LCh \- convert images between +various colour spaces +.SH SYNOPSIS +#include +.br +#include + +int im_XYZ2disp(in, out, display) +.br +IMAGE *in, *out; +.br +struct im_col_display *display; + +int im_disp2XYZ(in, out, display) +.br +IMAGE *in, *out; +.br +struct im_col_display *display; + +int im_Lab2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_XYZ2Lab(in, out) +.br +IMAGE *in, *out; + +int im_XYZ2Yxy(in, out) +.br +IMAGE *in, *out; + +int im_Yxy2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_XYZ2sRGB(in, out) +.br +IMAGE *in, *out; + +int im_sRGB2XYZ(in, out) +.br +IMAGE *in, *out; + +int im_Lab2LCh(in, out) +.br +IMAGE *in, *out; + +int im_LCh2Lab(in, out) +.br +IMAGE *in, *out; + +int im_LCh2UCS(in, out) +.br +IMAGE *in, *out; + +int im_UCS2LCh(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +Functions to convert images between the different colour spaces supported by +VIPS: RGB, sRGB, XYZ, Yxy, Lab, LCh and UCS. RGB and sRGB are three band uchar +files. XYZ, Lab, LCh and UCS are three band float files. + +These are the basic conversion routines provided by VIPS. Other conversions, +such as im_Lab2disp(3), are built by composing these functions and are provided +as a convenience to the programmer. + +The VIPS colour spaces inter-convert as follows: + + +------- sRGB + | + LabQ ----- Lab ----- XYZ ----- RGB + | | | + | | +------- Yxy + | | + | +------- LCh ----- UCS + | + +-------- LabS + +The colour spaces are: + +LabQ --- This is the principal VIPS colorimetric storage format. See +im_LabQ2Lab(3) for an explanation. You cannot perform calculations on LabQ +images. They are for storage only. + +LabS --- This format represents coordinates in CIE Lab space as 16-bit +integers. It is the best format for computation, being relatively compact, +quick, and accurate. Colour values expressed in this way are hard to +visualise. See the page for im_LabQ2LabS(). + +Lab --- Coordinates in CIE Lab space are represented as float values in the +usual range. This is the easiest format for general work: adding 50 to the L +channel, for example, has the expected result. + +XYZ --- CIE XYZ colour space. + +Yxy --- CIE Yxy colour space. + +RGB --- This format is compatible with the RGB colour systems used in other +packages. If you want to export your image to a PC, for example, convert your +colorimetric image to RGB, then turn it to TIFF with vips2TIFF(1). You need to +supply a structure which characterises your display. See the manual page for +im_col_XYZ2rgb(3) for hints on these guys. + +sRGB --- This is a standard RGB, as defined by: + + http://www.color.org/contrib/sRGB.html + +it's handy for carrying colour information through JPEG compressors, for +example. + +LCh --- Like Lab, but the rectangular ab coordinates are replaced with the +polar Ch (Chroma and hue) coordinates. Hue angles are expressed in degrees. + +UCS --- A colour space based on the CMC(1:1) colour difference measurement. +This is a highly uniform colour space, much better than Lab for expressing +small differences. Conversions to and from UCS are extremely slow. + +These conversions set the Type field in the image header to LABQ, LABS, LAB, +XYZ, RGB, sRGB, LCH and UCS respectively. The Type field is for user +information only --- no function reads the Type field to determine its +behaviour. Visualisation programs, such as ip(1), use the Type field to help +present the information to the user. + +All VIPS colour spaces are based around D65. + +VIPS has functions for finding colour difference images. See +im_dE_fromLab(3). + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_dE_fromLab(3), im_LabQ2Lab(3), im_Lab2disp(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1990 - 1993 +.SH AUTHOR +K. Martinez \- 2/12/1992 +.br +J. Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_XYZ2sRGB.3 b/libsrc/colour/man3/im_XYZ2sRGB.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_XYZ2sRGB.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_Yxy2XYZ.3 b/libsrc/colour/man3/im_Yxy2XYZ.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_Yxy2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_col_C2Cucs.3 b/libsrc/colour/man3/im_col_C2Cucs.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_C2Cucs.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Ch2ab.3 b/libsrc/colour/man3/im_col_Ch2ab.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Ch2ab.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Ch2hucs.3 b/libsrc/colour/man3/im_col_Ch2hucs.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Ch2hucs.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Chucs2h.3 b/libsrc/colour/man3/im_col_Chucs2h.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Chucs2h.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Cucs2C.3 b/libsrc/colour/man3/im_col_Cucs2C.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Cucs2C.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_L2Lucs.3 b/libsrc/colour/man3/im_col_L2Lucs.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_L2Lucs.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Lab2XYZ.3 b/libsrc/colour/man3/im_col_Lab2XYZ.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Lab2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_Lucs2L.3 b/libsrc/colour/man3/im_col_Lucs2L.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_Lucs2L.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_XYZ2Lab.3 b/libsrc/colour/man3/im_col_XYZ2Lab.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_XYZ2Lab.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_XYZ2rgb.3 b/libsrc/colour/man3/im_col_XYZ2rgb.3 new file mode 100644 index 00000000..6c273ff2 --- /dev/null +++ b/libsrc/colour/man3/im_col_XYZ2rgb.3 @@ -0,0 +1,145 @@ +.TH IM_COL_XYZ2RGB 3 "2 December 1992" +.SH NAME +im_col_Lab2LCh, im_col_LCh2ab, im_col_Lab2XYZ, im_col_XYZ2Lab, +im_col_pythagoras, im_col_display, im_col_XYZ2rgb, im_col_rgb2XYZ, +im_col_L2Lucs, im_col_Lucs2L, im_col_C2Cucs, im_col_Cucs2C, im_col_Ch2hucs, +im_col_Chucs2h, im_col_make_tables_UCS, im_col_dECMC +\- colour space conversion +.SH SYNOPSIS +#include +.br +#include + + +int im_col_ab2Ch( a, b, C, h ) +.br +float a, b, *C, *h; + + +int im_col_Ch2ab( C, h, a, b ) +.br +float C, h, *a, *b; + +int im_col_Lab2XYZ( L, a, b, X, Y, Z ) +.br +float L, a, b, *X, *Y, *Z; + +int im_col_XYZ2Lab( X, Y, Z, L, a, b ) +.br +float X, Y, Z, *L, *a, *b; + +float im_col_pythagoras( L1, a1, b1, L2, a2, b2 ) +.br +float L1, a1, b1, L2, a2, b2; + +extern struct im_col_display *im_col_displays[]; + +struct im_col_tab_disp *im_col_make_tables_RGB( im, display ) +.br +IMAGE *im; +.br +struct im_col_display *display; + +int im_col_XYZ2rgb( display, table, X, Y, Z, r, g, b, oflow ) +.br +struct im_col_display *display; +.br +struct im_col_tab_disp *table; +.br +float X, Y, Z; +.br +int *r, *g, *b; +.br +int *oflow; + + +int im_col_rgb2XYZ( display, table, r, g, b, X, Y, Z ) +.br +struct im_col_display *display; +.br +struct im_col_tab_disp *table; +.br +int r, g, b; +.br +float *X, *Y, *Z; + +float im_col_L2Lucs( L ) +.br +float L; + +float im_col_Lucs2L( Lucs ) +.br +float Lucs; + +float im_col_C2Cucs( C ) +.br +float C; + +float im_col_Cucs2C( Cucs ) +.br +float Cucs; + +float im_col_Ch2hucs( C, h ) +.br +float h, C; + +float im_col_Chucs2h( C, hucs ) +.br +float hucs, C; + +void im_col_make_tables_UCS( void ) + +float im_col_dECMC( L1, a1, b1, L2, a2, b2 ) +.br +float L1, a1, b1, L2, a2, b2; + +.SH DESCRIPTION +Colour space conversion. +These functions convert colour values between four different formats: XYZ +(float), Lab (float), UCS (float), and RGB (unsigned char) +displayable. Additionally, functions are provided to move from (a,b)-style +rectangular colour coordinates to (C,h)-style coordinates. h is always +in degrees. + +UCS is a colour space derived from the CMC(1:1) equations. There is no easy +analytical conversion from UCS to Lab, so look-up tables are used. These have +to be built with a call to im_col_make_tables_UCS(). Once built, these +tables are shared by all UCS functions. You may call im_col_make_tables_UCS() +many times - tables are only built on the first call. + +im_col_pythagoras() returns the pythagoran distance between two points in a +colour space. It can be used for finding CIELAB delta E's. im_col_dECMC() +returns the colour difference between two LAB points in CMC(1:1). + +An im_col_display structure characterises a CRT screen (see ). +You can make up your own (if you can find a TV analyser), or use one of the +structures provied by VIPS in the NULL-terminated array im_col_displays[]. See +the source for disp2XYZ(1) for ideas on extracting a display struct from this +list. + +im_make_tables_RGB(3) has a display type as argument, and returns a pointer +to the structure im_col_tab_RGB. This latter contains the matrices to go from +XYZ to luminances (and back), and the tables to go from the luminances (in r, +g, b) to the effective signal values to be applied to the monitor input (and +back). The function returns NULL on error. The IMAGE argument is passed on to +im_malloc() to make the space required for the tables. Pass either NULL (if +you need to free the memory yourself) or an IMAGE descriptor (if you want the +memory to be freed automatically when that descriptor is closed). + +im_col_XYZ2rgb() takes a display, a look-up table and an XYZ coordinate are +returns three values in the range 0-255. The extra value oflow is set to 0 if +the specified XYZ position aflls within the display gamut, and to 1 if the +point lies outside the gamut. im_col_rgb2XYZ() is the reverse transformation. +.SH RETURN VALUE +The functions (usually) return 0 on success and -1 on error. +.SH SEE\ ALSO +.nf +im_XYZ2disp(3), im_dE_fromdisp(3). +.SH COPYRIGHT +National Gallery, 1990-1993. +.SH AUTHOR +D. Saunders \- 1988 +.br +J.Ph. Laurent \- 2/12/1992 +.br +J.Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_col_ab2Ch.3 b/libsrc/colour/man3/im_col_ab2Ch.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_ab2Ch.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_dECMC.3 b/libsrc/colour/man3/im_col_dECMC.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_dECMC.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_display.3 b/libsrc/colour/man3/im_col_display.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_display.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_make_tables_RGB.3 b/libsrc/colour/man3/im_col_make_tables_RGB.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_make_tables_RGB.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_make_tables_UCS.3 b/libsrc/colour/man3/im_col_make_tables_UCS.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_make_tables_UCS.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_pythagoras.3 b/libsrc/colour/man3/im_col_pythagoras.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_pythagoras.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_col_rgb2XYZ.3 b/libsrc/colour/man3/im_col_rgb2XYZ.3 new file mode 100644 index 00000000..3426e329 --- /dev/null +++ b/libsrc/colour/man3/im_col_rgb2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_col_XYZ2rgb.3 diff --git a/libsrc/colour/man3/im_dE00_fromLab.3 b/libsrc/colour/man3/im_dE00_fromLab.3 new file mode 100644 index 00000000..0f335f81 --- /dev/null +++ b/libsrc/colour/man3/im_dE00_fromLab.3 @@ -0,0 +1 @@ +.so man3/im_dE_fromLab.3 diff --git a/libsrc/colour/man3/im_dECMC_fromLab.3 b/libsrc/colour/man3/im_dECMC_fromLab.3 new file mode 100644 index 00000000..0f335f81 --- /dev/null +++ b/libsrc/colour/man3/im_dECMC_fromLab.3 @@ -0,0 +1 @@ +.so man3/im_dE_fromLab.3 diff --git a/libsrc/colour/man3/im_dECMC_fromdisp.3 b/libsrc/colour/man3/im_dECMC_fromdisp.3 new file mode 100644 index 00000000..0d3cc14a --- /dev/null +++ b/libsrc/colour/man3/im_dECMC_fromdisp.3 @@ -0,0 +1 @@ +.so man3/im_dE_fromdisp.3 diff --git a/libsrc/colour/man3/im_dE_fromLab.3 b/libsrc/colour/man3/im_dE_fromLab.3 new file mode 100644 index 00000000..e8c0291c --- /dev/null +++ b/libsrc/colour/man3/im_dE_fromLab.3 @@ -0,0 +1,44 @@ +.TH im_dE_fromLab 3 "2 December 1992" +.SH NAME +im_dE_fromLab, im_dECMC_fromLab, im_dE00_fromLab \- calculate colour differences +.SH SYNOPSIS +#include +.br +#include + +int im_dE_fromLab(in1, in2, out) +.br +IMAGE *in1, *in2, *out; + +int im_dECMC_fromLab(in1, in2, out) +.br +IMAGE *in1, *in2, *out; + +int im_dE00_fromLab(in1, in2, out) +.br +IMAGE *in1, *in2, *out; + +.SH DESCRIPTION +Given a pair of images in Lab format, these functions calculate CIE76, +CMC(1:1) and CIEDE2000 colour difference images. These are the basic colour +difference +calculators in VIPS --- other colour difference functions, such as +.B im_dE_fromXYZ(3), +are provided as a convenience to the programmer. + +See +.B im_Lab2XYZ(3) +for an explanation of the various colour spaces VIPS +supports and how to convert between them. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_XYZ2rgb(3), im_dE_fromXYZ(3). +.SH COPYRIGHT +National Gallery, 1990 - 1993 +.SH AUTHOR +J.Ph. Laurent \- 2/12/1992 +.br +J. Cupitt \- 21/7/93 + diff --git a/libsrc/colour/man3/im_dE_fromXYZ.3 b/libsrc/colour/man3/im_dE_fromXYZ.3 new file mode 100644 index 00000000..0d3cc14a --- /dev/null +++ b/libsrc/colour/man3/im_dE_fromXYZ.3 @@ -0,0 +1 @@ +.so man3/im_dE_fromdisp.3 diff --git a/libsrc/colour/man3/im_dE_fromdisp.3 b/libsrc/colour/man3/im_dE_fromdisp.3 new file mode 100644 index 00000000..02ae7763 --- /dev/null +++ b/libsrc/colour/man3/im_dE_fromdisp.3 @@ -0,0 +1,40 @@ +.TH IM_DE_FROMDISP 3 "2 December 1992" +.SH NAME +im_dE_fromdisp, im_dE_fromXYZ, im_dECMC_fromdisp \- derived colour difference +functions +.SH SYNOPSIS +#include +.br +#include + +im_dE_fromdisp( in1, in2, out, display ) +.br +IMAGE *in1, *in2, *out; +.br +struct im_col_display *display; + +int im_dE_fromXYZ(in, out) +.br +IMAGE *in, *out; + +im_dECMC_fromdisp( in1, in2, out, display ) +.br +IMAGE *in1, *in2, *out; +.br +struct im_col_display *display; + +.SH DESCRIPTION +These functions are built on the basic VIPS colour difference measurements as a +convenience for the programmer. See im_dE_fromLab(3) for an explanation of the +colour difference metrics and the basic comparision functions. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_col_XYZ2rgb(3), im_dE_fromLab(3), im_LabQ2Lab(3), im_Lab2disp(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1990 - 1993 +.SH AUTHOR +K. Martinez \- 2/12/1992 +.br +J. Cupitt \- 21/7/93 diff --git a/libsrc/colour/man3/im_disp2Lab.3 b/libsrc/colour/man3/im_disp2Lab.3 new file mode 100644 index 00000000..cb1e992d --- /dev/null +++ b/libsrc/colour/man3/im_disp2Lab.3 @@ -0,0 +1 @@ +.so man3/im_Lab2UCS.3 diff --git a/libsrc/colour/man3/im_disp2XYZ.3 b/libsrc/colour/man3/im_disp2XYZ.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_disp2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/colour/man3/im_icc_ac2rc.3 b/libsrc/colour/man3/im_icc_ac2rc.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_ac2rc.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_export.3 b/libsrc/colour/man3/im_icc_export.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_export.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_export_depth.3 b/libsrc/colour/man3/im_icc_export_depth.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_export_depth.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_import.3 b/libsrc/colour/man3/im_icc_import.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_import.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_import_embedded.3 b/libsrc/colour/man3/im_icc_import_embedded.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_import_embedded.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_present.3 b/libsrc/colour/man3/im_icc_present.3 new file mode 100644 index 00000000..4e5ea9dc --- /dev/null +++ b/libsrc/colour/man3/im_icc_present.3 @@ -0,0 +1 @@ +.so man3/im_icc_transform.3 diff --git a/libsrc/colour/man3/im_icc_transform.3 b/libsrc/colour/man3/im_icc_transform.3 new file mode 100644 index 00000000..24eae259 --- /dev/null +++ b/libsrc/colour/man3/im_icc_transform.3 @@ -0,0 +1,118 @@ +.TH IM_ICC_*() 3 "April 2002" +.SH NAME +im_icc_present, im_icc_transform, im_icc_import, im_icc_import_embedded, +im_icc_export, +im_icc_export_depth \- transform images using ICC profiles +.SH SYNOPSIS +#include +.br +#include + +#define VIPS_INTENT_PERCEPTUAL (0) +.br +#define VIPS_INTENT_RELATIVE_COLORIMETRIC (1) +.br +#define VIPS_INTENT_SATURATION (2) +.br +#define VIPS_INTENT_ABSOLUTE_COLORIMETRIC (3) + +int +.br +im_icc_present( void ) +.br +int +.br +im_icc_transform( IMAGE *in, IMAGE *out, +.br + const char *input_profile_filename, +.br + const char *output_profile_filename, +.br + int intent ) + +int +.br +im_icc_import( IMAGE *in, IMAGE *out, +.br + const char *input_profile_filename, int intent ) + +int +.br +im_icc_import_embedded( IMAGE *in, IMAGE *out, +.br + int intent ) + +int +.br +im_icc_export_depth( IMAGE *in, IMAGE *out, int depth, +.br + const char *output_profile_filename, int intent ) + +int +.br +im_icc_export( IMAGE *in, IMAGE *out, +.br + const char *output_profile_filename, int intent ) + +int +.br +im_icc_ac2rc( IMAGE *in, IMAGE *out, +.br + const char *profile_filename ) + +.SH DESCRIPTION +.B im_icc_present(3) +returns non-zero if there is an ICC library available. Calls to the other +VIPS ICC functions will all fail with an error message if there is no library. + +.B im_icc_transform(3) +maps between two images using an input and output profile and an intent. The +input image must have a format matching the input profile (eg. 4 bands for a +CMYK profile). The output image will have a form matching the output profile +(eg. 3 bands for an RGB output profile). The input image must be 8 or 16 bit +unsigned integer. The output image is always 8 bit unsigned int. The output +profile is attached to the output image under the name +.B "icc-profile-data". +Functions like +.B im_vips2jpeg(3) +will then attach the profile to the files they create. + +.B im_icc_import(3) +takes an image to D50 Lab float (profile interconnect space) from device space. +The input image must match the format expected by the profile: for example, a +printer profile will usually need 4 band CMYK data. The input image must be 8 +or 16 bit unsigned integer. + +.B im_icc_import_embedded(3) +takes an image to D50 Lab float (profile interconnect space) from device +space, using the profile embedded in the image. If there is no embedded +profile, an error is returned. Test for the presence of a profile with +.B im_header_get_type(3). + +.B im_icc_export_depth(3) +takes an image from D50 Lab float back to device space. The output image will +match the format of the profile: for example, a screen profile will write 3 +band RGB data. The output image biut depth can be set to 8 or 16 with the +depth parameter. The profile is attached to the output image under +the name +.B "icc-profile-data". +Functions like +.B im_vips2jpeg(3) +will then attach the profile to the files they create. + +.B im_icc_export(3) +behaves just as +.B im_icc_export_depth(), +but with depth always 8 bit. + +.B im_icc_ac2rc(3) +converts an image from D50 absolute to media relative colorimetry using the +media white point found in the ICC profile. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH SEE ALSO +im_LabQ2Lab(3), im_Lab2XYZ(3). +.SH COPYRIGHT +The National Gallery + diff --git a/libsrc/colour/man3/im_lab_morph.3 b/libsrc/colour/man3/im_lab_morph.3 new file mode 100644 index 00000000..ac5563ba --- /dev/null +++ b/libsrc/colour/man3/im_lab_morph.3 @@ -0,0 +1,44 @@ +.TH IM_LAB_MORPH 3 "8 March 2001" +.SH NAME +im_lab_morph \- calculate colour differences +.SH SYNOPSIS +#include + +int im_lab_morph( IMAGE *in, IMAGE *out, +.br + DOUBLEMASK *mask, +.br + double L_offset, double L_scale, +.br + double a_scale, double b_scale ) + +.SH DESCRIPTION +This function tweaks the colour in an LAB image. It's useful for +making a 'tweaked' image for sending to a colour printer. + +It performs three corrections: first, it straightens the neutral axis (this is +useful if your printer tends to tint shadows slightly red, for example); it +moves L* by adding an offset and then multiplying by a scale (useful if your +printer thinks in relative colorimetry, for example), and finally scales a* +and b* by a factor (useful if your printer desaturates to avoid gamut +clipping). + +The neutral axis straightening is specified as a DOUBLEMASK containing L*, a* +and b* readings taken from a print of a neutral greyscale (one with a* and b* +zero). For example: + + 3 4 + 14.23 4.8 -3.95 + 18.74 2.76 -2.62 + 23.46 1.4 -1.95 + 27.53 1.76 -2.01 + +This is interpolated to make an a/b offset for each input value of L*. The top +and tail are interpolated towards [100,0,0] and [0,0,0]. Entries in the mask +can be in any order. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH COPYRIGHT +National Gallery, 2001 + diff --git a/libsrc/colour/man3/im_sRGB2XYZ.3 b/libsrc/colour/man3/im_sRGB2XYZ.3 new file mode 100644 index 00000000..6866a230 --- /dev/null +++ b/libsrc/colour/man3/im_sRGB2XYZ.3 @@ -0,0 +1 @@ +.so man3/im_XYZ2disp.3 diff --git a/libsrc/conversion/Makefile.am b/libsrc/conversion/Makefile.am new file mode 100644 index 00000000..0f16c537 --- /dev/null +++ b/libsrc/conversion/Makefile.am @@ -0,0 +1,62 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libconversion.la + +libconversion_la_SOURCES = \ + im_bernd.c \ + im_vips2tiff.c \ + im_tiff2vips.c \ + conver_dispatch.c \ + dbh.h \ + im_bandjoin.c \ + im_analyze2vips.c \ + im_black.c \ + im_c2amph.c \ + im_c2rect.c \ + im_c2imag.c \ + im_c2ps.c \ + im_c2real.c \ + im_clip.c \ + im_copy.c \ + im_csv2vips.c \ + im_vips2csv.c \ + im_extract.c \ + im_exr2vips.c \ + im_falsecolour.c \ + im_fliphor.c \ + im_flipver.c \ + im_gbandjoin.c \ + im_insert.c \ + im_jpeg2vips.c \ + im_lrjoin.c \ + im_magick2vips.c \ + im_mask2vips.c \ + im_msb.c \ + im_png2vips.c \ + im_ppm2vips.c \ + im_recomb.c \ + im_replicate.c \ + im_grid.c \ + im_raw2vips.c \ + im_ri2c.c \ + im_rightshift_size.c \ + im_rot180.c \ + im_rot270.c \ + im_rot90.c \ + im_scale.c \ + im_scaleps.c \ + im_slice.c \ + im_subsample.c \ + im_system.c \ + im_print.c \ + im_tbjoin.c \ + im_tile_cache.c \ + im_text.c \ + im_thresh.c \ + im_vips2mask.c \ + im_vips2jpeg.c \ + im_vips2ppm.c \ + im_vips2png.c \ + im_zoom.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/conversion/conver_dispatch.c b/libsrc/conversion/conver_dispatch.c new file mode 100644 index 00000000..089d7d5d --- /dev/null +++ b/libsrc/conversion/conver_dispatch.c @@ -0,0 +1,1964 @@ +/* VIPS function dispatch tables for conversion. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +print_vec( im_object *argv ) +{ + const char *message = argv[0]; + char **out = (char **) &argv[1]; + + if( im_print( message ) ) + return( -1 ); + *out = im_strdup( NULL, "printed" ); + + return( 0 ); +} + +static im_arg_desc print_arg_types[] = { + IM_INPUT_STRING( "message" ), + IM_OUTPUT_STRING( "result" ) +}; + +static im_function print_desc = { + "im_print", /* Name */ + "print string to stdout", /* Description */ + 0, /* Flags */ + print_vec, /* Dispatch function */ + IM_NUMBER( print_arg_types ), /* Size of arg list */ + print_arg_types /* Arg list */ +}; + +static int +system_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + char *cmd = argv[1]; + char **out = (char **) &argv[2]; + + if( im_system( in, cmd, out ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc system_arg_types[] = { + IM_INPUT_IMAGE( "im" ), + IM_INPUT_STRING( "command" ), + IM_OUTPUT_STRING( "output" ) +}; + +static im_function system_desc = { + "im_system", /* Name */ + "run command on image", /* Description */ + 0, /* Flags */ + system_vec, /* Dispatch function */ + IM_NUMBER( system_arg_types ), /* Size of arg list */ + system_arg_types /* Arg list */ +}; + +static int +subsample_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + IMAGE *out = argv[1]; + int xsh = *((int *) argv[2]); + int ysh = *((int *) argv[3]); + + if( im_subsample( in, out, xsh, ysh ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc subsample_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xshrink" ), + IM_INPUT_INT( "yshrink" ) +}; + +static im_function subsample_desc = { + "im_subsample", /* Name */ + "subsample image by integer factors", /* Description */ + IM_FN_PIO, /* Flags */ + subsample_vec, /* Dispatch function */ + IM_NUMBER( subsample_args ), /* Size of arg list */ + subsample_args /* Arg list */ +}; + +/* Args to im_bernd. + */ +static im_arg_desc bernd_args[] = { + IM_INPUT_STRING( "tiffname" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ) +}; + +/* Call im_bernd via arg vector. + */ +static int +bernd_vec( im_object *argv ) +{ + char *name = argv[0]; + int left = *((int *) argv[1]); + int top = *((int *) argv[2]); + int width = *((int *) argv[3]); + int height = *((int *) argv[4]); + + return( im_bernd( name, left, top, width, height ) ); +} + +/* Description of im_bernd. + */ +static im_function bernd_desc = { + "im_bernd", /* Name */ + "extract from pyramid as jpeg", /* Description */ + 0, /* Flags */ + bernd_vec, /* Dispatch function */ + IM_NUMBER( bernd_args ), /* Size of arg list */ + bernd_args /* Arg list */ +}; + +/* Args to im_extract. + */ +static im_arg_desc extract_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "band" ) +}; + +/* Call im_extract via arg vector. + */ +static int +extract_vec( im_object *argv ) +{ + IMAGE_BOX box; + + box.xstart = *((int *) argv[2]); + box.ystart = *((int *) argv[3]); + box.xsize = *((int *) argv[4]); + box.ysize = *((int *) argv[5]); + box.chsel = *((int *) argv[6]); + + return( im_extract( argv[0], argv[1], &box ) ); +} + +/* Description of im_extract. + */ +static im_function extract_desc = { + "im_extract", /* Name */ + "extract area/band", /* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + extract_vec, /* Dispatch function */ + IM_NUMBER( extract_args ), /* Size of arg list */ + extract_args /* Arg list */ +}; + +/* Args to im_extract_area. + */ +static im_arg_desc extract_area_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ) +}; + +/* Call im_extract_area via arg vector. + */ +static int +extract_area_vec( im_object *argv ) +{ + int x = *((int *) argv[2]); + int y = *((int *) argv[3]); + int w = *((int *) argv[4]); + int h = *((int *) argv[5]); + + return( im_extract_area( argv[0], argv[1], x, y, w, h ) ); +} + +/* Description of im_extract_area. + */ +static im_function extract_area_desc = { + "im_extract_area", /* Name */ + "extract area", /* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + extract_area_vec, /* Dispatch function */ + IM_NUMBER( extract_area_args ), /* Size of arg list */ + extract_area_args /* Arg list */ +}; + +/* Args to im_extract_bands. + */ +static im_arg_desc extract_bands_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "band" ), + IM_INPUT_INT( "nbands" ), +}; + +/* Call im_extract_bands via arg vector. + */ +static int +extract_bands_vec( im_object *argv ) +{ + int chsel = *((int *) argv[2]); + int nbands = *((int *) argv[3]); + + return( im_extract_bands( argv[0], argv[1], chsel, nbands ) ); +} + +/* Description of im_extract_bands. + */ +static im_function extract_bands_desc = { + "im_extract_bands", /* Name */ + "extract several bands", /* Description */ + IM_FN_PIO, /* Flags */ + extract_bands_vec, /* Dispatch function */ + IM_NUMBER( extract_bands_args ),/* Size of arg list */ + extract_bands_args /* Arg list */ +}; + +/* Args to im_extract_band. + */ +static im_arg_desc extract_band_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "band" ) +}; + +/* Call im_extract_band via arg vector. + */ +static int +extract_band_vec( im_object *argv ) +{ + int chsel = *((int *) argv[2]); + + return( im_extract_band( argv[0], argv[1], chsel ) ); +} + +/* Description of im_extract_band. + */ +static im_function extract_band_desc = { + "im_extract_band", /* Name */ + "extract band", /* Description */ + IM_FN_PIO, /* Flags */ + extract_band_vec, /* Dispatch function */ + IM_NUMBER( extract_band_args ), /* Size of arg list */ + extract_band_args /* Arg list */ +}; + +/* Args to im_extract_areabands. + */ +static im_arg_desc extract_areabands_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "left" ), + IM_INPUT_INT( "top" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "band" ), + IM_INPUT_INT( "nbands" ) +}; + +/* Call im_extract_areabands via arg vector. + */ +static int +extract_areabands_vec( im_object *argv ) +{ + int left = *((int *) argv[2]); + int top = *((int *) argv[3]); + int width = *((int *) argv[4]); + int height = *((int *) argv[5]); + int band = *((int *) argv[6]); + int nbands = *((int *) argv[7]); + + return( im_extract_areabands( argv[0], argv[1], + left, top, width, height, band, nbands ) ); +} + +/* Description of im_extract_areabands. + */ +static im_function extract_areabands_desc = { + "im_extract_areabands", /* Name */ + "extract area and bands", /* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + extract_areabands_vec, /* Dispatch function */ + IM_NUMBER( extract_areabands_args ),/* Size of arg list */ + extract_areabands_args /* Arg list */ +}; + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_bandjoin via arg vector. + */ +static int +bandjoin_vec( im_object *argv ) +{ + return( im_bandjoin( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_bandjoin. + */ +static im_function bandjoin_desc = { + "im_bandjoin", /* Name */ + "bandwise join of two images", /* Description */ + IM_FN_PIO, /* Flags */ + bandjoin_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +static im_arg_desc gbandjoin_args[] = { + IM_INPUT_IMAGEVEC( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +static int +gbandjoin_vec( im_object *argv ) +{ + im_imagevec_object *iv = (im_imagevec_object *) argv[0]; + + return( im_gbandjoin( iv->vec, argv[1], iv->n ) ); +} + +static im_function gbandjoin_desc = { + "im_gbandjoin", /* Name */ + "bandwise join of many images", /* Description */ + IM_FN_PIO, /* Flags */ + gbandjoin_vec, /* Dispatch function */ + IM_NUMBER( gbandjoin_args ), /* Size of arg list */ + gbandjoin_args /* Arg list */ +}; + +/* Args to im_text. + */ +static im_arg_desc text_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "text" ), + IM_INPUT_STRING( "font" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "alignment" ), + IM_INPUT_INT( "dpi" ) +}; + +/* Call im_text via arg vector. + */ +static int +text_vec( im_object *argv ) +{ + int width = *((int *) argv[3]); + int alignment = *((int *) argv[4]); + int dpi = *((int *) argv[5]); + + return( im_text( argv[0], argv[1], argv[2], width, alignment, dpi ) ); +} + +/* Description of im_text. + */ +static im_function text_desc = { + "im_text", /* Name */ + "generate text image", /* Description */ + IM_FN_PIO, /* Flags */ + text_vec, /* Dispatch function */ + IM_NUMBER( text_args ), /* Size of arg list */ + text_args /* Arg list */ +}; + +/* Args to im_black. + */ +static im_arg_desc black_args[] = { + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "x_size" ), + IM_INPUT_INT( "y_size" ), + IM_INPUT_INT( "bands" ) +}; + +/* Call im_black via arg vector. + */ +static int +black_vec( im_object *argv ) +{ + int xs = *((int *) argv[1]); + int ys = *((int *) argv[2]); + int bands = *((int *) argv[3]); + + return( im_black( argv[0], xs, ys, bands ) ); +} + +/* Description of im_black. + */ +static im_function black_desc = { + "im_black", /* Name */ + "generate black image", /* Description */ + IM_FN_PIO, /* Flags */ + black_vec, /* Dispatch function */ + IM_NUMBER( black_args ), /* Size of arg list */ + black_args /* Arg list */ +}; + +/* Args to im_clip2fmt. + */ +static im_arg_desc clip2fmt_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "ofmt" ) +}; + +/* Call im_clip2fmt via arg vector. + */ +static int +clip2fmt_vec( im_object *argv ) +{ + int ofmt = *((int *) argv[2]); + + return( im_clip2fmt( argv[0], argv[1], ofmt ) ); +} + +/* Description of im_clip2fmt. + */ +static im_function clip2fmt_desc = { + "im_clip2fmt", /* Name */ + "convert image format to ofmt", /* Description */ + IM_FN_PIO, /* Flags */ + clip2fmt_vec, /* Dispatch function */ + IM_NUMBER( clip2fmt_args ), /* Size of arg list */ + clip2fmt_args /* Arg list */ +}; + +/* Call im_c2rect via arg vector. + */ +static int +c2rect_vec( im_object *argv ) +{ + return( im_c2rect( argv[0], argv[1] ) ); +} + +/* Description of im_c2rect. + */ +static im_function c2rect_desc = { + "im_c2rect", /* Name */ + "convert phase and amplitude to real and imaginary", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2rect_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_c2amph via arg vector. + */ +static int +c2amph_vec( im_object *argv ) +{ + return( im_c2amph( argv[0], argv[1] ) ); +} + +/* Description of im_c2amph. + */ +static im_function c2amph_desc = { + "im_c2amph", /* Name */ + "convert real and imaginary to phase and amplitude", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2amph_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2dcm via arg vector. + */ +static int +clip2dcm_vec( im_object *argv ) +{ + return( im_clip2dcm( argv[0], argv[1] ) ); +} + +/* Description of im_clip2dcm. + */ +static im_function clip2dcm_desc = { + "im_clip2dcm", /* Name */ + "convert to double complex", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2dcm_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2cm via arg vector. + */ +static int +clip2cm_vec( im_object *argv ) +{ + return( im_clip2cm( argv[0], argv[1] ) ); +} + +/* Description of im_clip2cm. + */ +static im_function clip2cm_desc = { + "im_clip2cm", /* Name */ + "convert to complex", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2cm_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2us via arg vector. + */ +static int +clip2us_vec( im_object *argv ) +{ + return( im_clip2us( argv[0], argv[1] ) ); +} + +/* Description of im_clip2us. + */ +static im_function clip2us_desc = { + "im_clip2us", /* Name */ + "convert to unsigned 16-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2us_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2ui via arg vector. + */ +static int +clip2ui_vec( im_object *argv ) +{ + return( im_clip2ui( argv[0], argv[1] ) ); +} + +/* Description of im_clip2ui. + */ +static im_function clip2ui_desc = { + "im_clip2ui", /* Name */ + "convert to unsigned 32-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2ui_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2s via arg vector. + */ +static int +clip2s_vec( im_object *argv ) +{ + return( im_clip2s( argv[0], argv[1] ) ); +} + +/* Description of im_clip2s. + */ +static im_function clip2s_desc = { + "im_clip2s", /* Name */ + "convert to signed 16-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2s_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2i via arg vector. + */ +static int +clip2i_vec( im_object *argv ) +{ + return( im_clip2i( argv[0], argv[1] ) ); +} + +/* Description of im_clip2i. + */ +static im_function clip2i_desc = { + "im_clip2i", /* Name */ + "convert to signed 32-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2i_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2d via arg vector. + */ +static int +clip2d_vec( im_object *argv ) +{ + return( im_clip2d( argv[0], argv[1] ) ); +} + +/* Description of im_clip2d. + */ +static im_function clip2d_desc = { + "im_clip2d", /* Name */ + "convert to double-precision float", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2d_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2f via arg vector. + */ +static int +clip2f_vec( im_object *argv ) +{ + return( im_clip2f( argv[0], argv[1] ) ); +} + +/* Description of im_clip2f. + */ +static im_function clip2f_desc = { + "im_clip2f", /* Name */ + "convert to single-precision float", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2f_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip2c via arg vector. + */ +static int +clip2c_vec( im_object *argv ) +{ + return( im_clip2c( argv[0], argv[1] ) ); +} + +/* Description of im_clip2c. + */ +static im_function clip2c_desc = { + "im_clip2c", /* Name */ + "convert to signed 8-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip2c_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_clip via arg vector. + */ +static int +clip_vec( im_object *argv ) +{ + return( im_clip( argv[0], argv[1] ) ); +} + +/* Description of im_clip. + */ +static im_function clip_desc = { + "im_clip", /* Name */ + "convert to unsigned 8-bit integer", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + clip_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_ri2c via arg vector. + */ +static int +ri2c_vec( im_object *argv ) +{ + return( im_ri2c( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_ri2c. + */ +static im_function ri2c_desc = { + "im_ri2c", /* Name */ + "join two non-complex images to form complex", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + ri2c_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_c2imag via arg vector. + */ +static int +c2imag_vec( im_object *argv ) +{ + return( im_c2imag( argv[0], argv[1] ) ); +} + +/* Description of im_c2imag. + */ +static im_function c2imag_desc = { + "im_c2imag", /* Name */ + "extract imaginary part of complex image", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2imag_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_c2real via arg vector. + */ +static int +c2real_vec( im_object *argv ) +{ + return( im_c2real( argv[0], argv[1] ) ); +} + +/* Description of im_c2real. + */ +static im_function c2real_desc = { + "im_c2real", /* Name */ + "extract real part of complex image", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2real_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_c2ps via arg vector. + */ +static int +c2ps_vec( im_object *argv ) +{ + return( im_c2ps( argv[0], argv[1] ) ); +} + +/* Description of im_c2ps. + */ +static im_function c2ps_desc = { + "im_c2ps", /* Name */ + "find power spectrum of complex image", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + c2ps_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args to im_copy_set. + */ +static im_arg_desc copy_set_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "Type" ), + IM_INPUT_DOUBLE( "Xres" ), + IM_INPUT_DOUBLE( "Yres" ), + IM_INPUT_INT( "Xoffset" ), + IM_INPUT_INT( "Yoffset" ) +}; + +/* Call im_copy_set via arg vector. + */ +static int +copy_set_vec( im_object *argv ) +{ + int Type = *((int *) argv[2]); + float Xres = *((double *) argv[3]); + float Yres = *((double *) argv[4]); + int Xoffset = *((int *) argv[5]); + int Yoffset = *((int *) argv[6]); + + return( im_copy_set( argv[0], argv[1], + Type, Xres, Yres, Xoffset, Yoffset ) ); +} + +/* Description of im_copy_set. + */ +static im_function copy_set_desc = { + "im_copy_set", /* Name */ + "copy image, setting informational fields", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_set_vec, /* Dispatch function */ + IM_NUMBER( copy_set_args ), /* Size of arg list */ + copy_set_args /* Arg list */ +}; + +/* Args to im_copy_set_meta. + */ +static im_arg_desc copy_set_meta_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_STRING( "field" ), + IM_INPUT_GVALUE( "value" ) +}; + +/* Call im_copy_set_meta via arg vector. + */ +static int +copy_set_meta_vec( im_object *argv ) +{ + const char *field = argv[2]; + GValue *value = argv[3]; + + return( im_copy_set_meta( argv[0], argv[1], field, value ) ); +} + +/* Description of im_copy_set_meta. + */ +static im_function copy_set_meta_desc = { + "im_copy_set_meta", /* Name */ + "copy image, setting a meta field", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_set_meta_vec, /* Dispatch function */ + IM_NUMBER( copy_set_meta_args ),/* Size of arg list */ + copy_set_meta_args /* Arg list */ +}; + +/* Args to im_copy_morph. + */ +static im_arg_desc copy_morph_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "Bands" ), + IM_INPUT_INT( "BandFmt" ), + IM_INPUT_INT( "Coding" ) +}; + +/* Call im_copy_morph via arg vector. + */ +static int +copy_morph_vec( im_object *argv ) +{ + int Bands = *((int *) argv[2]); + int BandFmt = *((int *) argv[3]); + int Coding = *((int *) argv[4]); + + return( im_copy_morph( argv[0], argv[1], + Bands, BandFmt, Coding ) ); +} + +/* Description of im_copy_morph. + */ +static im_function copy_morph_desc = { + "im_copy_morph", /* Name */ + "copy image, setting pixel layout", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_morph_vec, /* Dispatch function */ + IM_NUMBER( copy_morph_args ), /* Size of arg list */ + copy_morph_args /* Arg list */ +}; + +/* Call im_copy via arg vector. + */ +static int +copy_vec( im_object *argv ) +{ + return( im_copy( argv[0], argv[1] ) ); +} + +/* Description of im_copy. + */ +static im_function copy_desc = { + "im_copy", /* Name */ + "copy image", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_copy_swap via arg vector. + */ +static int +copy_swap_vec( im_object *argv ) +{ + return( im_copy_swap( argv[0], argv[1] ) ); +} + +/* Description of im_copy_swap. + */ +static im_function copy_swap_desc = { + "im_copy_swap", /* Name */ + "copy image, swapping byte order", + + /* Can't set PTOP ... we don't want to zap the LUT, we want the real + * image. + */ + IM_FN_PIO, /* Flags */ + + copy_swap_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_fliphor via arg vector. + */ +static int +fliphor_vec( im_object *argv ) +{ + return( im_fliphor( argv[0], argv[1] ) ); +} + +/* Description of im_fliphor. + */ +static im_function fliphor_desc = { + "im_fliphor", /* Name */ + "flip image left-right", + IM_FN_PIO, /* Flags */ + fliphor_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_flipver via arg vector. + */ +static int +flipver_vec( im_object *argv ) +{ + return( im_flipver( argv[0], argv[1] ) ); +} + +/* Description of im_flipver. + */ +static im_function flipver_desc = { + "im_flipver", /* Name */ + "flip image top-bottom", + IM_FN_PIO, /* Flags */ + flipver_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_falsecolour via arg vector. + */ +static int +falsecolour_vec( im_object *argv ) +{ + return( im_falsecolour( argv[0], argv[1] ) ); +} + +/* Description of im_falsecolour. + */ +static im_function falsecolour_desc = { + "im_falsecolour", /* Name */ + "turn luminance changes into chrominance changes", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + falsecolour_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args for im_recomb. + */ +static im_arg_desc recomb_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DMASK( "matrix" ) +}; + +/* Call im_recomb via arg vector. + */ +static int +recomb_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_recomb( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_recomb. + */ +static im_function recomb_desc = { + "im_recomb", /* Name */ + "linear recombination with mask", + IM_FN_PIO, /* Flags */ + recomb_vec, /* Dispatch function */ + IM_NUMBER( recomb_args ), /* Size of arg list */ + recomb_args /* Arg list */ +}; + +/* Args for im_insert. + */ +static im_arg_desc insert_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_IMAGE( "sub" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ) +}; + +/* Call im_insert via arg vector. + */ +static int +insert_vec( im_object *argv ) +{ + int x = *((int *) argv[3]); + int y = *((int *) argv[4]); + + return( im_insert( argv[0], argv[1], argv[2], x, y ) ); +} + +/* Description of im_insert. + */ +static im_function insert_desc = { + "im_insert", /* Name */ + "insert sub-image into main image at position", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + insert_vec, /* Dispatch function */ + IM_NUMBER( insert_args ), /* Size of arg list */ + insert_args /* Arg list */ +}; + +/* Call im_insert_noexpand via arg vector. + */ +static int +insert_noexpand_vec( im_object *argv ) +{ + int x = *((int *) argv[3]); + int y = *((int *) argv[4]); + + return( im_insert_noexpand( argv[0], argv[1], argv[2], x, y ) ); +} + +/* Description of im_insert_noexpand. + */ +static im_function insert_noexpand_desc = { + "im_insert_noexpand", /* Name */ + "insert sub-image into main image at position, no expansion", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + insert_noexpand_vec, /* Dispatch function */ + IM_NUMBER( insert_args ), /* Size of arg list */ + insert_args /* Arg list */ +}; + +/* Call im_rot180 via arg vector. + */ +static int +rot180_vec( im_object *argv ) +{ + return( im_rot180( argv[0], argv[1] ) ); +} + +/* Description of im_rot180. + */ +static im_function rot180_desc = { + "im_rot180", /* Name */ + "rotate image 180 degrees", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + rot180_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_rot90 via arg vector. + */ +static int +rot90_vec( im_object *argv ) +{ + return( im_rot90( argv[0], argv[1] ) ); +} + +/* Description of im_rot90. + */ +static im_function rot90_desc = { + "im_rot90", /* Name */ + "rotate image 90 degrees clockwise", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + rot90_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_rot270 via arg vector. + */ +static int +rot270_vec( im_object *argv ) +{ + return( im_rot270( argv[0], argv[1] ) ); +} + +/* Description of im_rot270. + */ +static im_function rot270_desc = { + "im_rot270", /* Name */ + "rotate image 270 degrees clockwise", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + rot270_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_lrjoin via arg vector. + */ +static int +lrjoin_vec( im_object *argv ) +{ + return( im_lrjoin( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_lrjoin. + */ +static im_function lrjoin_desc = { + "im_lrjoin", /* Name */ + "join two images left-right", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + lrjoin_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_tbjoin via arg vector. + */ +static int +tbjoin_vec( im_object *argv ) +{ + return( im_tbjoin( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_tbjoin. + */ +static im_function tbjoin_desc = { + "im_tbjoin", /* Name */ + "join two images top-bottom", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + tbjoin_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args to im_mask2vips. + */ +static im_arg_desc mask2vips_args[] = { + IM_INPUT_DMASK( "input" ), + IM_OUTPUT_IMAGE( "output" ), +}; + +/* Call im_mask2vips via arg vector. + */ +static int +mask2vips_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + + return( im_mask2vips( mo->mask, argv[1] ) ); +} + +/* Description of im_mask2vips. + */ +static im_function mask2vips_desc = { + "im_mask2vips", /* Name */ + "convert DOUBLEMASK to VIPS image", + 0, /* Flags */ + mask2vips_vec, /* Dispatch function */ + IM_NUMBER( mask2vips_args ), /* Size of arg list */ + mask2vips_args /* Arg list */ +}; + +/* Args to im_vips2mask. + */ +static im_arg_desc vips2mask_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_DMASK( "output" ), +}; + +/* Call im_vips2mask via arg vector. + */ +static int +vips2mask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[1]; + + if( !(mo->mask = im_vips2mask( argv[0], mo->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_vips2mask. + */ +static im_function vips2mask_desc = { + "im_vips2mask", /* Name */ + "convert VIPS image to DOUBLEMASK", + 0, /* Flags */ + vips2mask_vec, /* Dispatch function */ + IM_NUMBER( vips2mask_args ), /* Size of arg list */ + vips2mask_args /* Arg list */ +}; + +/* Call im_scale via arg vector. + */ +static int +scale_vec( im_object *argv ) +{ + return( im_scale( argv[0], argv[1] ) ); +} + +/* Description of im_scale. + */ +static im_function scale_desc = { + "im_scale", /* Name */ + "scale image linearly to fit range 0-255", + IM_FN_PIO, /* Flags */ + scale_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_scaleps via arg vector. + */ +static int +scaleps_vec( im_object *argv ) +{ + return( im_scaleps( argv[0], argv[1] ) ); +} + +/* Description of im_scaleps. + */ +static im_function scaleps_desc = { + "im_scaleps", /* Name */ + "logarithmic scale of image to fit range 0-255", + 0, /* Flags */ + scaleps_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args to im_slice. + */ +static im_arg_desc slice_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_DOUBLE( "thresh1" ), + IM_INPUT_DOUBLE( "thresh2" ) +}; + +/* Call im_slice via arg vector. + */ +static int +slice_vec( im_object *argv ) +{ + double t1 = *((double *) argv[2]); + double t2 = *((double *) argv[3]); + + return( im_slice( argv[0], argv[1], t1, t2 ) ); +} + +/* Description of im_slice. + */ +static im_function slice_desc = { + "im_slice", /* Name */ + "slice an image using two thresholds", + 0, /* Flags */ + slice_vec, /* Dispatch function */ + IM_NUMBER( slice_args ), /* Size of arg list */ + slice_args /* Arg list */ +}; + +/* Args to im_thresh. + */ +static im_arg_desc thresh_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_DOUBLE( "threshold" ) +}; + +/* Call im_thresh via arg vector. + */ +static int +thresh_vec( im_object *argv ) +{ + double t1 = *((double *) argv[2]); + + return( im_thresh( argv[0], argv[1], t1 ) ); +} + +/* Description of im_thresh. + */ +static im_function thresh_desc = { + "im_thresh", /* Name */ + "slice an image at a threshold", + 0, /* Flags */ + thresh_vec, /* Dispatch function */ + IM_NUMBER( thresh_args ), /* Size of arg list */ + thresh_args /* Arg list */ +}; + +/* Args to im_grid. + */ +static im_arg_desc grid_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "tile_height" ), + IM_INPUT_INT( "across" ), + IM_INPUT_INT( "down" ) +}; + +/* Call im_grid via arg vector. + */ +static int +grid_vec( im_object *argv ) +{ + int tile_height = *((int *) argv[2]); + int across = *((int *) argv[3]); + int down = *((int *) argv[4]); + + return( im_grid( argv[0], argv[1], tile_height, across, down ) ); +} + +/* Description of im_grid. + */ +static im_function grid_desc = { + "im_grid", /* Name */ + "chop a tall thin image into a grid of images", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + grid_vec, /* Dispatch function */ + IM_NUMBER( grid_args ), /* Size of arg list */ + grid_args /* Arg list */ +}; + +/* Args to im_replicate. + */ +static im_arg_desc replicate_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "across" ), + IM_INPUT_INT( "down" ) +}; + +/* Call im_replicate via arg vector. + */ +static int +replicate_vec( im_object *argv ) +{ + int across = *((int *) argv[2]); + int down = *((int *) argv[3]); + + return( im_replicate( argv[0], argv[1], across, down ) ); +} + +/* Description of im_replicate. + */ +static im_function replicate_desc = { + "im_replicate", /* Name */ + "replicate an image horizontally and vertically", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + replicate_vec, /* Dispatch function */ + IM_NUMBER( replicate_args ), /* Size of arg list */ + replicate_args /* Arg list */ +}; + +/* Args to im_zoom. + */ +static im_arg_desc zoom_args[] = { + IM_INPUT_IMAGE( "input" ), + IM_OUTPUT_IMAGE( "output" ), + IM_INPUT_INT( "xfac" ), + IM_INPUT_INT( "yfac" ) +}; + +/* Call im_zoom via arg vector. + */ +static int +zoom_vec( im_object *argv ) +{ + int xfac = *((int *) argv[2]); + int yfac = *((int *) argv[3]); + + return( im_zoom( argv[0], argv[1], xfac, yfac ) ); +} + +/* Description of im_zoom. + */ +static im_function zoom_desc = { + "im_zoom", /* Name */ + "simple zoom of an image by integer factors", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + zoom_vec, /* Dispatch function */ + IM_NUMBER( zoom_args ), /* Size of arg list */ + zoom_args /* Arg list */ +}; + +static int +jpeg2vips_vec( im_object *argv ) +{ + char *in = argv[0]; + IMAGE *out = argv[1]; + + if( im_jpeg2vips( in, out ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc jpeg2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +static im_function jpeg2vips_desc = { + "im_jpeg2vips", /* Name */ + "convert from jpeg", /* Description */ + 0, /* Flags */ + jpeg2vips_vec, /* Dispatch function */ + IM_NUMBER( jpeg2vips_args ), /* Size of arg list */ + jpeg2vips_args /* Arg list */ +}; + +static int +vips2jpeg_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + char *out = argv[1]; + + if( im_vips2jpeg( in, out ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc vips2jpeg_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_STRING( "out" ) +}; + +static im_function vips2jpeg_desc = { + "im_vips2jpeg", /* Name */ + "convert to jpeg", /* Description */ + 0, /* Flags */ + vips2jpeg_vec, /* Dispatch function */ + IM_NUMBER( vips2jpeg_args ), /* Size of arg list */ + vips2jpeg_args /* Arg list */ +}; + +static int +vips2mimejpeg_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + int qfac = *((int *) argv[1]); + + if( im_vips2mimejpeg( in, qfac ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc vips2mimejpeg_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_INT( "qfac" ) +}; + +static im_function vips2mimejpeg_desc = { + "im_vips2mimejpeg", /* Name */ + "convert to jpeg as mime type on stdout", /* Description */ + 0, /* Flags */ + vips2mimejpeg_vec, /* Dispatch function */ + IM_NUMBER( vips2mimejpeg_args ), /* Size of arg list */ + vips2mimejpeg_args /* Arg list */ +}; + +/* Args for vips2png. + */ +static im_arg_desc vips2png_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_STRING( "out" ) +}; + +/* Call im_vips2png via arg vector. + */ +static int +vips2png_vec( im_object *argv ) +{ + return( im_vips2png( argv[0], argv[1] ) ); +} + +/* Description of im_vips2png. + */ +static im_function vips2png_desc = { + "im_vips2png", /* Name */ + "convert VIPS image to PNG file", /* Description */ + 0, + vips2png_vec, /* Dispatch function */ + IM_NUMBER( vips2png_args ), /* Size of arg list */ + vips2png_args /* Arg list */ +}; + +/* Args for png2vips. + */ +static im_arg_desc png2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_png2vips via arg vector. + */ +static int +png2vips_vec( im_object *argv ) +{ + return( im_png2vips( argv[0], argv[1] ) ); +} + +/* Description of im_png2vips. + */ +static im_function png2vips_desc = { + "im_png2vips", /* Name */ + "convert PNG file to VIPS image", /* Description */ + 0, + png2vips_vec, /* Dispatch function */ + IM_NUMBER( png2vips_args ), /* Size of arg list */ + png2vips_args /* Arg list */ +}; + +/* Args for exr2vips. + */ +static im_arg_desc exr2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_exr2vips via arg vector. + */ +static int +exr2vips_vec( im_object *argv ) +{ + return( im_exr2vips( argv[0], argv[1] ) ); +} + +/* Description of im_exr2vips. + */ +static im_function exr2vips_desc = { + "im_exr2vips", /* Name */ + "convert an OpenEXR file to VIPS", /* Description */ + 0, + exr2vips_vec, /* Dispatch function */ + IM_NUMBER( exr2vips_args ), /* Size of arg list */ + exr2vips_args /* Arg list */ +}; + +/* Args for vips2tiff. + */ +static im_arg_desc vips2tiff_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_STRING( "out" ) +}; + +/* Call im_vips2tiff via arg vector. + */ +static int +vips2tiff_vec( im_object *argv ) +{ + return( im_vips2tiff( argv[0], argv[1] ) ); +} + +/* Description of im_vips2tiff. + */ +static im_function vips2tiff_desc = { + "im_vips2tiff", /* Name */ + "convert VIPS image to TIFF file", /* Description */ + 0, + vips2tiff_vec, /* Dispatch function */ + IM_NUMBER( vips2tiff_args ), /* Size of arg list */ + vips2tiff_args /* Arg list */ +}; + +/* Args for magick2vips. + */ +static im_arg_desc magick2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_magick2vips via arg vector. + */ +static int +magick2vips_vec( im_object *argv ) +{ + return( im_magick2vips( argv[0], argv[1] ) ); +} + +/* Description of im_magick2vips. + */ +static im_function magick2vips_desc = { + "im_magick2vips", /* Name */ + "load file with libMagick", /* Description */ + 0, + magick2vips_vec, /* Dispatch function */ + IM_NUMBER( magick2vips_args ), /* Size of arg list */ + magick2vips_args /* Arg list */ +}; + +/* Args for tiff2vips. + */ +static im_arg_desc tiff2vips_args[] = { + IM_INPUT_STRING( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_tiff2vips via arg vector. + */ +static int +tiff2vips_vec( im_object *argv ) +{ + return( im_tiff2vips( argv[0], argv[1] ) ); +} + +/* Description of im_tiff2vips. + */ +static im_function tiff2vips_desc = { + "im_tiff2vips", /* Name */ + "convert TIFF file to VIPS image", /* Description */ + 0, + tiff2vips_vec, /* Dispatch function */ + IM_NUMBER( tiff2vips_args ), /* Size of arg list */ + tiff2vips_args /* Arg list */ +}; + +static int +analyze2vips_vec( im_object *argv ) +{ + const char *in = argv[0]; + IMAGE *out = argv[1]; + + return( im_analyze2vips( in, out ) ); +} + +static im_arg_desc analyze2vips_arg_types[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_IMAGE( "im" ) +}; + +static im_function analyze2vips_desc = { + "im_analyze2vips", /* Name */ + "read a file in analyze format",/* Description */ + 0, /* Flags */ + analyze2vips_vec, /* Dispatch function */ + IM_NUMBER( analyze2vips_arg_types ),/* Size of arg list */ + analyze2vips_arg_types /* Arg list */ +}; + +static int +csv2vips_vec( im_object *argv ) +{ + const char *in = argv[0]; + IMAGE *out = argv[1]; + + return( im_csv2vips( in, out ) ); +} + +static im_arg_desc csv2vips_arg_types[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_IMAGE( "im" ) +}; + +static im_function csv2vips_desc = { + "im_csv2vips", /* Name */ + "read a file in csv format",/* Description */ + 0, /* Flags */ + csv2vips_vec, /* Dispatch function */ + IM_NUMBER( csv2vips_arg_types ),/* Size of arg list */ + csv2vips_arg_types /* Arg list */ +}; + +static int +vips2csv_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + const char *filename = argv[1]; + + return( im_vips2csv( in, filename ) ); +} + +static im_arg_desc vips2csv_arg_types[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_STRING( "filename" ) +}; + +static im_function vips2csv_desc = { + "im_vips2csv", /* Name */ + "write an image in csv format", /* Description */ + 0, /* Flags */ + vips2csv_vec, /* Dispatch function */ + IM_NUMBER( vips2csv_arg_types ),/* Size of arg list */ + vips2csv_arg_types /* Arg list */ +}; + +static int +ppm2vips_vec( im_object *argv ) +{ + const char *in = argv[0]; + IMAGE *out = argv[1]; + + return( im_ppm2vips( in, out ) ); +} + +static im_arg_desc ppm2vips_arg_types[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_IMAGE( "im" ) +}; + +static im_function ppm2vips_desc = { + "im_ppm2vips", /* Name */ + "read a file in pbm/pgm/ppm format", /* Description */ + 0, /* Flags */ + ppm2vips_vec, /* Dispatch function */ + IM_NUMBER( ppm2vips_arg_types ),/* Size of arg list */ + ppm2vips_arg_types /* Arg list */ +}; + +static int +vips2ppm_vec( im_object *argv ) +{ + IMAGE *im = argv[0]; + const char *filename = argv[1]; + + return( im_vips2ppm( im, filename ) ); +} + +static im_arg_desc vips2ppm_arg_types[] = { + IM_INPUT_IMAGE( "im" ), + IM_INPUT_STRING( "filename" ) +}; + +static im_function vips2ppm_desc = { + "im_vips2ppm", /* Name */ + "write a file in pbm/pgm/ppm format", /* Description */ + 0, /* Flags */ + vips2ppm_vec, /* Dispatch function */ + IM_NUMBER( vips2ppm_arg_types ),/* Size of arg list */ + vips2ppm_arg_types /* Arg list */ +}; + +/* Call im_msb via arg vector. + */ +static int +msb_vec (im_object * argv) +{ + return im_msb (argv[0], argv[1]); +} + +/* Description of im_msb. + */ +static im_function msb_desc = { + "im_msb", /* Name */ + "convert to uchar by discarding bits", + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + msb_vec, /* Dispatch function */ + IM_NUMBER (one_in_one_out), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args to im_msb_band. + */ +static im_arg_desc msb_band_args[] = { + IM_INPUT_IMAGE ("in"), + IM_OUTPUT_IMAGE ("out"), + IM_INPUT_INT ("band") +}; + +/* Call im_msb_band via arg vector. + */ +static int +msb_band_vec (im_object * argv) +{ + IMAGE *in = (IMAGE *) argv[0]; + IMAGE *out = (IMAGE *) argv[1]; + int *band = (int *) argv[2]; + + return im_msb_band (in, out, *band); +} + +/* Description of im_msb_band. + */ +static im_function msb_band_desc = { + "im_msb_band", /* Name */ + "convert to single band uchar by discarding bits", + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + msb_band_vec, /* Dispatch function */ + IM_NUMBER (msb_band_args), /* Size of arg list */ + msb_band_args /* Arg list */ +}; + +/* Args to im_rightshift_size. + */ +static im_arg_desc rightshift_size_args[] = { + IM_INPUT_IMAGE ("in"), + IM_OUTPUT_IMAGE ("out"), + IM_INPUT_INT ("xshift"), + IM_INPUT_INT ("yshift"), + IM_INPUT_INT ("band_fmt") +}; + +/* Call im_rightshift_size via arg vector. + */ +static int +rightshift_size_vec (im_object * argv) +{ + IMAGE *in = (IMAGE *) argv[0]; + IMAGE *out = (IMAGE *) argv[1]; + int *xshift = (int *) argv[2]; + int *yshift = (int *) argv[3]; + int *band_fmt = (int *) argv[4]; + + return im_rightshift_size (in, out, *xshift, *yshift, *band_fmt ); +} + +/* Description of im_rightshift_size. + */ +static im_function rightshift_size_desc = { + "im_rightshift_size", /* Name */ + "decrease size by a power-of-two factor", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + rightshift_size_vec, /* Dispatch function */ + IM_NUMBER (rightshift_size_args), /* Size of arg list */ + rightshift_size_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *conv_list[] = { + &bandjoin_desc, + &bernd_desc, + &black_desc, + &c2amph_desc, + &c2imag_desc, + &c2ps_desc, + &c2real_desc, + &c2rect_desc, + &clip2c_desc, + &clip2cm_desc, + &clip2d_desc, + &clip2dcm_desc, + &clip2f_desc, + &clip2fmt_desc, + &clip2i_desc, + &clip2s_desc, + &clip2ui_desc, + &clip2us_desc, + &clip_desc, + ©_desc, + ©_morph_desc, + ©_swap_desc, + ©_set_desc, + ©_set_meta_desc, + &csv2vips_desc, + &extract_area_desc, + &extract_areabands_desc, + &extract_band_desc, + &extract_bands_desc, + &extract_desc, + &falsecolour_desc, + &fliphor_desc, + &flipver_desc, + &gbandjoin_desc, + &grid_desc, + &insert_desc, + &insert_noexpand_desc, + &jpeg2vips_desc, + &lrjoin_desc, + &magick2vips_desc, + &mask2vips_desc, + &msb_desc, + &msb_band_desc, + &png2vips_desc, + &exr2vips_desc, + &ppm2vips_desc, + &analyze2vips_desc, + &print_desc, + &recomb_desc, + &replicate_desc, + &ri2c_desc, + &rot180_desc, + &rot270_desc, + &rot90_desc, + &scale_desc, + &scaleps_desc, + &rightshift_size_desc, + &slice_desc, + &subsample_desc, + &system_desc, + &tbjoin_desc, + &text_desc, + &thresh_desc, + &tiff2vips_desc, + &vips2csv_desc, + &vips2jpeg_desc, + &vips2mask_desc, + &vips2mimejpeg_desc, + &vips2png_desc, + &vips2ppm_desc, + &vips2tiff_desc, + &zoom_desc +}; + +/* Package of functions. + */ +im_package im__conversion = { + "conversion", + IM_NUMBER( conv_list ), + conv_list +}; diff --git a/libsrc/conversion/dbh.h b/libsrc/conversion/dbh.h new file mode 100644 index 00000000..782cbf67 --- /dev/null +++ b/libsrc/conversion/dbh.h @@ -0,0 +1,98 @@ +/* + * + * (c) Copyright, 1986-1991 + * Biodynamics Research Unit + * Mayo Foundation + * + * dbh.h + * + * + * database sub-definitions + */ + +/* + * + * The previous-generation header for Analyze images. Although (c) Mayo + * Foundation, it has been in the public domain for many years. + * + * Analyze images have a 348 byte header stored in a file with a .hdr suffix, + * and a corresponding .img file containing just the pixel data. + * + */ + +struct header_key /* header_key */ + { /* off + size*/ + int sizeof_hdr; /* 0 + 4 */ + char data_type[10]; /* 4 + 10 */ + char db_name[18]; /* 14 + 18 */ + int extents; /* 32 + 4 */ + short int session_error; /* 36 + 2 */ + char regular; /* 38 + 1 */ + char hkey_un0; /* 39 + 1 */ + }; /* total=40 */ + +struct image_dimension /* image_dimension */ + { /* off + size*/ + short int dim[8]; /* 0 + 16 */ + char vox_units[4]; /* 16 + 4 */ + char cal_units[8]; /* 20 + 4 */ + short int unused1; /* 24 + 2 */ + short int datatype; /* 30 + 2 */ + short int bitpix; /* 32 + 2 */ + short int dim_un0; /* 34 + 2 */ + float pixdim[8]; /* 36 + 32 */ + + /* pixdim[] specifies the voxel dimensions: + pixdim[1] - voxel width + pixdim[2] - voxel height + pixdim[3] - interslice distance + ..etc + */ + float vox_offset; /* 68 + 4 */ + float funused1; /* 72 + 4 */ + float funused2; /* 76 + 4 */ + float funused3; /* 80 + 4 */ + float cal_max; /* 84 + 4 */ + float cal_min; /* 88 + 4 */ + int compressed; /* 92 + 4 */ + int verified; /* 96 + 4 */ + int glmax, glmin; /* 100 + 8 */ + }; + +struct data_history /* data_history */ + { /* off + size*/ + char descrip[80]; /* 0 + 80 */ + char aux_file[24]; /* 80 + 24 */ + char orient; /* 104 + 1 */ + char originator[10]; /* 105 + 10 */ + char generated[10]; /* 115 + 10 */ + char scannum[10]; /* 125 + 10 */ + char patient_id[10]; /* 135 + 10 */ + char exp_date[10]; /* 145 + 10 */ + char exp_time[10]; /* 155 + 10 */ + char hist_un0[3]; /* 165 + 3 */ + int views; /* 168 + 4 */ + int vols_added; /* 172 + 4 */ + int start_field; /* 176 + 4 */ + int field_skip; /* 180 + 4 */ + int omax,omin; /* 184 + 8 */ + int smax,smin; /* 192 + 8 */ + }; /* total=200 */ + +struct dsr /* dsr */ + { /* off + size*/ + struct header_key hk; /* 0 + 40 */ + struct image_dimension dime; /* 40 + 108 */ + struct data_history hist; /* 148 + 200 */ + }; /* total=348 */ + +/* Acceptable values for hdr.dime.datatype */ +#define DT_UNKNOWN 0 +#define DT_BINARY 1 +#define DT_UNSIGNED_CHAR 2 +#define DT_SIGNED_SHORT 4 +#define DT_SIGNED_INT 8 +#define DT_FLOAT 16 +#define DT_COMPLEX 32 +#define DT_DOUBLE 64 +#define DT_RGB 128 diff --git a/libsrc/conversion/im_analyze2vips.c b/libsrc/conversion/im_analyze2vips.c new file mode 100644 index 00000000..d5f9eeb2 --- /dev/null +++ b/libsrc/conversion/im_analyze2vips.c @@ -0,0 +1,583 @@ +/* Read a Analyze file. Old-style header (so called 7.5 format). + * + * 3/8/05 + * - dbh.h header from Ralph Myers + * 22/8/05 + * - better byteswapper + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "dbh.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* The things we can have in header fields. Can't use GType, since we want a + * static value we can use in a declaration. + */ +typedef enum { + BYTE, + SHORT, + INT, + FLOAT, + STRING +} Type; + +/* A field in the dsr header. + */ +typedef struct { + const char *name; /* Eg. "header_key.sizeof_hdr" */ + Type type; + glong offset; /* Offset in struct */ + int len; /* Sizeof ... useful for string types */ +} Field; + +static Field dsr_header[] = { + { "dsr-header_key.sizeof_hdr", INT, + G_STRUCT_OFFSET( struct dsr, hk.sizeof_hdr ), 4 }, + { "dsr-header_key.data_type", STRING, + G_STRUCT_OFFSET( struct dsr, hk.data_type ), 10 }, + { "dsr-header_key.db_name", STRING, + G_STRUCT_OFFSET( struct dsr, hk.db_name ), 18 }, + { "dsr-header_key.extents", INT, + G_STRUCT_OFFSET( struct dsr, hk.extents ), 4 }, + { "dsr-header_key.session_error", SHORT, + G_STRUCT_OFFSET( struct dsr, hk.session_error ), 2 }, + { "dsr-header_key.regular", BYTE, + G_STRUCT_OFFSET( struct dsr, hk.regular ), 1 }, + { "dsr-header_key.hkey_un0", BYTE, + G_STRUCT_OFFSET( struct dsr, hk.hkey_un0 ), 1 }, + + { "dsr-image_dimension.dim[0]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[0] ), 2 }, + { "dsr-image_dimension.dim[1]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[1] ), 2 }, + { "dsr-image_dimension.dim[2]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[2] ), 2 }, + { "dsr-image_dimension.dim[3]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[3] ), 2 }, + { "dsr-image_dimension.dim[4]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[4] ), 2 }, + { "dsr-image_dimension.dim[5]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[5] ), 2 }, + { "dsr-image_dimension.dim[6]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[6] ), 2 }, + { "dsr-image_dimension.dim[7]", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim[7] ), 2 }, + { "dsr-image_dimension.vox_units[0]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.vox_units[0] ), 1 }, + { "dsr-image_dimension.vox_units[1]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.vox_units[1] ), 1 }, + { "dsr-image_dimension.vox_units[2]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.vox_units[2] ), 1 }, + { "dsr-image_dimension.vox_units[3]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.vox_units[3] ), 1 }, + { "dsr-image_dimension.cal_units[0]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[0] ), 1 }, + { "dsr-image_dimension.cal_units[1]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[1] ), 1 }, + { "dsr-image_dimension.cal_units[2]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[2] ), 1 }, + { "dsr-image_dimension.cal_units[3]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[3] ), 1 }, + { "dsr-image_dimension.cal_units[4]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[4] ), 1 }, + { "dsr-image_dimension.cal_units[5]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[5] ), 1 }, + { "dsr-image_dimension.cal_units[6]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[6] ), 1 }, + { "dsr-image_dimension.cal_units[7]", BYTE, + G_STRUCT_OFFSET( struct dsr, dime.cal_units[7] ), 1 }, + { "dsr-image_dimension.data_type", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.datatype ), 2 }, + { "dsr-image_dimension.bitpix", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.bitpix ), 2 }, + { "dsr-image_dimension.dim_un0", SHORT, + G_STRUCT_OFFSET( struct dsr, dime.dim_un0 ), 2 }, + { "dsr-image_dimension.pixdim[0]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[0] ), 4 }, + { "dsr-image_dimension.pixdim[1]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[1] ), 4 }, + { "dsr-image_dimension.pixdim[2]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[2] ), 4 }, + { "dsr-image_dimension.pixdim[3]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[3] ), 4 }, + { "dsr-image_dimension.pixdim[4]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[4] ), 4 }, + { "dsr-image_dimension.pixdim[5]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[5] ), 4 }, + { "dsr-image_dimension.pixdim[6]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[6] ), 4 }, + { "dsr-image_dimension.pixdim[7]", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.pixdim[7] ), 4 }, + { "dsr-image_dimension.vox_offset", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.vox_offset ), 4 }, + { "dsr-image_dimension.cal_max", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.cal_max ), 4 }, + { "dsr-image_dimension.cal_min", FLOAT, + G_STRUCT_OFFSET( struct dsr, dime.cal_min ), 4 }, + { "dsr-image_dimension.compressed", INT, + G_STRUCT_OFFSET( struct dsr, dime.compressed ), 4 }, + { "dsr-image_dimension.verified", INT, + G_STRUCT_OFFSET( struct dsr, dime.verified ), 4 }, + { "dsr-image_dimension.glmax", INT, + G_STRUCT_OFFSET( struct dsr, dime.glmax ), 4 }, + { "dsr-image_dimension.glmin", INT, + G_STRUCT_OFFSET( struct dsr, dime.glmin ), 4 }, + + { "dsr-data_history.descrip", STRING, + G_STRUCT_OFFSET( struct dsr, hist.descrip ), 80 }, + { "dsr-data_history.aux_file", STRING, + G_STRUCT_OFFSET( struct dsr, hist.aux_file ), 24 }, + { "dsr-data_history.orient", BYTE, + G_STRUCT_OFFSET( struct dsr, hist.orient ), 1 }, + { "dsr-data_history.originator", STRING, + G_STRUCT_OFFSET( struct dsr, hist.originator ), 10 }, + { "dsr-data_history.generated", STRING, + G_STRUCT_OFFSET( struct dsr, hist.generated ), 10 }, + { "dsr-data_history.scannum", STRING, + G_STRUCT_OFFSET( struct dsr, hist.scannum ), 10 }, + { "dsr-data_history.patient_id", STRING, + G_STRUCT_OFFSET( struct dsr, hist.patient_id ), 10 }, + { "dsr-data_history.exp_date", STRING, + G_STRUCT_OFFSET( struct dsr, hist.exp_date ), 10 }, + { "dsr-data_history.exp_time", STRING, + G_STRUCT_OFFSET( struct dsr, hist.exp_time ), 10 }, + { "dsr-data_history.hist_un0", STRING, + G_STRUCT_OFFSET( struct dsr, hist.hist_un0 ), 3 }, + { "dsr-data_history.views", INT, + G_STRUCT_OFFSET( struct dsr, hist.views ), 4 }, + { "dsr-data_history.vols_added", INT, + G_STRUCT_OFFSET( struct dsr, hist.vols_added ), 4 }, + { "dsr-data_history.start_field", INT, + G_STRUCT_OFFSET( struct dsr, hist.start_field ), 4 }, + { "dsr-data_history.field_skip", INT, + G_STRUCT_OFFSET( struct dsr, hist.field_skip ), 4 }, + { "dsr-data_history.omax", INT, + G_STRUCT_OFFSET( struct dsr, hist.omax ), 4 }, + { "dsr-data_history.omin", INT, + G_STRUCT_OFFSET( struct dsr, hist.omin ), 4 }, + { "dsr-data_history.smax", INT, + G_STRUCT_OFFSET( struct dsr, hist.smax ), 4 }, + { "dsr-data_history.smin", INT, + G_STRUCT_OFFSET( struct dsr, hist.smin ), 4 } +}; + +/* Given a filename, generate the names for the header and the image data. + * + * Eg. + * "fred" -> "fred.hdr", "fred.img" + * "fred.img" -> "fred.hdr", "fred.img" + */ +static void +generate_filenames( const char *path, char *header, char *image ) +{ + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + + const char *olds[] = { ".img", ".hdr" }; + + /* Take off any modifiers. + */ + im_filename_split( path, name, mode ); + + im__change_suffix( name, header, FILENAME_MAX, ".hdr", olds, 2 ); + im__change_suffix( name, image, FILENAME_MAX, ".img", olds, 2 ); +} + +/* str is a str which may not be NULL-terminated. Return a pointer to a static + * buffer with a NULL-terminated version so we can safely printf() the string. + * Also, make sure the string is plain ascii. + */ +static char * +getstr( int mx, const char *str ) +{ + static char buf[256]; + int i; + + assert( mx < 256 ); + + strncpy( buf, str, mx ); + buf[mx]= '\0'; + + /* How annoying, patient_id has some funny ctrlchars in that mess up + * xml encode later. + */ + for( i = 0; i < mx && buf[i]; i++ ) + if( !isascii( buf[i] ) || buf[i] < 32 ) + buf[i] = '@'; + + return( buf ); +} + +#ifdef DEBUG +static void +print_dsr( struct dsr *d ) +{ + int i; + + for( i = 0; i < IM_NUMBER( dsr_header ); i++ ) { + printf( "%s = ", dsr_header[i].name ); + + switch( dsr_header[i].type ) { + case BYTE: + printf( "%d\n", G_STRUCT_MEMBER( char, d, + dsr_header[i].offset ) ); + break; + + case SHORT: + printf( "%d\n", G_STRUCT_MEMBER( short, d, + dsr_header[i].offset ) ); + break; + + case INT: + printf( "%d\n", G_STRUCT_MEMBER( int, d, + dsr_header[i].offset ) ); + break; + + case FLOAT: + printf( "%g\n", G_STRUCT_MEMBER( float, d, + dsr_header[i].offset ) ); + break; + + case STRING: + printf( "\"%s\"\n", getstr( dsr_header[i].len, + &G_STRUCT_MEMBER( char, d, + dsr_header[i].offset ) ) ); + break; + + default: + assert( 0 ); + } + } +} +#endif /*DEBUG*/ + +static struct dsr * +read_header( const char *header ) +{ + struct dsr *d; + unsigned int len; + + if( !(d = (struct dsr *) im__file_read_name( header, &len )) ) + return( NULL ); + if( len != sizeof( struct dsr ) ) { + im_error( "im_analyze2vips", + _( "header file size incorrect" ) ); + im_free( d ); + return( NULL ); + } + + /* Ouch! Should check at configure time I guess. + */ + assert( sizeof( struct dsr ) == 348 ); + + /* dsr headers are always SPARC byte order (MSB first). Do we need to + * swap? + */ + if( !im_amiMSBfirst() ) { + int i; + + for( i = 0; i < IM_NUMBER( dsr_header ); i++ ) { + unsigned char *p; + + + switch( dsr_header[i].type ) { + case SHORT: + p = &G_STRUCT_MEMBER( unsigned char, d, + dsr_header[i].offset ); + im__read_2byte( 1, p, &p ); + break; + + case INT: + case FLOAT: + p = &G_STRUCT_MEMBER( unsigned char, d, + dsr_header[i].offset ); + im__read_4byte( 1, p, &p ); + break; + + case BYTE: + case STRING: + break; + + default: + assert( 0 ); + } + } + } + + if( len != d->hk.sizeof_hdr ) { + im_error( "im_analyze2vips", + _( "header file size incorrect" ) ); + im_free( d ); + return( NULL ); + } + + return( d ); +} + +/* Try to get VIPS header properties from a dsr. + */ +static int +get_vips_properties( struct dsr *d, + int *width, int *height, int *bands, int *fmt ) +{ + int i; + + if( d->dime.dim[0] < 2 || d->dime.dim[0] > 7 ) { + im_error( "im_analyze2vips", + _( "%d-dimensional images not supported" ), + d->dime.dim[0] ); + return( -1 ); + } + + /* Size of base 2d images. + */ + *width = d->dime.dim[1]; + *height = d->dime.dim[2]; + + for( i = 3; i <= d->dime.dim[0]; i++ ) + *height *= d->dime.dim[i]; + + /* Check it's a datatype we can handle. + */ + switch( d->dime.datatype ) { + case DT_UNSIGNED_CHAR: + *bands = 1; + *fmt = IM_BANDFMT_UCHAR; + break; + + case DT_SIGNED_SHORT: + *bands = 1; + *fmt = IM_BANDFMT_SHORT; + break; + + case DT_SIGNED_INT: + *bands = 1; + *fmt = IM_BANDFMT_INT; + break; + + case DT_FLOAT: + *bands = 1; + *fmt = IM_BANDFMT_FLOAT; + break; + + case DT_COMPLEX: + *bands = 1; + *fmt = IM_BANDFMT_COMPLEX; + break; + + case DT_DOUBLE: + *bands = 1; + *fmt = IM_BANDFMT_DOUBLE; + break; + + case DT_RGB: + *bands = 3; + *fmt = IM_BANDFMT_UCHAR; + break; + + default: + im_error( "im_analyze2vips", + _( "datatype %d not supported" ), d->dime.datatype ); + return( -1 ); + } + +#ifdef DEBUG + printf( "get_vips_properties: width = %d\n", *width ); + printf( "get_vips_properties: height = %d\n", *height ); + printf( "get_vips_properties: bands = %d\n", *bands ); + printf( "get_vips_properties: fmt = %d\n", *fmt ); +#endif /*DEBUG*/ + + return( 0 ); +} + +static void +attach_meta( IMAGE *out, struct dsr *d ) +{ + int i; + + im_meta_set_blob( out, "dsr", + (im_callback_fn) im_free, d, d->hk.sizeof_hdr ); + + for( i = 0; i < IM_NUMBER( dsr_header ); i++ ) { + switch( dsr_header[i].type ) { + case BYTE: + im_meta_set_int( out, dsr_header[i].name, + G_STRUCT_MEMBER( char, d, + dsr_header[i].offset ) ); + break; + + case SHORT: + im_meta_set_int( out, dsr_header[i].name, + G_STRUCT_MEMBER( short, d, + dsr_header[i].offset ) ); + break; + + case INT: + im_meta_set_int( out, dsr_header[i].name, + G_STRUCT_MEMBER( int, d, + dsr_header[i].offset ) ); + break; + + case FLOAT: + im_meta_set_double( out, dsr_header[i].name, + G_STRUCT_MEMBER( float, d, + dsr_header[i].offset ) ); + break; + + case STRING: + im_meta_set_string( out, dsr_header[i].name, + getstr( dsr_header[i].len, + &G_STRUCT_MEMBER( char, d, + dsr_header[i].offset ) ) ); + break; + + default: + assert( 0 ); + } + } +} + +int +im_isanalyze( const char *filename ) +{ + char header[FILENAME_MAX]; + char image[FILENAME_MAX]; + struct dsr *d; + int width, height; + int bands; + int fmt; + + generate_filenames( filename, header, image ); + if( !(d = read_header( header )) ) + return( 0 ); + +#ifdef DEBUG + print_dsr( d ); +#endif /*DEBUG*/ + + if( get_vips_properties( d, &width, &height, &bands, &fmt ) ) { + im_free( d ); + return( 0 ); + } + im_free( d ); + + return( 1 ); +} + +int +im_analyze2vips_header( const char *filename, IMAGE *out ) +{ + char header[FILENAME_MAX]; + char image[FILENAME_MAX]; + struct dsr *d; + int width, height; + int bands; + int fmt; + + generate_filenames( filename, header, image ); + if( !(d = read_header( header )) ) + return( -1 ); + +#ifdef DEBUG + print_dsr( d ); +#endif /*DEBUG*/ + + if( get_vips_properties( d, &width, &height, &bands, &fmt ) ) { + im_free( d ); + return( -1 ); + } + + im_initdesc( out, width, height, bands, im_bits_of_fmt( fmt ), fmt, + IM_CODING_NONE, + bands == 1 ? IM_TYPE_B_W : IM_TYPE_sRGB, + 1.0, 1.0, + 0, 0 ); + + attach_meta( out, d ); + + return( 0 ); +} + +int +im_analyze2vips( const char *filename, IMAGE *out ) +{ + char header[FILENAME_MAX]; + char image[FILENAME_MAX]; + struct dsr *d; + IMAGE *t[2]; + int width, height; + int bands; + int fmt; + im_arch_type arch = im_amiMSBfirst() ? + IM_ARCH_NATIVE : IM_ARCH_BYTE_SWAPPED; + + generate_filenames( filename, header, image ); + if( !(d = read_header( header )) ) + return( -1 ); + +#ifdef DEBUG + print_dsr( d ); +#endif /*DEBUG*/ + + if( get_vips_properties( d, &width, &height, &bands, &fmt ) || + im_open_local_array( out, t, 2, "im_analyze2vips", "p" ) || + im_raw2vips( image, t[0], width, height, + bands * im_bits_of_fmt( fmt ) / 8, 0 ) || + im_copy_morph( t[0], t[1], bands, fmt, IM_CODING_NONE ) || + im_copy_from( t[1], out, arch ) ) { + im_free( d ); + return( -1 ); + } + + attach_meta( out, d ); + + return( 0 ); +} + diff --git a/libsrc/conversion/im_bandjoin.c b/libsrc/conversion/im_bandjoin.c new file mode 100644 index 00000000..98da268e --- /dev/null +++ b/libsrc/conversion/im_bandjoin.c @@ -0,0 +1,161 @@ +/* @(#) Function to perform a band-wise join of two images. If the two images + * @(#) have n and m bands respectively, then the output image will have n+m + * @(#) bands, with the first n coming from the first image and the last m + * @(#) from the second. Works for any image type. + * @(#) + * @(#) Function im_bandjoin() assumes that the imin image + * @(#) is either memory mapped or in the buffer pimin->data. + * @(#) + * @(#) int im_bandjoin(imin1, imin2, imout) + * @(#) IMAGE *imin1, *imin2, *imout; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 12/02/1990 + * Modified on : 07/03/1991, by N. Dessipris, history removed + * 27/10/93 JC + * - adapted for partials + * - Nicos formatting removed + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Bandjoin generate function. + */ +static int +bandjoin_gen( REGION *or, REGION **ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int x, y, z; + int i1s = IM_IMAGE_SIZEOF_PEL( ir[0]->im ); /* sizeof LHS, RHS pels */ + int i2s = IM_IMAGE_SIZEOF_PEL( ir[1]->im ); + + /* Ask for input we need. + */ + if( im_prepare( ir[0], r ) ) + return( -1 ); + if( im_prepare( ir[1], r ) ) + return( -1 ); + + /* Perform join. + */ + for( y = to; y < bo; y++ ) { + PEL *i1 = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + PEL *i2 = (PEL *) IM_REGION_ADDR( ir[1], le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + /* Copy bytes from first file. + */ + for( z = 0; z < i1s; z++ ) + *q++ = *i1++; + + /* Copy bytes from in2. + */ + for( z = 0; z < i2s; z++ ) + *q++ = *i2++; + } + } + + return( 0 ); +} + +/* Join two images. out->Bands = in1->Bands + in2->Bands. in1 goes first in + * the list. + */ +int +im_bandjoin( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE **in; + + /* Check our args. + */ + if( im_piocheck( in1, out ) ) + return( -1 ); + if( im_piocheck( in2, out ) ) + return( -1 ); + if( in1->Xsize != in2->Xsize || + in1->Ysize != in2->Ysize ) { + im_errormsg( "im_bandjoin: images not same size" ); + return( -1 ); + } + if( in1->BandFmt != in2->BandFmt ) { + im_errormsg( "im_bandjoin: images not same type" ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_bandjoin: input coded" ); + return( -1 ); + } + + /* Set up the output header. + */ + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + out->Bands = in1->Bands + in2->Bands; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in1, in2, NULL ) ) + return( -1 ); + + /* Make input array. + */ + if( !(in = im_allocate_input_array( out, in1, in2, NULL )) ) + return( -1 ); + + /* Make output image. + */ + if( im_generate( out, + im_start_many, bandjoin_gen, im_stop_many, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_bernd.c b/libsrc/conversion/im_bernd.c new file mode 100644 index 00000000..c220e303 --- /dev/null +++ b/libsrc/conversion/im_bernd.c @@ -0,0 +1,94 @@ +/* @(#) Extract a tile from a pyramid as a jpeg + * @(#) + * @(#) int + * @(#) im_bernd( const char *tiffname, + * @(#) int x, int y, int w, int h ) + * @(#) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 7/5/99 JC + * - from im_tiff2vips and im_vips2jpeg, plus some stuff from Steve + * 11/7/01 JC + * - page number now in filename + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +extract( IMAGE *in, int x, int y, int w, int h ) +{ + int len; + char *buf; + IMAGE *t1 = im_open_local( in, "im_bernd:2", "p" ); + + if( im_extract_area( in, t1, x, y, w, h ) || + im_vips2bufjpeg( t1, in, 75, &buf, &len ) ) + return( -1 ); + + fwrite( buf, sizeof( char ), len, stdout ); + fflush( stdout ); + + if( ferror( stdout ) ) { + im_errormsg( "im_bernd: error writing output" ); + return( -1 ); + } + + return( 0 ); +} + +int +im_bernd( const char *tiffname, int x, int y, int w, int h ) +{ + IMAGE *in; + + if( !(in = im_open( "im_bernd:1", "p" )) ) + return( -1 ); + if( im_tiff2vips( tiffname, in ) || + extract( in, x, y, w, h ) ) { + im_close( in ); + return( -1 ); + } + im_close( in ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_black.c b/libsrc/conversion/im_black.c new file mode 100644 index 00000000..e9578991 --- /dev/null +++ b/libsrc/conversion/im_black.c @@ -0,0 +1,123 @@ +/* @(#) Make a black uchar image of a specified size. Sometimes useful for + * @(#) building masks. + * @(#) IMAGE out should nhave been set by the calling program + * @(#) + * @(#) int + * @(#) im_black(out, x, y, bands) + * @(#) IMAGE *out; + * @(#) int x, y; + * @(#) int bands; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/08/1990 + * Modified on : 16/04/1991 by N. Dessipris to work on a line by line basis + * 15/8/94 JC + * - adapted for partials + * - ANSIfied + * - memory leaks fixed! + * 2/3/98 JC + * - IM_ANY added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Generate function --- just black out the region. + */ +static int +black_gen( REGION *or ) +{ + int y; + int sz = IM_REGION_SIZEOF_LINE( or ); + int ls = IM_REGION_LSKIP( or ); + char *q = IM_REGION_ADDR( or, or->valid.left, or->valid.top ); + + for( y = 0; y < or->valid.height; y++, q += ls ) + memset( q, 0, sz ); + + return( 0 ); +} + +/* Make a one band black uchar image of a specified size. + */ +int +im_black( IMAGE *out, int x, int y, int bands ) +{ + int type; + + /* Check parameters. + */ + if( x < 0 || y < 0 || bands < 0 ) { + im_errormsg( "im_black: bad parameter" ); + return( -1 ); + } + + /* Check descriptor. + */ + if( im_poutcheck( out ) ) + return( -1 ); + + /* Set fields. + */ + if( bands == 1 ) + type = IM_TYPE_B_W; + else + type = IM_TYPE_MULTIBAND; + im_initdesc( out, + x, y, bands, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, type, + 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( im_generate( out, NULL, black_gen, NULL, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_c2amph.c b/libsrc/conversion/im_c2amph.c new file mode 100644 index 00000000..112ce044 --- /dev/null +++ b/libsrc/conversion/im_c2amph.c @@ -0,0 +1,142 @@ +/* @(#) Functions which transforms the real and the imaginary parts of + * @(#) a complex image into amplitude and phase. + * @(#) Input image is either memory mapped or in a buffer. + * @(#) Used to display an inverse complex Fourier transform + * @(#) + * @(#) int im_c2amph(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 09/05/1990 + * 15/6/93 JC + * - stupid stupid includes and externs fixed + * - I have been editing for 1 1/2 hours and I'm still drowning in + * rubbish extetrnshh + * 13/12/94 JC + * - modernised + * 9/7/02 JC + * - degree output, for consistency + * - slightly better behaviour in edge cases + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + int x;\ + \ + for( x = 0; x < n; x++ ) {\ + double re = p[0];\ + double im = p[1];\ + double am, ph;\ + \ + am = sqrt( re * re + im * im );\ + \ + if( re == 0 ) { \ + if( im < 0.0 ) \ + ph = 270; \ + else if( im == 0.0 ) \ + ph = 0; \ + else \ + ph = 90; \ + } \ + else { \ + double t = atan( im / re ); \ + \ + if( re > 0.0 ) \ + if( im < 0.0 ) \ + ph = IM_DEG( t + IM_PI * 2.0 ); \ + else \ + ph = IM_DEG( t ); \ + else \ + ph = IM_DEG( t + IM_PI ); \ + } \ + \ + q[0] = am; \ + q[1] = ph; \ + \ + p += 2; \ + q += 2; \ + }\ +} + +/* c2amph buffer processor. + */ +static void +buffer_c2amph( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_DPCOMPLEX: loop(double); break; + case IM_BANDFMT_COMPLEX: loop(float); break; + default: + error_exit( "buffer_c2amph: internal error" ); + } +} + +int +im_c2amph( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE || !im_iscomplex( in ) ) { + im_errormsg( "im_c2amph: input should be uncoded complex" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) buffer_c2amph, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_c2imag.c b/libsrc/conversion/im_c2imag.c new file mode 100644 index 00000000..504d1853 --- /dev/null +++ b/libsrc/conversion/im_c2imag.c @@ -0,0 +1,117 @@ +/* @(#) Extract the imaginary part of a complex image. Output is float or + * @(#) double. + * @(#) + * @(#) int im_c2imag( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 09/05/1990 + * 21/12/94 JC + * - im_c2amph() adapted to make im_c2real() and im_c2imag() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in + 1;\ + TYPE *q = (TYPE *) out;\ + int x;\ + \ + for( x = 0; x < n; x++ ) {\ + double re = *p;\ + \ + p += 2;\ + *q++ = re;\ + }\ +} + +/* c2imag buffer processor. + */ +static void +buffer_c2imag( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_DPCOMPLEX: loop(double); break; + case IM_BANDFMT_COMPLEX: loop(float); break; + default: + error_exit( "buffer_c2imag: internal error" ); + } +} + +int +im_c2imag( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE || !im_iscomplex( in ) ) { + im_errormsg( "im_c2imag: input should be uncoded complex" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Output will be float or double. + */ + if( in->BandFmt == IM_BANDFMT_DPCOMPLEX ) { + out->BandFmt = IM_BANDFMT_DOUBLE; + out->Bbits = IM_BBITS_DOUBLE; + } + else { + out->BandFmt = IM_BANDFMT_FLOAT; + out->Bbits = IM_BBITS_FLOAT; + } + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) buffer_c2imag, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_c2ps.c b/libsrc/conversion/im_c2ps.c new file mode 100644 index 00000000..75303191 --- /dev/null +++ b/libsrc/conversion/im_c2ps.c @@ -0,0 +1,64 @@ +/* @(#) Find the power spectrum of a complex image. + * @(#) + * @(#) int im_c2ps( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 09/05/1990 + * 21/12/94 JC + * - im_c2imag() adapted to make new im_c2ps() + * 8/5/95 JC + * - now just calls im_abs() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_c2ps( IMAGE *in, IMAGE *out ) +{ + return( im_abs( in, out ) ); +} diff --git a/libsrc/conversion/im_c2real.c b/libsrc/conversion/im_c2real.c new file mode 100644 index 00000000..0a14a30b --- /dev/null +++ b/libsrc/conversion/im_c2real.c @@ -0,0 +1,122 @@ +/* @(#) Extract the real part of a complex image. Output is float or double. + * @(#) + * @(#) int im_c2real( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 09/05/1990 + * 15/6/93 JC + * - stupid stupid includes and externs fixed + * - I have been editing for 1 1/2 hours and I'm still drowning in + * rubbish extetrnshh + * 13/12/94 JC + * - modernised + * 21/12/94 JC + * - im_c2amph() adapted to make im_c2real() and im_c2imag() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + int x;\ + \ + for( x = 0; x < n; x++ ) {\ + double re = *p;\ + \ + p += 2;\ + *q++ = re;\ + }\ +} + +/* c2real buffer processor. + */ +static void +buffer_c2real( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_DPCOMPLEX: loop(double); break; + case IM_BANDFMT_COMPLEX: loop(float); break; + default: + error_exit( "buffer_c2real: internal error" ); + } +} + +int +im_c2real( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE || !im_iscomplex( in ) ) { + im_errormsg( "im_c2real: input should be uncoded complex" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Output will be float or double. + */ + if( in->BandFmt == IM_BANDFMT_DPCOMPLEX ) { + out->BandFmt = IM_BANDFMT_DOUBLE; + out->Bbits = IM_BBITS_DOUBLE; + } + else { + out->BandFmt = IM_BANDFMT_FLOAT; + out->Bbits = IM_BBITS_FLOAT; + } + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) buffer_c2real, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_c2rect.c b/libsrc/conversion/im_c2rect.c new file mode 100644 index 00000000..a4c1a13f --- /dev/null +++ b/libsrc/conversion/im_c2rect.c @@ -0,0 +1,108 @@ +/* @(#) Turn (amplitude, phase) image to (x, y) rectangular coordinates. + * @(#) + * @(#) int im_c2rect(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * 9/7/02 JC + * - from im_c2amph() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop(TYPE) \ +{\ + TYPE *p = (TYPE *) in;\ + TYPE *q = (TYPE *) out;\ + int x;\ + \ + for( x = 0; x < n; x++ ) {\ + double am = p[0];\ + double ph = p[1];\ + double re, im;\ + \ + re = am * cos( IM_RAD( ph ) ); \ + im = am * sin( IM_RAD( ph ) ); \ + \ + q[0] = re; \ + q[1] = im; \ + \ + p += 2; \ + q += 2; \ + }\ +} + +/* c2rect buffer processor. + */ +static void +buffer_c2rect( void *in, void *out, int w, IMAGE *im ) +{ + int n = w * im->Bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_DPCOMPLEX: loop(double); break; + case IM_BANDFMT_COMPLEX: loop(float); break; + default: + error_exit( "buffer_c2rect: internal error" ); + } +} + +int +im_c2rect( IMAGE *in, IMAGE *out ) +{ + if( in->Coding != IM_CODING_NONE || !im_iscomplex( in ) ) { + im_errormsg( "im_c2rect: input should be uncoded complex" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Do the processing. + */ + if( im_wrapone( in, out, + (im_wrapone_fn) buffer_c2rect, in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_clip.c b/libsrc/conversion/im_clip.c new file mode 100644 index 00000000..d457afad --- /dev/null +++ b/libsrc/conversion/im_clip.c @@ -0,0 +1,484 @@ +/* @(#) Clip any down to 0-255. Call im_copy if the image is already uchar. + * @(#) + * @(#) int + * @(#) im_clip( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Author: Nicos Dessipris + * Written on: 07/03/1991 + * Modified on: + * 04/05/1992 JC + * - works for char, uchar too + * - floating point code removed from integer clip operations + * - uses nint() instead of own rounding code + * - calculated the number of >255 clips for float/double input + * incorrectly + * - rejects complex input correctly now + * 27/4/93 JC + * - adapted to work with partial images + * - nint() removed, now just +0.5 + * - im_warning code removed + * 30/6/93 JC + * - adapted for partial v2 + * 31/8/93 JC + * - now detects and prints over/underflows + * 27/10/93 JC + * - unsigned integer clips now faster! + * - falls back to im_copy() correctly + * 5/5/94 JC + * - switched to rint() + * 18/8/94 JC + * - now uses evalend callback + * 9/5/95 JC + * - now does complex too + * 11/7/95 JC + * - now uses IM_RINT() macro + * 10/3/01 JC + * - slightly faster and simpler + * - generalised to im_clip2fmt(), all other clippers now just call + * this + * 21/4/04 JC + * - now does floor(), not rint() ... you'll need to round yourself + * before calling this if you want round-to-nearest + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Global state. Track over/under-flows for all sequences in this. + */ +typedef struct { + IMAGE *in; /* Parameters */ + IMAGE *out; + int ofmt; + + int underflow; /* Number of underflows */ + int overflow; /* Number of overflows */ +} Clip; + +static int +clip_destroy( Clip *clip ) +{ + /* Print warnings, if necessary. + */ + if( clip->overflow || clip->underflow ) + im_warn( "im_clip", + _( "%d underflows and %d overflows detected" ), + clip->underflow, clip->overflow ); + + return( 0 ); +} + +/* Build a Clip. + */ +static Clip * +clip_new( IMAGE *in, IMAGE *out, int ofmt ) +{ + Clip *clip = IM_NEW( out, Clip ); + + if( !clip ) + return( NULL ); + + clip->in = in; + clip->out = out; + clip->ofmt = ofmt; + clip->underflow = 0; + clip->overflow = 0; + + if( im_add_close_callback( out, + (im_callback_fn) clip_destroy, clip, NULL ) ) + return( NULL ); + + return( clip ); +} + +/* Our sequence value: the region this sequence is using, and two local stats. + */ +typedef struct { + REGION *ir; /* Input region */ + int underflow; /* Number of underflows */ + int overflow; /* Number of overflows */ +} ClipSequence; + +/* Destroy a sequence value. + */ +static int +stop_clip( ClipSequence *seq, IMAGE *in, Clip *clip ) +{ + /* Add to global stats. + */ + clip->underflow += seq->underflow; + clip->overflow += seq->overflow; + + /* Junk our region too. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Make a sequence value. + */ +static void * +start_clip( IMAGE *out, IMAGE *in, Clip *clip ) +{ + ClipSequence *seq = IM_NEW( out, ClipSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->overflow = 0; + seq->underflow = 0; + + if( !(seq->ir = im_region_create( in )) ) + return( NULL ); + + return( (void *) seq ); +} + +/* Clip int types to an int type. + */ +#define IM_CLIP_INT_INT( ITYPE, OTYPE, IM_CLIP ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + int t = p[x];\ + \ + IM_CLIP( t, seq );\ + \ + q[x] = t;\ + }\ +} + +/* Clip float types to an int type. + */ +#define IM_CLIP_FLOAT_INT( ITYPE, OTYPE, IM_CLIP ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + ITYPE v = floor( p[x] );\ + \ + IM_CLIP( v, seq ); \ + \ + q[x] = v;\ + }\ +} + +/* Clip complex types to an int type. Just take the real part. + */ +#define IM_CLIP_COMPLEX_INT( ITYPE, OTYPE, IM_CLIP ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + ITYPE v = floor( p[0] );\ + p += 2;\ + \ + IM_CLIP( v, seq ); \ + \ + q[x] = v;\ + }\ +} + +/* Clip non-complex types to a float type. + */ +#define IM_CLIP_REAL_FLOAT( ITYPE, OTYPE ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ )\ + q[x] = p[x];\ +} + +/* Clip complex types to a float type ... just take real. + */ +#define IM_CLIP_COMPLEX_FLOAT( ITYPE, OTYPE ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + q[x] = p[0];\ + p += 2;\ + }\ +} + +/* Clip any to a complex type ... set imaginary to zero. + */ +#define IM_CLIP_ANY_COMPLEX( ITYPE, OTYPE ) { \ + ITYPE *p = (ITYPE *) IM_REGION_ADDR( ir, le, y );\ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y );\ + \ + for( x = 0; x < sz; x++ ) {\ + q[0] = p[x];\ + q[1] = 0.0;\ + q += 2;\ + }\ +} + +#define BAND_SWITCH_INNER( ITYPE, INT, FLOAT, COMPLEX ) { \ + switch( clip->out->BandFmt ) { \ + case IM_BANDFMT_UCHAR: \ + INT( ITYPE, unsigned char, IM_CLIP_UCHAR ); \ + break; \ + case IM_BANDFMT_CHAR: \ + INT( ITYPE, signed char, IM_CLIP_CHAR ); \ + break; \ + case IM_BANDFMT_USHORT: \ + INT( ITYPE, unsigned short, IM_CLIP_USHORT ); \ + break; \ + case IM_BANDFMT_SHORT: \ + INT( ITYPE, signed short, IM_CLIP_SHORT ); \ + break; \ + case IM_BANDFMT_UINT: \ + INT( ITYPE, unsigned int, IM_CLIP_NONE ); \ + break; \ + case IM_BANDFMT_INT: \ + INT( ITYPE, signed int, IM_CLIP_NONE ); \ + break; \ + case IM_BANDFMT_FLOAT: \ + FLOAT( ITYPE, float ); \ + break; \ + case IM_BANDFMT_DOUBLE: \ + FLOAT( ITYPE, double ); \ + break; \ + case IM_BANDFMT_COMPLEX: \ + COMPLEX( ITYPE, float ); \ + break; \ + case IM_BANDFMT_DPCOMPLEX: \ + COMPLEX( ITYPE, double ); \ + break; \ + default: \ + assert( 0 ); \ + } \ +} + +/* Clip a small area. + */ +static int +clip_gen( REGION *or, ClipSequence *seq, IMAGE *in, Clip *clip ) +{ + REGION *ir = seq->ir; + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + int x, y; + + if( im_prepare( ir, r ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + switch( clip->in->BandFmt ) { + case IM_BANDFMT_UCHAR: + BAND_SWITCH_INNER( unsigned char, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_CHAR: + BAND_SWITCH_INNER( signed char, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_USHORT: + BAND_SWITCH_INNER( unsigned short, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_SHORT: + BAND_SWITCH_INNER( signed short, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_UINT: + BAND_SWITCH_INNER( unsigned int, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_INT: + BAND_SWITCH_INNER( signed int, + IM_CLIP_INT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_FLOAT: + BAND_SWITCH_INNER( float, + IM_CLIP_FLOAT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_DOUBLE: + BAND_SWITCH_INNER( double, + IM_CLIP_FLOAT_INT, IM_CLIP_REAL_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_COMPLEX: + BAND_SWITCH_INNER( float, + IM_CLIP_COMPLEX_INT, IM_CLIP_COMPLEX_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + case IM_BANDFMT_DPCOMPLEX: + BAND_SWITCH_INNER( double, + IM_CLIP_COMPLEX_INT, IM_CLIP_COMPLEX_FLOAT, + IM_CLIP_ANY_COMPLEX ); + break; + default: + assert( 0 ); + } + } + + return( 0 ); +} + +/* Clip to any format. + */ +int +im_clip2fmt( IMAGE *in, IMAGE *out, int ofmt ) +{ + Clip *clip; + + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_clip2fmt", _( "in must be uncoded" ) ); + return( -1 ); + } + if( ofmt < 0 || ofmt > IM_BANDFMT_DPCOMPLEX ) { + im_error( "im_clip2fmt", _( "ofmt out of range" ) ); + return( -1 ); + } + + /* Trivial case: fall back to im_copy(). + */ + if( in->BandFmt == ofmt ) + return( im_copy( in, out ) ); + + if( !(clip = clip_new( in, out, ofmt )) ) + return( -1 ); + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->BandFmt = ofmt; + out->Bbits = im_bits_of_fmt( ofmt ); + + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) || + im_generate( out, start_clip, clip_gen, stop_clip, in, clip ) ) + return( -1 ); + + return( 0 ); +} + +/* Legacy clippers. + */ +int +im_clip( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_UCHAR ) ); +} + +int +im_clip2c( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_CHAR ) ); +} + +int +im_clip2us( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_USHORT ) ); +} + +int +im_clip2s( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_SHORT ) ); +} + +int +im_clip2ui( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_UINT ) ); +} + +int +im_clip2i( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_INT ) ); +} + +int +im_clip2f( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_FLOAT ) ); +} + +int +im_clip2d( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_DOUBLE ) ); +} + +int +im_clip2cm( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_COMPLEX ) ); +} + +int +im_clip2dcm( IMAGE *in, IMAGE *out ) +{ + return( im_clip2fmt( in, out, IM_BANDFMT_DPCOMPLEX ) ); +} diff --git a/libsrc/conversion/im_copy.c b/libsrc/conversion/im_copy.c new file mode 100644 index 00000000..43326969 --- /dev/null +++ b/libsrc/conversion/im_copy.c @@ -0,0 +1,530 @@ +/* @(#) Copy an image. + * @(#) + * @(#) int + * @(#) im_copy( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Copy and set informational header fields + * @(#) + * @(#) int im_copy_set( in, out, type, xres, yres, xoff, yoff ) + * @(#) IMAGE *in, *out; + * @(#) int type; + * @(#) float xres, yres; + * @(#) int xoff, yoff; + * @(#) + * @(#) copy, swapping byte order + * @(#) + * @(#) int + * @(#) im_copy_swap( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris, based on im_powtra() + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 23/4/93 J.Cupitt + * - adapted to work with partial images + * 30/6/93 JC + * - adapted for partial v2 + * - and ANSI C + * 7/7/93 JC + * - now does IM_CODING_LABQ too + * 22/2/95 JC + * - new use of im_region_region() + * 25/6/02 JC + * - added im_copy_set() + * - hint is IM_ANY + * 5/9/02 JC + * - added xoff/yoff to copy_set + * 14/4/04 JC + * - im_copy() now zeros Xoffset/Yoffset (since origin is the same as + * input) + * 26/5/04 JC + * - added im_copy_swap() + * 1/6/05 + * - added im_copy_morph() + * 13/6/05 + * - oop, im_copy_set() was messed up + * 29/9/06 + * - added im_copy_set_meta(), handy wrapper for nip2 to set meta fields + * 2/11/06 + * - moved im__convert_saveable() here so it's always defined (was part + * of JPEG write code) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Copy a small area. + */ +static int +copy_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + + /* Ask for input we need. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* Attach output region to that. + */ + if( im_region_region( or, ir, r, r->left, r->top ) ) + return( -1 ); + + return( 0 ); +} + +/* Copy image, changing header fields. + */ +static int +im_copy_set_all( IMAGE *in, IMAGE *out, + int Type, float Xres, float Yres, int Xoffset, int Yoffset, + int Bands, int BandFmt, int Coding ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_copy", _( "in must be uncoded" ) ); + return( -1 ); + } + if( Coding != IM_CODING_NONE && Coding != IM_CODING_LABQ ) { + im_error( "im_copy", _( "Coding must be NONE or LABQ" ) ); + return( -1 ); + } + if( BandFmt < 0 || BandFmt > IM_BANDFMT_DPCOMPLEX ) { + im_error( "im_copy", _( "BandFmt must be in range [0,%d]" ), + IM_BANDFMT_DPCOMPLEX ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = Type; + out->Xres = Xres; + out->Yres = Yres; + out->Xoffset = Xoffset; + out->Yoffset = Yoffset; + out->Bands = Bands; + out->BandFmt = BandFmt; + out->Coding = Coding; + out->Bbits = im_bits_of_fmt( BandFmt ); + + /* Sanity check: we (may) have changed bytes-per-pixel since we've + * changed Bands and BandFmt ... bad! + */ + if( IM_IMAGE_SIZEOF_PEL( in ) != IM_IMAGE_SIZEOF_PEL( out ) ) { + im_error( "im_copy", _( "sizeof( pixel ) has changed" ) ); + return( -1 ); + } + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, im_start_one, copy_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Copy image, changing informational header fields. + */ +int +im_copy_set( IMAGE *in, IMAGE *out, + int Type, float Xres, float Yres, int Xoffset, int Yoffset ) +{ + return( im_copy_set_all( in, out, + Type, Xres, Yres, 0, 0, + in->Bands, in->BandFmt, in->Coding ) ); +} + +/* Copy image, changing nothing. + */ +int +im_copy( IMAGE *in, IMAGE *out ) +{ + return( im_copy_set( in, out, + in->Type, in->Xres, in->Yres, 0, 0 ) ); +} + +/* Copy image, changing fields which affect pixel layout. + */ +int +im_copy_morph( IMAGE *in, IMAGE *out, + int Bands, int BandFmt, int Coding ) +{ + return( im_copy_set_all( in, out, + in->Type, in->Xres, in->Yres, 0, 0, + Bands, BandFmt, Coding ) ); +} + +int +im_copy_set_meta( IMAGE *in, IMAGE *out, const char *field, GValue *value ) +{ + if( im_copy( in, out ) || + im_meta_set( out, field, value ) ) + return( 1 ); + + return( 0 ); +} + +/* Swap pairs of bytes. + */ +static void +im_copy_swap2_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = IM_IMAGE_SIZEOF_PEL( im ) * width; /* Bytes in buffer */ + + for( x = 0; x < sz; x += 2 ) { + out[x] = in[x + 1]; + out[x + 1] = in[x]; + } +} + +/* Swap 4- of bytes. + */ +static void +im_copy_swap4_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = IM_IMAGE_SIZEOF_PEL( im ) * width; /* Bytes in buffer */ + + for( x = 0; x < sz; x += 4 ) { + out[x] = in[x + 3]; + out[x + 1] = in[x + 2]; + out[x + 2] = in[x + 1]; + out[x + 3] = in[x]; + } +} + +/* Swap 8- of bytes. + */ +static void +im_copy_swap8_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + int x; + int sz = IM_IMAGE_SIZEOF_PEL( im ) * width; /* Bytes in buffer */ + + for( x = 0; x < sz; x += 8 ) { + out[x] = in[x + 7]; + out[x + 1] = in[x + 6]; + out[x + 2] = in[x + 5]; + out[x + 3] = in[x + 4]; + out[x + 4] = in[x + 3]; + out[x + 5] = in[x + 2]; + out[x + 6] = in[x + 1]; + out[x + 7] = in[x]; + } +} + +/* Copy, swapping byte order between little and big endian. + */ +int +im_copy_swap( IMAGE *in, IMAGE *out ) +{ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_copy_swap", _( "in must be uncoded" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: + case IM_BANDFMT_UCHAR: + if( im_copy( in, out ) ) + return( -1 ); + break; + + case IM_BANDFMT_SHORT: + case IM_BANDFMT_USHORT: + if( im_wrapone( in, out, + (im_wrapone_fn) im_copy_swap2_gen, in, NULL ) ) + return( -1 ); + break; + + case IM_BANDFMT_INT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_COMPLEX: + if( im_wrapone( in, out, + (im_wrapone_fn) im_copy_swap4_gen, in, NULL ) ) + return( -1 ); + break; + + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_DPCOMPLEX: + if( im_wrapone( in, out, + (im_wrapone_fn) im_copy_swap8_gen, in, NULL ) ) + return( -1 ); + break; + + default: + im_error( "im_copy_swap", _( "unsupported image type" ) ); + return( -1 ); + } + + return( 0 ); +} + +int +im_copy_from( IMAGE *in, IMAGE *out, im_arch_type architecture ) +{ + switch( architecture ) { + case IM_ARCH_NATIVE: + return( im_copy( in, out ) ); + + case IM_ARCH_BYTE_SWAPPED: + return( im_copy_swap( in, out ) ); + + case IM_ARCH_LSB_FIRST: + return( im_amiMSBfirst() ? + im_copy_swap( in, out ) : im_copy( in, out ) ); + + case IM_ARCH_MSB_FIRST: + return( im_amiMSBfirst() ? + im_copy( in, out ) : im_copy_swap( in, out ) ); + + default: + im_error( "im_copy_from", + _( "bad architecture: %d" ), architecture ); + return( -1 ); + } +} + +/* Convert to 1 or 3 band uchar sRGB (or 2/4 band, if allow_alpha is set). + * Need to im_close() the return IMAGE. + */ +IMAGE * +im__convert_saveable( IMAGE *in, gboolean allow_alpha ) +{ + IMAGE *out; + + if( !(out = im_open( "im__convert_saveable", "p" )) ) + return( NULL ); + + /* If this is a IM_CODING_LABQ, we can go straight to RGB. + */ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + static void *table = NULL; + + /* Make sure fast LabQ2disp tables are built. 7 is sRGB. + */ + if( !table ) + table = im_LabQ2disp_build_table( NULL, + im_col_displays( 7 ) ); + + if( !t || im_LabQ2disp_table( in, t, table ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + + /* Get the bands right. If we have >3, drop down to 3. If we have 2, + * drop down to 1. If allow_alpha is on, we can also have 2/4 bands. + */ + if( in->Coding == IM_CODING_NONE ) { + if( in->Bands == 2 && !allow_alpha ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || im_extract_band( in, t, 0 ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + else if( in->Bands > 3 && !allow_alpha ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || + im_extract_bands( in, t, 0, 3 ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + else if( in->Bands > 4 && allow_alpha ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || + im_extract_bands( in, t, 0, 4 ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + } + + /* Interpret the Type field for colorimetric images. + */ + if( in->Bands == 3 && in->BandFmt == IM_BANDFMT_SHORT && + in->Type == IM_TYPE_LABS ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || im_LabS2LabQ( in, t ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || im_LabQ2Lab( in, t ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + + if( in->Coding != IM_CODING_NONE ) { + im_close( out ); + return( NULL ); + } + + if( in->Bands == 3 && in->Type == IM_TYPE_LCH ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_LCh2Lab( t[0], t[1] ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + if( in->Bands == 3 && in->Type == IM_TYPE_YXY ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_Yxy2XYZ( t[0], t[1] ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + if( in->Bands == 3 && in->Type == IM_TYPE_UCS ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_UCS2XYZ( t[0], t[1] ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + if( in->Bands == 3 && in->Type == IM_TYPE_LAB ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_Lab2XYZ( t[0], t[1] ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + if( in->Bands == 3 && in->Type == IM_TYPE_XYZ ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "conv-1", "p" ) || + im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || + im_XYZ2disp( t[0], t[1], im_col_displays( 7 ) ) ) { + im_close( out ); + return( NULL ); + } + + in = t[1]; + } + + /* Clip to uchar if not there already. + */ + if( in->BandFmt != IM_BANDFMT_UCHAR ) { + IMAGE *t = im_open_local( out, "conv:1", "p" ); + + if( !t || im_clip( in, t ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + + if( im_copy( in, out ) ) { + im_close( out ); + return( NULL ); + } + + return( out ); +} + diff --git a/libsrc/conversion/im_csv2vips.c b/libsrc/conversion/im_csv2vips.c new file mode 100644 index 00000000..8f07076e --- /dev/null +++ b/libsrc/conversion/im_csv2vips.c @@ -0,0 +1,316 @@ +/* Read a csv file. + * + * 19/12/05 JC + * - hacked from ppm reader + * 11/9/06 + * - now distingushes whitespace and separators, so we can have blank + * fields + * 20/9/06 + * - oop, unquoted trailing columns could get missed + * 17/5/07 + * - added im_csv2vips_header() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +skip_line( FILE *fp ) +{ + int ch; + + while( (ch = fgetc( fp )) != '\n' && ch != EOF ) + ; + + return( ch ); +} + +static int +skip_white( FILE *fp, const char whitemap[256] ) +{ + int ch; + + do { + ch = fgetc( fp ); + } while (ch != EOF && ch != '\n' && whitemap[ch] ); + + ungetc( ch, fp ); + + return( ch ); +} + +static int +skip_to_sep( FILE *fp, const char sepmap[256] ) +{ + int ch; + + do { + ch = fgetc( fp ); + } while (ch != EOF && ch != '\n' && !sepmap[ch] ); + + ungetc( ch, fp ); + + return( ch ); +} + +/* Read a single item. Syntax is: + * + * item : whitespace* double? whitespace* [EOF|EOL|separator] + * + * Return the char that caused failure on fail (EOF or \n). + */ +static int +read_double( FILE *fp, const char whitemap[256], const char sepmap[256], + int lineno, int colno, double *out ) +{ + int ch; + + /* The fscanf() may change this ... but all other cases need a zero. + */ + *out = 0; + + ch = skip_white( fp, whitemap ); + if( ch == EOF || ch == '\n' ) + return( ch ); + + if( !sepmap[ch] && fscanf( fp, "%lf", out ) != 1 ) { + /* Only a warning, since (for example) exported spreadsheets + * will often have text or date fields. + */ + im_warn( "im_csv2vips", + _( "error parsing number, line %d, column %d" ), + lineno, colno ); + + /* Step over the bad data to the next separator. + */ + ch = skip_to_sep( fp, sepmap ); + } + + /* Don't need to check result, we have read a field successfully. + */ + ch = skip_white( fp, whitemap ); + + /* If it's a separator, we have to step over it. + */ + if( ch != EOF && sepmap[ch] ) + (void) fgetc( fp ); + + return( 0 ); +} + +static int +read_csv( FILE *fp, IMAGE *out, + int start_skip, + const char *whitespace, const char *separator, + int lines ) +{ + int i; + char whitemap[256]; + char sepmap[256]; + const char *p; + fpos_t pos; + int columns; + int ch; + double d; + double *buf; + int y; + + /* Make our char maps. + */ + for( i = 0; i < 256; i++ ) { + whitemap[i] = 0; + sepmap[i] = 0; + } + for( p = whitespace; *p; p++ ) + whitemap[(int) *p] = 1; + for( p = separator; *p; p++ ) + sepmap[(int) *p] = 1; + + /* Skip first few lines. + */ + for( i = 0; i < start_skip; i++ ) + if( skip_line( fp ) == EOF ) { + im_error( "im_csv2vips", + _( "end of file while skipping start" ) ); + return( -1 ); + } + + /* Parse the first line to get number of columns. Only bother checking + * fgetpos() the first time we use it: assume it's working after this. + */ + if( fgetpos( fp, &pos ) ) { + im_error_system( errno, "im_csv2vips", _( "unable to seek" ) ); + return( -1 ); + } + for( columns = 0; (ch = read_double( fp, whitemap, sepmap, + start_skip + 1, columns + 1, &d )) == 0; columns++ ) + ; + fsetpos( fp, &pos ); + + if( columns == 0 ) { + im_error( "im_csv2vips", _( "empty line" ) ); + return( -1 ); + } + if( ch == -2 ) + /* Failed to parse a number. + */ + return( -1 ); + + /* If lines is -1, we have to parse the whole file to get the + * number of lines out. + */ + if( lines == -1 ) { + fgetpos( fp, &pos ); + for( lines = 0; skip_line( fp ) != EOF; lines++ ) + ; + fsetpos( fp, &pos ); + } + + im_initdesc( out, columns, lines, 1, + IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + + if( im_outcheck( out ) || im_setupout( out ) || + !(buf = IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( out ), double )) ) + return( -1 ); + + for( y = 0; y < lines; y++ ) { + int x; + + for( x = 0; x < columns; x++ ) { + ch = read_double( fp, whitemap, sepmap, + y + start_skip + 1, x + 1, &d ); + if( ch == EOF ) { + im_error( "im_csv2vips", + _( "unexpected end of file" ) ); + return( -1 ); + } + else if( ch == '\n' ) { + im_error( "im_csv2vips", + _( "unexpected end of line" ) ); + return( -1 ); + } + else if( ch ) + /* Parse error. + */ + return( -1 ); + + buf[x] = d; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + + /* Skip over the '\n' to the next line. + */ + skip_line( fp ); + } + + return( 0 ); +} + +int +im_csv2vips( const char *filename, IMAGE *out ) +{ + /* Read options. + */ + int start_skip = 0; + char *whitespace = " \""; + char *separator = ";,\t"; + int lines = -1; + + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char *p, *q, *r; + FILE *fp; + + /* Parse mode string. + */ + im_filename_split( filename, name, mode ); + p = &mode[0]; + while( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "ski", q ) && (r = im_getsuboption( q )) ) + start_skip = atoi( r ); + else if( im_isprefix( "whi", q ) && (r = im_getsuboption( q )) ) + whitespace = r; + else if( im_isprefix( "sep", q ) && (r = im_getsuboption( q )) ) + separator = r; + else if( im_isprefix( "lin", q ) && (r = im_getsuboption( q )) ) + lines = atoi( r ); + } + + if( !(fp = fopen( name, "r" )) ) { + im_error( "im_csv2vips", + _( "unable to open \"%s\"" ), name ); + return( -1 ); + } + + if( read_csv( fp, out, start_skip, whitespace, separator, lines ) ) { + fclose( fp ); + return( -1 ); + } + fclose( fp ); + + return( 0 ); +} + +/* We can't just read the header of a CSV. Instead, we read to a temp image, + * then copy just the header to the output. + */ +int +im_csv2vips_header( const char *filename, IMAGE *out ) +{ + IMAGE *t; + + if( !(t = im_open( "im_csv2vips_header", "p" )) ) + return( -1 ); + if( im_csv2vips( filename, t ) || + im_cp_desc( out, t ) ) { + im_close( t ); + return( -1 ); + } + im_close( t ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_exr2vips.c b/libsrc/conversion/im_exr2vips.c new file mode 100644 index 00000000..4e3d57b0 --- /dev/null +++ b/libsrc/conversion/im_exr2vips.c @@ -0,0 +1,424 @@ +/* Convert OpenEXR to VIPS + * + * 1/5/06 + * - from im_png2vips.c + * 17/5/06 + * - oops, buffer calcs were wrong + * 19/5/06 + * - added tiled read, with a separate cache + * - removed *255 we had before, better to do something clever with + * chromaticities + + - colour management + - attributes + - more of OpenEXR's pixel formats + - more than just RGBA channels + + the openexr C API is very limited ... it seems RGBA half pixels is + all you can do + + openexr lets you have different formats in different channels :-( + + there's no API to read the "chromaticities" attribute :-( + + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_OPENEXR + +#include + +int +im_exr2vips( const char *name, IMAGE *out ) +{ + im_error( "im_exr2vips", _( "OpenEXR support disabled" ) ); + return( -1 ); +} + +int +im_exr2vips_header( const char *name, IMAGE *out ) +{ + im_error( "im_exr2vips_header", _( "OpenEXR support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_OPENEXR*/ + +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What we track during a OpenEXR read. + */ +typedef struct { + char *name; + IMAGE *out; + + ImfTiledInputFile *tiles; + ImfInputFile *lines; + const ImfHeader *header; + Rect window; + int tile_width; + int tile_height; + + /* Need to single-thread calls to ReadTile. + */ + GMutex *lock; +} Read; + +static void +get_imf_error( void ) +{ + im_error( "im_exr2vips", _( "EXR error: %s" ), ImfErrorMessage() ); +} + +static void +read_destroy( Read *read ) +{ + IM_FREE( read->name ); + + IM_FREEF( ImfCloseTiledInputFile, read->tiles ); + IM_FREEF( ImfCloseInputFile, read->lines ); + + IM_FREEF( g_mutex_free, read->lock ); + + im_free( read ); +} + +static Read * +read_new( const char *name, IMAGE *out ) +{ + Read *read; + int xmin, ymin; + int xmax, ymax; + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + + read->name = im_strdup( NULL, name ); + read->out = out; + read->tiles = NULL; + read->lines = NULL; + read->lock = NULL; + + if( im_add_close_callback( out, + (im_callback_fn) read_destroy, read, NULL ) ) { + read_destroy( read ); + return( NULL ); + } + + /* Try to open tiled first ... if that fails, fall back to scanlines. + + FIXME ... seems a bit ugly, but how else can you spot a tiled + EXR image? + + */ + if( !(read->tiles = ImfOpenTiledInputFile( read->name )) ) { + if( !(read->lines = ImfOpenInputFile( read->name )) ) { + get_imf_error(); + return( NULL ); + } + } + +#ifdef DEBUG + if( read->tiles ) + printf( "im_exr2vips: opening in tiles mode\n" ); + else + printf( "im_exr2vips: opening in scanline mode\n" ); +#endif /*DEBUG*/ + + if( read->tiles ) { + read->header = ImfTiledInputHeader( read->tiles ); + read->lock = g_mutex_new(); + read->tile_width = ImfTiledInputTileXSize( read->tiles ); + read->tile_height = ImfTiledInputTileYSize( read->tiles ); + } + else + read->header = ImfInputHeader( read->lines ); + + ImfHeaderDataWindow( read->header, &xmin, &ymin, &xmax, &ymax ); + read->window.left = xmin; + read->window.top = ymin; + read->window.width = xmax - xmin + 1; + read->window.height = ymax - ymin + 1; + + return( read ); +} + +/* Read a OpenEXR file (header) into a VIPS (header). + */ +static int +exr2vips_header( Read *read, IMAGE *out ) +{ + /* + + FIXME ... not really sRGB. I think EXR is actually linear (no + gamma). We ought to read the chromaticities from the header, put + through a 3x3 matrix and output as XYZ + + */ + im_initdesc( out, + read->window.width, read->window.height, 4, + IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_sRGB, 1.0, 1.0, 0, 0 ); + + return( 0 ); +} + +/* Read a OpenEXR file header into a VIPS header. + */ +int +im_exr2vips_header( const char *name, IMAGE *out ) +{ + Read *read; + + if( !(read = read_new( name, out )) || + exr2vips_header( read, out ) ) + return( -1 ); + + return( 0 ); +} + +static int +fill_region( REGION *out, ImfRgba *imf_buffer, Read *read ) +{ + Rect *r = &out->valid; + + const int tw = read->tile_width; + const int th = read->tile_height; + + /* Find top left of tiles we need. + */ + const int xs = (r->left / tw) * tw; + const int ys = (r->top / th) * th; + + int x, y, z; + Rect image; + + /* Area of image. + */ + image.left = 0; + image.top = 0; + image.width = read->out->Xsize; + image.height = read->out->Ysize; + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += th ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += tw ) { + Rect tile; + Rect hit; + int result; + + if( !ImfTiledInputSetFrameBuffer( read->tiles, + imf_buffer - + (read->window.left + x) - + (read->window.top + y) * tw, + 1, tw ) ) { + get_imf_error(); + return( -1 ); + } + +#ifdef DEBUG + printf( "im_exr2vips: requesting tile %d x %d\n", + x / tw, y / th ); +#endif /*DEBUG*/ + + g_mutex_lock( read->lock ); + result = ImfTiledInputReadTile( read->tiles, + x / tw, y / th, 0, 0 ); + g_mutex_unlock( read->lock ); + + if( !result ) { + get_imf_error(); + return( -1 ); + } + + /* The tile in the file, in VIPS coordinates. + */ + tile.left = x; + tile.top = y; + tile.width = tw; + tile.height = th; + im_rect_intersectrect( &tile, &image, &tile ); + + /* The part of this tile that hits the region. + */ + im_rect_intersectrect( &tile, r, &hit ); + + /* Convert to float and write to the region. + */ + for( z = 0; z < hit.height; z++ ) { + ImfRgba *p = imf_buffer + + (hit.left - tile.left) + + (hit.top - tile.top + z) * tw; + float *q = (float *) IM_REGION_ADDR( out, + hit.left, hit.top + z ); + + ImfHalfToFloatArray( 4 * hit.width, + (ImfHalf *) p, q ); + } + } + + return( 0 ); +} + +/* Allocate a tile buffer. + */ +static void * +seq_start( IMAGE *out, Read *read ) +{ + ImfRgba *imf_buffer; + + if( !(imf_buffer = IM_ARRAY( out, + read->tile_width * read->tile_height, ImfRgba )) ) + return( NULL ); + + return( (void *) imf_buffer ); +} + +/* Read tilewise. + */ +static int +exr2vips_tiles( Read *read, IMAGE *out ) +{ + if( exr2vips_header( read, out ) || + im_poutcheck( out ) || + im_demand_hint( out, IM_SMALLTILE, NULL ) || + im_generate( out, seq_start, fill_region, NULL, read, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Read scanlinewise. + */ +static int +exr2vips_lines( Read *read, IMAGE *out ) +{ + const int left = read->window.left; + const int top = read->window.top; + const int width = read->window.width; + const int height = read->window.height; + + ImfRgba *imf_buffer; + float *vips_buffer; + int y; + + if( !(imf_buffer = IM_ARRAY( out, width, ImfRgba )) || + !(vips_buffer = IM_ARRAY( out, 4 * width, float )) || + exr2vips_header( read, out ) || + im_outcheck( out ) || + im_setupout( out ) ) + return( -1 ); + + for( y = 0; y < height; y++ ) { + if( !ImfInputSetFrameBuffer( read->lines, + imf_buffer - left - (top + y) * width, + 1, width ) ) { + get_imf_error(); + return( -1 ); + } + if( !ImfInputReadPixels( read->lines, top + y, top + y ) ) { + get_imf_error(); + return( -1 ); + } + + ImfHalfToFloatArray( 4 * width, + (ImfHalf *) imf_buffer, vips_buffer ); + + if( im_writeline( y, out, (PEL *) vips_buffer ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +exr2vips( Read *read ) +{ + if( read->tiles ) { + IMAGE *raw; + + /* Tile cache: keep enough for two complete rows of tiles. + * This lets us do (smallish) area ops, like im_conv(), while + * still only hitting each OpenEXR tile once. + */ + if( !(raw = im_open_local( read->out, "cache", "p" )) ) + return( -1 ); + if( exr2vips_tiles( read, raw ) ) + return( -1 ); + if( im_tile_cache( raw, read->out, + read->tile_width, read->tile_height, + 2 * (1 + raw->Xsize / read->tile_width) ) ) + return( -1 ); + } + else { + if( exr2vips_lines( read, read->out ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read a OpenEXR file into a VIPS image. + */ +int +im_exr2vips( const char *name, IMAGE *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "im_exr2vips: reading \"%s\"\n", name ); +#endif /*DEBUG*/ + + if( !(read = read_new( name, out )) || + exr2vips( read ) ) + return( -1 ); + + return( 0 ); +} + +#endif /*HAVE_OPENEXR*/ diff --git a/libsrc/conversion/im_extract.c b/libsrc/conversion/im_extract.c new file mode 100644 index 00000000..b7ace8e7 --- /dev/null +++ b/libsrc/conversion/im_extract.c @@ -0,0 +1,261 @@ +/* @(#) Function to extract a portion of a Vasari format picture. + * @(#) Function im_extract() assumes that the imin image + * @(#) is either memory mapped or in the buffer pimin->data. + * @(#) + * @(#) int im_extract(pimin, pimout, pbox) + * @(#) IMAGE *pimin, *pimout; + * @(#) IMAGE_BOX *pbox; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 12/02/1990 + * Modified on: 4/6/92, J.Cupitt + * - speed up! why wasn't this done before? Why am I stupid? + * - layout, messages fixed + * now extracts IM_CODING_LABQ to IM_CODING_LABQ file: K.Martinez 1/7/93 + * 2/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 7/7/93 JC + * - behaviour for IM_CODING_LABQ fixed + * - better messages + * 7/10/94 JC + * - new IM_NEW() + * 22/2/95 JC + * - new use of im_region_region() + * 6/7/98 JC + * - im_extract_area() and im_extract_band() added + * 11/7/01 JC + * - im_extract_band() now numbers from zero + * 7/11/01 JC + * - oh what pain, im_extract now numbers bands from zero as well + * 6/9/02 JC + * - zero xoff/yoff for extracted area + * 14/4/04 JC + * - nope, -ve the origin + * 17/7/04 + * - added im_extract_bands(), remove many bands from image + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Extract one or more bands ... get number of output bands from + * or->im->Bands. + */ +static int +extract_band( REGION *or, REGION *ir, IMAGE *in, IMAGE_BOX *box ) +{ + Rect iarea; + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int es = IM_IMAGE_SIZEOF_ELEMENT( ir->im ); + int ipel = IM_IMAGE_SIZEOF_PEL( ir->im ); + int opel = IM_IMAGE_SIZEOF_PEL( or->im ); + char *p, *q; + int x, y, z; + + /* Ask for input we need. + */ + iarea = or->valid; + iarea.left += box->xstart; + iarea.top += box->ystart; + if( im_prepare( ir, &iarea ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + p = IM_REGION_ADDR( ir, le + box->xstart, y + box->ystart ) + + box->chsel * es; + q = IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + for( z = 0; z < opel; z++ ) + q[z] = p[z]; + + p += ipel; + q += opel; + } + } + + return( 0 ); +} + +/* Extract an area. Can just use pointers. + */ +static int +extract_area( REGION *or, REGION *ir, IMAGE *in, IMAGE_BOX *box ) +{ + Rect iarea; + + /* Ask for input we need. Translate from demand in or's space to + * demand in ir's space. + */ + iarea = or->valid; + iarea.left += box->xstart; + iarea.top += box->ystart; + if( im_prepare( ir, &iarea ) ) + return( -1 ); + + /* Attach or to ir. + */ + if( im_region_region( or, ir, &or->valid, iarea.left, iarea.top ) ) + return( -1 ); + + return( 0 ); +} + +/* Extract an area and bands from an image. + */ +int +im_extract_areabands( IMAGE *in, IMAGE *out, + int left, int top, int width, int height, int band, int nbands ) +{ + IMAGE_BOX *box; + + if( im_piocheck( in, out ) ) + return( -1 ); + if( band < 0 || nbands < 1 || band + nbands > in->Bands ) { + im_error( "im_extract_areabands", + _( "band selection out of range" ) ); + return( -1 ); + } + if( left + width > in->Xsize || + top + height > in->Ysize || + left < 0 || top < 0 || + width <= 0 || height <= 0 ) { + im_error( "im_extract_areabands", _( "bad extract area" ) ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) { + if( in->Coding != IM_CODING_LABQ ) { + im_error( "im_extract_areabands", + _( "unknown coding" ) ); + return( -1 ); + } + + /* We only do area extract for coding == labq. + */ + if( band != 0 || nbands != in->Bands ) { + im_error( "im_extract_areabands", + _( "can only extract areas from LABQ" ) ); + return( -1 ); + } + } + + /* Set up the output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = nbands; + out->Xsize = width; + out->Ysize = height; + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + if( !(box = IM_NEW( out, IMAGE_BOX )) ) + return( -1 ); + box->xstart = left; + box->ystart = top; + box->xsize = width; + box->ysize = height; + box->chsel = band; + + /* Extracting all bands is a special case ... we can do it with + * pointers. + */ + if( band == 0 && nbands == in->Bands ) { + if( im_generate( out, + im_start_one, extract_area, im_stop_one, in, box ) ) + return( -1 ); + } + else { + if( im_generate( out, + im_start_one, extract_band, im_stop_one, in, box ) ) + return( -1 ); + } + + out->Xoffset = -left; + out->Yoffset = -top; + + return( 0 ); +} + +/* Legacy interface. + */ +int +im_extract( IMAGE *in, IMAGE *out, IMAGE_BOX *box ) +{ + if( box->chsel == -1 ) + return( im_extract_areabands( in, out, + box->xstart, box->ystart, box->xsize, box->ysize, + 0, in->Bands ) ); + else + return( im_extract_areabands( in, out, + box->xstart, box->ystart, box->xsize, box->ysize, + box->chsel, 1 ) ); +} + +/* Convenience functions. + */ +int +im_extract_area( IMAGE *in, IMAGE *out, + int left, int top, int width, int height ) +{ + return( im_extract_areabands( in, out, + left, top, width, height, 0, in->Bands ) ); +} + +int +im_extract_bands( IMAGE *in, IMAGE *out, int chsel, int nbands ) +{ + return( im_extract_areabands( in, out, + 0, 0, in->Xsize, in->Ysize, chsel, nbands ) ); +} + +int +im_extract_band( IMAGE *in, IMAGE *out, int chsel ) +{ + return( im_extract_bands( in, out, chsel, 1 ) ); +} diff --git a/libsrc/conversion/im_falsecolour.c b/libsrc/conversion/im_falsecolour.c new file mode 100644 index 00000000..5b4112b6 --- /dev/null +++ b/libsrc/conversion/im_falsecolour.c @@ -0,0 +1,355 @@ +/* @(#) Maps a one band uchar in image to a 3 band uchar out image. The + * @(#) colours are chosen to give an image of uniform luminance, but + * @(#) with a wide range of hues. + * @(#) + * @(#) Input should be either memory mapped or in buffer, out should have + * @(#) been set by a call to im_openout() or im_setbuf(). + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_falsecolour( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 23/6/95 JC + * - rewritten for PIO + * - now walks edges of colour cube to get more saturated appearance + * 21/8/05 + * - uses falsecolour scale from PET scanner + * 7/4/06 + * - hmm, reversed scale + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Falsecolour scale nicked from a PET scan. + */ +static unsigned char PET_colour[][3] = { + { 12, 0, 25 }, + { 17, 0, 34 }, + { 20, 0, 41 }, + { 22, 0, 45 }, + { 23, 0, 47 }, + { 27, 0, 55 }, + { 12, 0, 25 }, + { 5, 0, 11 }, + { 5, 0, 11 }, + { 5, 0, 11 }, + { 1, 0, 4 }, + { 1, 0, 4 }, + { 6, 0, 13 }, + { 15, 0, 30 }, + { 19, 0, 40 }, + { 23, 0, 48 }, + { 28, 0, 57 }, + { 36, 0, 74 }, + { 42, 0, 84 }, + { 46, 0, 93 }, + { 51, 0, 102 }, + { 59, 0, 118 }, + { 65, 0, 130 }, + { 69, 0, 138 }, + { 72, 0, 146 }, + { 81, 0, 163 }, + { 47, 0, 95 }, + { 12, 0, 28 }, + { 64, 0, 144 }, + { 61, 0, 146 }, + { 55, 0, 140 }, + { 52, 0, 137 }, + { 47, 0, 132 }, + { 43, 0, 128 }, + { 38, 0, 123 }, + { 30, 0, 115 }, + { 26, 0, 111 }, + { 23, 0, 108 }, + { 17, 0, 102 }, + { 9, 0, 94 }, + { 6, 0, 91 }, + { 2, 0, 87 }, + { 0, 0, 88 }, + { 0, 0, 100 }, + { 0, 0, 104 }, + { 0, 0, 108 }, + { 0, 0, 113 }, + { 0, 0, 121 }, + { 0, 0, 125 }, + { 0, 0, 129 }, + { 0, 0, 133 }, + { 0, 0, 141 }, + { 0, 0, 146 }, + { 0, 0, 150 }, + { 0, 0, 155 }, + { 0, 0, 162 }, + { 0, 0, 167 }, + { 0, 0, 173 }, + { 0, 0, 180 }, + { 0, 0, 188 }, + { 0, 0, 193 }, + { 0, 0, 197 }, + { 0, 0, 201 }, + { 0, 0, 209 }, + { 0, 0, 214 }, + { 0, 0, 218 }, + { 0, 0, 222 }, + { 0, 0, 230 }, + { 0, 0, 235 }, + { 0, 0, 239 }, + { 0, 0, 243 }, + { 0, 0, 247 }, + { 0, 4, 251 }, + { 0, 10, 255 }, + { 0, 14, 255 }, + { 0, 18, 255 }, + { 0, 24, 255 }, + { 0, 31, 255 }, + { 0, 36, 255 }, + { 0, 39, 255 }, + { 0, 45, 255 }, + { 0, 53, 255 }, + { 0, 56, 255 }, + { 0, 60, 255 }, + { 0, 66, 255 }, + { 0, 74, 255 }, + { 0, 77, 255 }, + { 0, 81, 255 }, + { 0, 88, 251 }, + { 0, 99, 239 }, + { 0, 104, 234 }, + { 0, 108, 230 }, + { 0, 113, 225 }, + { 0, 120, 218 }, + { 0, 125, 213 }, + { 0, 128, 210 }, + { 0, 133, 205 }, + { 0, 141, 197 }, + { 0, 145, 193 }, + { 0, 150, 188 }, + { 0, 154, 184 }, + { 0, 162, 176 }, + { 0, 167, 172 }, + { 0, 172, 170 }, + { 0, 180, 170 }, + { 0, 188, 170 }, + { 0, 193, 170 }, + { 0, 197, 170 }, + { 0, 201, 170 }, + { 0, 205, 170 }, + { 0, 211, 170 }, + { 0, 218, 170 }, + { 0, 222, 170 }, + { 0, 226, 170 }, + { 0, 232, 170 }, + { 0, 239, 170 }, + { 0, 243, 170 }, + { 0, 247, 170 }, + { 0, 251, 161 }, + { 0, 255, 147 }, + { 0, 255, 139 }, + { 0, 255, 131 }, + { 0, 255, 120 }, + { 0, 255, 105 }, + { 0, 255, 97 }, + { 0, 255, 89 }, + { 0, 255, 78 }, + { 0, 255, 63 }, + { 0, 255, 55 }, + { 0, 255, 47 }, + { 0, 255, 37 }, + { 0, 255, 21 }, + { 0, 255, 13 }, + { 0, 255, 5 }, + { 2, 255, 2 }, + { 13, 255, 13 }, + { 18, 255, 18 }, + { 23, 255, 23 }, + { 27, 255, 27 }, + { 35, 255, 35 }, + { 40, 255, 40 }, + { 43, 255, 43 }, + { 48, 255, 48 }, + { 55, 255, 55 }, + { 60, 255, 60 }, + { 64, 255, 64 }, + { 69, 255, 69 }, + { 72, 255, 72 }, + { 79, 255, 79 }, + { 90, 255, 82 }, + { 106, 255, 74 }, + { 113, 255, 70 }, + { 126, 255, 63 }, + { 140, 255, 56 }, + { 147, 255, 53 }, + { 155, 255, 48 }, + { 168, 255, 42 }, + { 181, 255, 36 }, + { 189, 255, 31 }, + { 197, 255, 27 }, + { 209, 255, 21 }, + { 224, 255, 14 }, + { 231, 255, 10 }, + { 239, 255, 7 }, + { 247, 251, 3 }, + { 255, 243, 0 }, + { 255, 239, 0 }, + { 255, 235, 0 }, + { 255, 230, 0 }, + { 255, 222, 0 }, + { 255, 218, 0 }, + { 255, 214, 0 }, + { 255, 209, 0 }, + { 255, 201, 0 }, + { 255, 197, 0 }, + { 255, 193, 0 }, + { 255, 188, 0 }, + { 255, 180, 0 }, + { 255, 176, 0 }, + { 255, 172, 0 }, + { 255, 167, 0 }, + { 255, 156, 0 }, + { 255, 150, 0 }, + { 255, 146, 0 }, + { 255, 142, 0 }, + { 255, 138, 0 }, + { 255, 131, 0 }, + { 255, 125, 0 }, + { 255, 121, 0 }, + { 255, 117, 0 }, + { 255, 110, 0 }, + { 255, 104, 0 }, + { 255, 100, 0 }, + { 255, 96, 0 }, + { 255, 90, 0 }, + { 255, 83, 0 }, + { 255, 78, 0 }, + { 255, 75, 0 }, + { 255, 71, 0 }, + { 255, 67, 0 }, + { 255, 65, 0 }, + { 255, 63, 0 }, + { 255, 59, 0 }, + { 255, 54, 0 }, + { 255, 52, 0 }, + { 255, 50, 0 }, + { 255, 46, 0 }, + { 255, 41, 0 }, + { 255, 39, 0 }, + { 255, 36, 0 }, + { 255, 32, 0 }, + { 255, 25, 0 }, + { 255, 22, 0 }, + { 255, 20, 0 }, + { 255, 17, 0 }, + { 255, 13, 0 }, + { 255, 10, 0 }, + { 255, 7, 0 }, + { 255, 4, 0 }, + { 255, 0, 0 }, + { 252, 0, 0 }, + { 251, 0, 0 }, + { 249, 0, 0 }, + { 248, 0, 0 }, + { 244, 0, 0 }, + { 242, 0, 0 }, + { 240, 0, 0 }, + { 237, 0, 0 }, + { 234, 0, 0 }, + { 231, 0, 0 }, + { 229, 0, 0 }, + { 228, 0, 0 }, + { 225, 0, 0 }, + { 222, 0, 0 }, + { 221, 0, 0 }, + { 219, 0, 0 }, + { 216, 0, 0 }, + { 213, 0, 0 }, + { 212, 0, 0 }, + { 210, 0, 0 }, + { 207, 0, 0 }, + { 204, 0, 0 }, + { 201, 0, 0 }, + { 199, 0, 0 }, + { 196, 0, 0 }, + { 193, 0, 0 }, + { 192, 0, 0 }, + { 190, 0, 0 }, + { 188, 0, 0 }, + { 184, 0, 0 }, + { 183, 0, 0 }, + { 181, 0, 0 }, + { 179, 0, 0 }, + { 175, 0, 0 }, + { 174, 0, 0 }, + { 174, 0, 0 } +}; + +/* False colour. One band uchar images only. + */ +int +im_falsecolour( IMAGE *in, IMAGE *out ) +{ + IMAGE *lut; + + /* Check our args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Bands != 1 || in->Coding != IM_CODING_NONE || + in->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_falsecolour", _( "input image not one band " + "uchar uncoded" ) ); + return( -1 ); + } + + if( !(lut = im_image( (PEL *) PET_colour, + 1, 256, 3, IM_BANDFMT_UCHAR )) ) + return( -1 ); + if( im_maplut( in, out, lut ) ) { + im_close( lut ); + return( -1 ); + } + + im_close( lut ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_fliphor.c b/libsrc/conversion/im_fliphor.c new file mode 100644 index 00000000..997d2513 --- /dev/null +++ b/libsrc/conversion/im_fliphor.c @@ -0,0 +1,150 @@ +/* @(#) Flips image left-right. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_fliphor( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1990, N. Dessipris + * Written on: 28/10/91 + * Updated on: + * 19/7/93 JC + * - now allows IM_CODING_LABQ too + * - yuk! needs rewriting + * 21/12/94 JC + * - rewritten + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Flip a small area. + */ +static int +flip_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + Rect in; + char *p, *q; + int x, y, z; + + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int ps = IM_IMAGE_SIZEOF_PEL( ir->im ); /* sizeof pel */ + + int hgt = ir->im->Xsize - r->width; + + int lastx; + + /* Transform to input coordinates. + */ + in = *r; + in.left = hgt - r->left; + + /* Find x of final pixel in input area. + */ + lastx = IM_RECT_RIGHT( &in ) - 1; + + /* Ask for input we need. + */ + if( im_prepare( ir, &in ) ) + return( -1 ); + + /* Loop, copying and reversing lines. + */ + for( y = to; y < bo; y++ ) { + p = IM_REGION_ADDR( ir, lastx, y ); + q = IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + /* Copy the pel. + */ + for( z = 0; z < ps; z++ ) + q[z] = p[z]; + + /* Skip forwards in out, back in in. + */ + q += ps; + p -= ps; + } + } + + return( 0 ); +} + +/* Copy image. + */ +int +im_fliphor( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_fliphor: in must be uncoded" ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, im_start_one, flip_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = in->Xsize; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/conversion/im_flipver.c b/libsrc/conversion/im_flipver.c new file mode 100644 index 00000000..99c4a311 --- /dev/null +++ b/libsrc/conversion/im_flipver.c @@ -0,0 +1,141 @@ +/* @(#) Flips image up-down. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_flipver( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1990, N. Dessipris + * Written on: 28/10/91 + * Updated on: + * 21/12/94 JC + * - adapted from new im_fliphor(). + * 30/8/96 JC + * - ooops! IM_REGION_SIZEOF_LINE() not valid until im_prepare() has been + * called + * 7/3/03 JC + * - ahem, memcpy() line size calc was wrong, occasional segvs + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Flip a small area. + */ +static int +flip_gen( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + Rect in; + PEL *p, *q; + int y; + + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM( r ); + + int ls; + int psk, qsk; + + /* Transform to input coordinates. + */ + in = *r; + in.top = ir->im->Ysize - bo; + + /* Ask for input we need. + */ + if( im_prepare( ir, &in ) ) + return( -1 ); + + /* Loop, copying and reversing lines. + */ + p = (PEL *) IM_REGION_ADDR( ir, le, in.top + in.height - 1 ); + q = (PEL *) IM_REGION_ADDR( or, le, to ); + psk = IM_REGION_LSKIP( ir ); + qsk = IM_REGION_LSKIP( or ); + ls = IM_REGION_SIZEOF_LINE( or ); + + for( y = to; y < bo; y++ ) { + memcpy( q, p, ls ); + + p -= psk; + q += qsk; + } + + return( 0 ); +} + +/* Copy image. + */ +int +im_flipver( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_flipver: in must be uncoded" ); + return( -1 ); + } + + /* Prepare output header. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, im_start_one, flip_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = in->Ysize; + + return( 0 ); +} diff --git a/libsrc/conversion/im_gbandjoin.c b/libsrc/conversion/im_gbandjoin.c new file mode 100644 index 00000000..6ec3ee8f --- /dev/null +++ b/libsrc/conversion/im_gbandjoin.c @@ -0,0 +1,228 @@ +/* @(#) Function to perform a band-wise join of no images. + * @(#) Input images can have any number of bands; for instance if im[0] has j + * @(#) bands, im[1] k, ...., im[no-1] l bands, output has j+k+...+l bands + * @(#) respectively + * @(#) + * @(#) Function im_gbandjoin() assumes that the imin image + * @(#) is either memory mapped or in buffer + * @(#) + * @(#) int im_gbandjoin( imarray, imout, no ) + * @(#) IMAGE *imarray[], *imout; + * @(#) int no; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris, modification of im_bandjoin() + * + * Author: N. Dessipris + * Written on: 17/04/1991 + * Modified on : + * 16/3/94 JC + * - rewritten for partials + * - now in ANSI C + * - now works for any number of input images, except zero + * 7/10/94 JC + * - new IM_NEW() + * 16/4/07 + * - fall back to im_copy() for 1 input image + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Struct we carry stuff around in. + */ +typedef struct joins { + int nim; /* Number of input images */ + IMAGE **in; /* Array of input images, NULL-terminated */ + int *is; /* An int for SIZEOF_PEL() for each image */ +} Join; + +/* Make a Join struct. + */ +static Join * +make_join( IMAGE *out, IMAGE **in, int nim ) +{ + Join *jn; + int i; + + if( !(jn = IM_NEW( out, Join )) ) + return( NULL ); + jn->nim = nim; + if( !(jn->in = IM_ARRAY( out, nim + 1, IMAGE * )) || + !(jn->is = IM_ARRAY( out, nim, int )) ) + return( NULL ); + + /* Remember to NULL-terminate. + */ + for( i = 0; i < nim; i++ ) { + jn->in[i] = in[i]; + jn->is[i] = IM_IMAGE_SIZEOF_PEL( in[i] ); + } + jn->in[nim] = NULL; + + return( jn ); +} + +/* Perform join. + */ +static int +join_bands( REGION *or, REGION **ir, IMAGE **in, Join *jn ) +{ + int x, y, z, i; + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ps = IM_IMAGE_SIZEOF_PEL( or->im ); + + /* Prepare each input area. + */ + for( i = 0; i < jn->nim; i++ ) + if( im_prepare( ir[i], r ) ) + return( -1 ); + + /* Loop over output! + */ + for( y = to; y < bo; y++ ) { + PEL *qb = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Loop for each input image. + */ + for( i = 0; i < jn->nim; i++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir[i], le, y ); + PEL *q = qb; + int k = jn->is[i]; + + /* Copy all PELs from this line of this input image + * into the correct place in the output line. + */ + for( x = le; x < ri; x++ ) { + PEL *qn = q; + + /* Copy one PEL. + */ + for( z = 0; z < k; z++ ) + *q++ = *p++; + + /* Skip to the point at which the next PEL + * from this input should go. + */ + q = qn + ps; + } + + /* Move on to the line start for the next PEL. + */ + qb += k; + } + } + + return( 0 ); +} + +/* Band-wise join of a vector of image descriptors. + */ +int +im_gbandjoin( IMAGE **in, IMAGE *out, int nim ) +{ + int i; + Join *jn; + + /* Check it out! + */ + if( nim < 1 ) { + im_error( "im_gbandjoin", _( "zero input images!" ) ); + return( -1 ); + } + if( nim == 1 ) + return( im_copy( in[0], out ) ); + + /* Check our args. + */ + if( im_poutcheck( out ) ) + return( -1 ); + for( i = 0; i < nim; i++ ) { + if( im_pincheck( in[i] ) ) + return( -1 ); + + if( in[i]->Coding != IM_CODING_NONE ) { + im_error( "im_gbandjoin", _( "uncoded input only" ) ); + return( -1 ); + } + + if( in[0]->BandFmt != in[i]->BandFmt ) { + im_error( "im_gbandjoin", + _( "input images differ in format" ) ); + return( -1 ); + } + if( in[0]->Xsize != in[i]->Xsize || + in[0]->Ysize != in[i]->Ysize ) { + im_error( "im_gbandjoin", + _( "input images differ in size" ) ); + return( -1 ); + } + } + + /* Build a data area. + */ + if( !(jn = make_join( out, in, nim )) ) + return( -1 ); + + /* Prepare the output header. + */ + if( im_cp_desc_array( out, jn->in ) ) + return( -1 ); + out->Bands = 0; + for( i = 0; i < nim; i++ ) + out->Bands += in[i]->Bands; + + /* Set demand hints. + */ + if( im_demand_hint_array( out, IM_THINSTRIP, jn->in ) ) + return( -1 ); + + if( im_generate( out, + im_start_many, join_bands, im_stop_many, jn->in, jn ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_grid.c b/libsrc/conversion/im_grid.c new file mode 100644 index 00000000..465cf4c5 --- /dev/null +++ b/libsrc/conversion/im_grid.c @@ -0,0 +1,180 @@ +/* chop a tall thin image up into a grid ... useful for displaying volumetric + * images + * + * 4/8/05 + * 7/9/05 + * - oops, clipping was wrong + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG +#define DEBUG_PAINT +#define DEBUG_MAKE + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct _Grid { + IMAGE *in; + IMAGE *out; + int tile_height; + int across; + int down; +} Grid; + +static int +grid_gen( REGION *or, REGION *ir, IMAGE *in, Grid *grid ) +{ + Rect *r = &or->valid; + int twidth = grid->in->Xsize; + int theight = grid->tile_height; + int x, y; + Rect tile; + + /* Find top left of tiles we need. + */ + int xs = (r->left / twidth) * twidth; + int ys = (r->top / theight) * theight; + + /* The tile enclosing the top-left corner of the requested area. + */ + tile.left = xs; + tile.top = ys; + tile.width = twidth; + tile.height = theight; + + /* If the request fits inside a single tile, we can just pointer-copy. + */ + if( im_rect_includesrect( &tile, r ) ) { + Rect irect; + + /* Translate request to input space. + */ + irect = *r; + irect.left -= xs; + irect.top -= ys; + irect.top += grid->across * ys + theight * (xs / twidth); + + if( im_prepare( ir, &irect ) || + im_region_region( or, ir, r, irect.left, irect.top ) ) + return( -1 ); + + return( 0 ); + } + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += theight ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += twidth ) { + Rect paint; + Rect input; + + /* Whole tile at x, y + */ + tile.left = x; + tile.top = y; + tile.width = twidth; + tile.height = theight; + + /* Which parts touch the area of the output we are + * building. + */ + im_rect_intersectrect( &tile, r, &paint ); + + assert( !im_rect_isempty( &paint ) ); + + /* Translate back to ir coordinates. + */ + input = paint; + input.left -= x; + input.top -= y; + input.top += grid->across * y + theight * (x / twidth); + + /* Render into or. + */ + if( im_prepare_to( ir, or, &input, + paint.left, paint.top ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_grid( IMAGE *in, IMAGE *out, int tile_height, int across, int down ) +{ + Grid *grid = IM_NEW( out, Grid ); + + if( !grid || im_piocheck( in, out ) ) + return( -1 ); + if( across <= 0 || down <= 0 ) { + im_error( "im_grid", _( "bad parameters" ) ); + return( -1 ); + } + if( in->Ysize % tile_height != 0 || + in->Ysize / tile_height != across * down ) { + im_error( "im_grid", _( "bad grid geometry" ) ); + return( -1 ); + } + + grid->in = in; + grid->out = out; + grid->tile_height = tile_height; + grid->across = across; + grid->down = down; + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Xsize * across; + out->Ysize = tile_height * down; + + /* We can render small tiles with pointer copies. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + if( im_generate( out, + im_start_one, grid_gen, im_stop_one, in, grid ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/conversion/im_insert.c b/libsrc/conversion/im_insert.c new file mode 100644 index 00000000..c19adb84 --- /dev/null +++ b/libsrc/conversion/im_insert.c @@ -0,0 +1,379 @@ +/* @(#) Insert one image into another. ins is inserted into image in at + * @(#) position x, y relative to the top LH corner of in. out is made large + * @(#) enough to hold both in and ins. Any areas of out not coming from + * @(#) either in or ins are set to black (binary 0). If ins overlaps in, ins + * @(#) will appear on top of in. Both images must have the same number of + * @(#) bands and the same BandFmt. + * @(#) + * @(#) im_insert_noepand() always outputs an image the same size as in. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_insert(in, ins, out, x, y ) + * @(#) IMAGE *in, *ins, *out; + * @(#) int x, y; + * @(#) + * @(#) int im_insert_noexpand(in, ins, out, x, y ) + * @(#) IMAGE *in, *ins, *out; + * @(#) int x, y; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Copyright: 1990, J. Cupitt + * + * Author: J. Cupitt + * Written on: 02/08/1990 + * Modified on : + * 31/8/93 JC + * - ANSIfied + * - Nicos' reformatting undone. Grr! + * 22/12/94 + * - modernised + * - now does IM_CODING_LABQ too + * 22/6/95 JC + * - partialized + * 10/2/02 JC + * - adapted for im_prepare_to() stuff + * 14/4/04 + * - sets Xoffset / Yoffset + * 3/7/06 + * - add sanity range checks + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold our state in this. + */ +typedef struct { + /* Args. + */ + IMAGE *main; /* Main image */ + IMAGE *sub; /* Sub image */ + IMAGE *out; /* Output image */ + int x, y; /* Position of sub wrt. main */ + + /* Geometry. + */ + Rect rout; /* Output space */ + Rect rmain; /* Position of main in output */ + Rect rsub; /* Position of sub in output */ +} InsertState; + +/* Trivial case: we just need pels from one of the inputs. + */ +static int +just_one( REGION *or, REGION *ir, int x, int y ) +{ + Rect need; + + /* Find the part of pos we need. + */ + need = or->valid; + need.left -= x; + need.top -= y; + if( im_prepare( ir, &need ) ) + return( -1 ); + + /* Attach our output to it. + */ + if( im_region_region( or, ir, &or->valid, need.left, need.top ) ) + return( -1 ); + + return( 0 ); +} + +/* Black out a region. + */ +static void +black_region( REGION *reg ) +{ + PEL *q = (PEL *) IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ); + int wd = IM_REGION_SIZEOF_LINE( reg ); + int ls = IM_REGION_LSKIP( reg ); + int y; + + for( y = 0; y < reg->valid.height; y++, q += ls ) + memset( (char *) q, 0, wd ); +} + +/* Paste in parts of ir that fall within or --- ir is an input REGION for an + * image positioned at pos within or. + */ +static int +paste_region( REGION *or, REGION *ir, Rect *pos ) +{ + Rect ovl; + + /* Does any of the sub-image appear in the area we have been asked + * to make? + */ + im_rect_intersectrect( &or->valid, pos, &ovl ); + if( !im_rect_isempty( &ovl ) ) { + /* Find the part of in we need. + */ + ovl.left -= pos->left; + ovl.top -= pos->top; + + /* Paint this area of pixels into or. + */ + if( im_prepare_to( ir, or, &ovl, + ovl.left + pos->left, ovl.top + pos->top ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Insert generate function. + */ +static int +insert_gen( REGION *or, REGION **ir, IMAGE **vec, InsertState *ins ) +{ + Rect ovl; + + /* Does the rect we have been asked for fall entirely inside the + * sub-image? + */ + if( im_rect_includesrect( &ins->rsub, &or->valid ) ) + return( just_one( or, ir[1], + ins->rsub.left, ins->rsub.top ) ); + + /* Does it fall entirely inside the main, and not at all inside the + * sub? + */ + im_rect_intersectrect( &or->valid, &ins->rsub, &ovl ); + if( im_rect_includesrect( &ins->rmain, &or->valid ) && + im_rect_isempty( &ovl ) ) + return( just_one( or, ir[0], + ins->rmain.left, ins->rmain.top ) ); + + /* Output requires both (or neither) input. If it is not entirely + * inside both the main and the sub, then there is going to be some + * black. + */ + if( !(im_rect_includesrect( &ins->rsub, &or->valid ) && + im_rect_includesrect( &ins->rmain, &or->valid )) ) + /* Could be clever --- but just black the whole thing for + * simplicity. + */ + black_region( or ); + + /* Paste from main. + */ + if( paste_region( or, ir[0], &ins->rmain ) ) + return( -1 ); + + /* Paste from sub. + */ + if( paste_region( or, ir[1], &ins->rsub ) ) + return( -1 ); + + return( 0 ); +} + +/* xy range we sanity check on ... just to stop crazy numbers from 1/0 etc. + * causing assert() failuresd later. + */ +#define RANGE (10000000) + +/* Do an insert. + */ +int +im_insert( IMAGE *main, IMAGE *sub, IMAGE *out, int x, int y ) +{ + InsertState *ins = IM_NEW( out, InsertState ); + IMAGE **vec; + + /* Check args. + */ + if( !ins || im_piocheck( main, out ) || im_pincheck( sub ) ) + return( -1 ); + if( main->BandFmt != sub->BandFmt || main->Bands != sub->Bands || + main->Coding != sub->Coding ) { + im_error( "im_insert", _( "inputs differ in format" ) ); + return( -1 ); + } + if( main->Coding != IM_CODING_NONE && main->Coding != IM_CODING_LABQ ) { + im_error( "im_insert", + _( "input should be uncoded or IM_CODING_LABQ" ) ); + return( -1 ); + } + if( x > RANGE || x < -RANGE || y > RANGE || y < -RANGE ) { + im_error( "im_insert", _( "xy out of range" ) ); + return( -1 ); + } + + /* Save args. + */ + ins->main = main; + ins->sub = sub; + ins->out = out; + ins->x = x; + ins->y = y; + + /* Calculate geometry. First, position rmain and rsub with (0,0) at + * top LH corner of main. + */ + ins->rmain.left = 0; + ins->rmain.top = 0; + ins->rmain.width = main->Xsize; + ins->rmain.height = main->Ysize; + ins->rsub.left = x; + ins->rsub.top = y; + ins->rsub.width = sub->Xsize; + ins->rsub.height = sub->Ysize; + + /* Now: output is bounding box of these two. + */ + im_rect_unionrect( &ins->rmain, &ins->rsub, &ins->rout ); + + /* Translate origin to top LH corner of rout. + */ + ins->rmain.left -= ins->rout.left; + ins->rmain.top -= ins->rout.top; + ins->rsub.left -= ins->rout.left; + ins->rsub.top -= ins->rout.top; + ins->rout.left = 0; + ins->rout.top = 0; + + /* Set up the output header. + */ + if( im_cp_descv( out, main, sub, NULL ) ) + return( -1 ); + out->Xsize = ins->rout.width; + out->Ysize = ins->rout.height; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, main, sub, NULL ) ) + return( -1 ); + + /* Make input array. + */ + if( !(vec = im_allocate_input_array( out, main, sub, NULL )) ) + return( -1 ); + + /* Make output image. + */ + if( im_generate( out, + im_start_many, insert_gen, im_stop_many, vec, ins ) ) + return( -1 ); + + out->Xoffset = ins->rmain.left; + out->Yoffset = ins->rmain.top; + + return( 0 ); +} + +/* As above, but don't expand to hold all of sub. + */ +int +im_insert_noexpand( IMAGE *main, IMAGE *sub, IMAGE *out, int x, int y ) +{ + InsertState *ins = IM_NEW( out, InsertState ); + IMAGE **vec; + + /* Check args. + */ + if( !ins || im_piocheck( main, out ) || im_pincheck( sub ) ) + return( -1 ); + if( main->BandFmt != sub->BandFmt || main->Bands != sub->Bands || + main->Coding != sub->Coding ) { + im_error( "im_insert_noexpand", + _( "inputs differ in format" ) ); + return( -1 ); + } + if( main->Coding != IM_CODING_NONE && main->Coding != IM_CODING_LABQ ) { + im_error( "im_insert_noexpand", + _( "input should be uncoded or IM_CODING_LABQ" ) ); + return( -1 ); + } + if( x > RANGE || x < -RANGE || y > RANGE || y < -RANGE ) { + im_error( "im_insert", _( "xy out of range" ) ); + return( -1 ); + } + + /* Save args. + */ + ins->main = main; + ins->sub = sub; + ins->out = out; + ins->x = x; + ins->y = y; + + /* Calculate geometry. + */ + ins->rmain.left = 0; + ins->rmain.top = 0; + ins->rmain.width = main->Xsize; + ins->rmain.height = main->Ysize; + ins->rsub.left = x; + ins->rsub.top = y; + ins->rsub.width = sub->Xsize; + ins->rsub.height = sub->Ysize; + ins->rout = ins->rmain; + + /* Set up the output header. + */ + if( im_cp_descv( out, main, sub, NULL ) ) + return( -1 ); + out->Xsize = ins->rout.width; + out->Ysize = ins->rout.height; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, main, sub, NULL ) ) + return( -1 ); + + /* Make input array. + */ + if( !(vec = im_allocate_input_array( out, main, sub, NULL )) ) + return( -1 ); + + /* Make output image. + */ + if( im_generate( out, + im_start_many, insert_gen, im_stop_many, vec, ins ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_jpeg2vips.c b/libsrc/conversion/im_jpeg2vips.c new file mode 100644 index 00000000..f36fd70b --- /dev/null +++ b/libsrc/conversion/im_jpeg2vips.c @@ -0,0 +1,714 @@ +/* Convert 1 or 3-band 8-bit VIPS images to/from JPEG. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 12/11/04 + * - better demand size choice for eval + * 30/6/05 JC + * - update im_error()/im_warn() + * - now loads and saves exif data + * 30/7/05 + * - now loads ICC profiles + * - now saves ICC profiles from the VIPS header + * 24/8/05 + * - jpeg load sets vips xres/yres from exif, if possible + * - jpeg save sets exif xres/yres from vips, if possible + * 29/8/05 + * - cut from old vips_jpeg.c + * 13/10/06 + * - add +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_JPEG + +#include + +int +im_jpeg2vips_header( const char *name, IMAGE *out ) +{ + im_error( "im_jpeg2vips_header", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +int +im_jpeg2vips( const char *name, IMAGE *out ) +{ + im_error( "im_jpeg2vips", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +#else /*HAVE_JPEG*/ + +#include +#include +#include +#include +#include + +#ifdef HAVE_EXIF +#ifdef UNTAGGED_EXIF +#include +#include +#include +#include +#else /*!UNTAGGED_EXIF*/ +#include +#include +#include +#include +#endif /*UNTAGGED_EXIF*/ +#endif /*HAVE_EXIF*/ + +#include +#include + +/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we + * also define. Make sure it's turned off. + */ +#ifdef HAVE_STDLIB_H +#undef HAVE_STDLIB_H +#endif /*HAVE_STDLIB_H*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define a new error handler for when we bomb out. + */ +typedef struct { + /* Public fields. + */ + struct jpeg_error_mgr pub; + + /* Private stuff for us. + */ + jmp_buf jmp; /* longjmp() here to get back to VIPS */ + FILE *fp; /* fclose() if non-NULL */ +} ErrorManager; + +/* New output message method - send to VIPS. + */ +METHODDEF(void) +new_output_message( j_common_ptr cinfo ) +{ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message)( cinfo, buffer ); + im_error( "vips_jpeg", _( "%s" ), buffer ); + +#ifdef DEBUG + printf( "vips_jpeg.c: new_output_message: \"%s\"\n", buffer ); +#endif /*DEBUG*/ +} + +/* New error_exit handler. + */ +METHODDEF(void) +new_error_exit( j_common_ptr cinfo ) +{ + ErrorManager *eman = (ErrorManager *) cinfo->err; + +#ifdef DEBUG + printf( "vips_jpeg.c: new_error_exit\n" ); +#endif /*DEBUG*/ + + /* Close the fp if necessary. + */ + if( eman->fp ) { + (void) fclose( eman->fp ); + eman->fp = NULL; + } + + /* Send the error message to VIPS. This method is overridden above. + */ + (*cinfo->err->output_message)( cinfo ); + + /* Jump back. + */ + longjmp( eman->jmp, 1 ); +} + +#ifdef HAVE_EXIF +#ifdef DEBUG_VERBOSE +/* Print exif for debugging ... hacked from exif-0.6.9/actions.c + */ +static void +show_tags( ExifData *data ) +{ + int i; + unsigned int tag; + const char *name; + + printf( "show EXIF tags:\n" ); + + for( i = 0; i < EXIF_IFD_COUNT; i++ ) + printf( "%-7.7s", exif_ifd_get_name( i ) ); + printf( "\n" ); + + for( tag = 0; tag < 0xffff; tag++ ) { + name = exif_tag_get_title( tag ); + if( !name ) + continue; + printf( " 0x%04x %-29.29s", tag, name ); + for( i = 0; i < EXIF_IFD_COUNT; i++ ) + if( exif_content_get_entry( data->ifd[i], tag ) ) + printf( " * " ); + else + printf( " - " ); + printf( "\n" ); + } +} + +static void +show_entry( ExifEntry *entry, void *client ) +{ + char exif_text[256]; + + printf( "%s", exif_tag_get_title( entry->tag ) ); + printf( "|" ); + printf( "%s", exif_entry_get_value( entry, exif_text, 256 ) ); + printf( "|" ); + printf( "%s", exif_format_get_name( entry->format ) ); + printf( "|" ); + printf( "%d bytes", entry->size ); + printf( "\n" ); +} + +static void +show_ifd( ExifContent *content, void *client ) +{ + exif_content_foreach_entry( content, show_entry, client ); + printf( "-\n" ); +} + +void +show_values( ExifData *data ) +{ + ExifByteOrder order; + + order = exif_data_get_byte_order( data ); + printf( "EXIF tags in '%s' byte order\n", + exif_byte_order_get_name( order ) ); + + printf( "%-20.20s", "Tag" ); + printf( "|" ); + printf( "%-58.58s", "Value" ); + printf( "\n" ); + + exif_data_foreach_content( data, show_ifd, NULL ); + + if( data->size ) + printf( "contains thumbnail of %d bytes\n", data->size ); +} +#endif /*DEBUG_VERBOSE*/ +#endif /*HAVE_EXIF*/ + +#ifdef HAVE_EXIF +static void +attach_exif_entry( ExifEntry *entry, IMAGE *im ) +{ + char name_text[256]; + VBuf name; + char value_text[256]; + VBuf value; + char exif_value[256]; + + im_buf_init_static( &name, name_text, 256 ); + im_buf_init_static( &value, value_text, 256 ); + + im_buf_appendf( &name, "exif-%s", exif_tag_get_title( entry->tag ) ); + im_buf_appendf( &value, "%s (%s, %d bytes)", + exif_entry_get_value( entry, exif_value, 256 ), + exif_format_get_name( entry->format ), + entry->size ); + + /* Can't do anything sensible with the error return. + */ + (void) im_meta_set_string( im, + im_buf_all( &name ), im_buf_all( &value ) ); +} + +static void +attach_exif_content( ExifContent *content, IMAGE *im ) +{ + exif_content_foreach_entry( content, + (ExifContentForeachEntryFunc) attach_exif_entry, im ); +} + +/* Just find the first occurence of the tag (is this correct?) + */ +static ExifEntry * +find_entry( ExifData *ed, ExifTag tag ) +{ + int i; + + for( i = 0; i < EXIF_IFD_COUNT; i++ ) { + ExifEntry *entry; + + if( (entry = exif_content_get_entry( ed->ifd[i], tag )) ) + return( entry ); + } + + return( NULL ); +} + +static int +get_entry_rational( ExifData *ed, ExifTag tag, double *out ) +{ + ExifEntry *entry; + ExifRational rational; + + if( !(entry = find_entry( ed, tag )) || + entry->format != EXIF_FORMAT_RATIONAL || + entry->components != 1 ) + return( -1 ); + + rational = exif_get_rational( entry->data, + exif_data_get_byte_order( ed ) ); + + *out = (double) rational.numerator / rational.denominator; + + return( 0 ); +} + +static int +get_entry_short( ExifData *ed, ExifTag tag, int *out ) +{ + ExifEntry *entry; + + if( !(entry = find_entry( ed, tag )) || + entry->format != EXIF_FORMAT_SHORT || + entry->components != 1 ) + return( -1 ); + + *out = exif_get_short( entry->data, + exif_data_get_byte_order( ed ) ); + + return( 0 ); +} + +static void +set_vips_resolution( IMAGE *im, ExifData *ed ) +{ + double xres, yres; + int unit; + + if( get_entry_rational( ed, EXIF_TAG_X_RESOLUTION, &xres ) || + get_entry_rational( ed, EXIF_TAG_Y_RESOLUTION, &yres ) || + get_entry_short( ed, EXIF_TAG_RESOLUTION_UNIT, &unit ) ) { + im_warn( "im_jpeg2vips", _( "error reading resolution" ) ); + return; + } + + switch( unit ) { + case 2: + /* In inches. + */ + xres /= 25.4; + yres /= 25.4; + break; + + case 3: + /* In cm. + */ + xres /= 10.0; + yres /= 10.0; + break; + + default: + im_warn( "im_jpeg2vips", _( "bad resolution unit" ) ); + return; + } + + im->Xres = xres; + im->Yres = yres; +} +#endif /*HAVE_EXIF*/ + +static int +read_exif( IMAGE *im, void *data, int data_length ) +{ + char *data_copy; + + /* Always attach a copy of the unparsed exif data. + */ + if( !(data_copy = im_malloc( NULL, data_length )) ) + return( -1 ); + memcpy( data_copy, data, data_length ); + if( im_meta_set_blob( im, IM_META_EXIF_NAME, + (im_callback_fn) im_free, data_copy, data_length ) ) { + im_free( data_copy ); + return( -1 ); + } + +#ifdef HAVE_EXIF +{ + ExifData *ed; + + if( !(ed = exif_data_new_from_data( data, data_length )) ) + return( -1 ); + + if( ed->size > 0 ) { +#ifdef DEBUG_VERBOSE + show_tags( ed ); + show_values( ed ); +#endif /*DEBUG_VERBOSE*/ + + /* Attach informational fields for what we find. + + FIXME ... better to have this in the UI layer? + + Or we could attach non-human-readable tags here (int, double + etc) and then move the human stuff to the UI layer? + + */ + exif_data_foreach_content( ed, + (ExifDataForeachContentFunc) attach_exif_content, im ); + + /* Look for resolution fields and use them to set the VIPS + * xres/yres fields. + */ + set_vips_resolution( im, ed ); + } + + exif_data_free( ed ); +} +#endif /*HAVE_EXIF*/ + + return( 0 ); +} + +/* Number of app2 sections we can capture. Each one can be 64k, so 640k should + * be enough for anyone (haha). + */ +#define MAX_APP2_SECTIONS (10) + +/* Read a cinfo to a VIPS image. + */ +static int +read_jpeg_header( struct jpeg_decompress_struct *cinfo, IMAGE *out ) +{ + jpeg_saved_marker_ptr p; + + /* Capture app2 sections here for assembly. + */ + void *app2_data[MAX_APP2_SECTIONS] = { 0 }; + int app2_data_length[MAX_APP2_SECTIONS] = { 0 }; + int data_length; + int i; + + /* Read JPEG header. + */ + jpeg_read_header( cinfo, TRUE ); + jpeg_calc_output_dimensions( cinfo ); + + /* Set VIPS header. + */ + im_initdesc( out, + cinfo->output_width, cinfo->output_height, + cinfo->output_components, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, IM_TYPE_sRGB, + 1.0, 1.0, 0, 0 ); + + /* Look for EXIF and ICC profile. + */ + for( p = cinfo->marker_list; p; p = p->next ) { + switch( p->marker ) { + case JPEG_APP0 + 1: + /* EXIF data. + */ +#ifdef DEBUG + printf( "read_jpeg_header: seen %d bytes of EXIF\n", + p->data_length ); +#endif /*DEBUG*/ + if( read_exif( out, p->data, p->data_length ) ) + return( -1 ); + break; + + case JPEG_APP0 + 2: + /* ICC profile. + */ +#ifdef DEBUG + printf( "read_jpeg_header: seen %d bytes of ICC\n", + p->data_length ); +#endif /*DEBUG*/ + + if( p->data_length > 14 && + im_isprefix( "ICC_PROFILE", + (char *) p->data ) ) { + /* cur_marker numbers from 1, according to + * spec. + */ + int cur_marker = p->data[12] - 1; + + if( cur_marker >= 0 && + cur_marker < MAX_APP2_SECTIONS ) { + app2_data[cur_marker] = p->data + 14; + app2_data_length[cur_marker] = + p->data_length - 14; + } + } + break; + + default: +#ifdef DEBUG + printf( "read_jpeg_header: seen %d bytes of data\n", + p->data_length ); +#endif /*DEBUG*/ + break; + } + } + + /* Assemble ICC sections. + */ + data_length = 0; + for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) + data_length += app2_data_length[i]; + if( data_length ) { + unsigned char *data; + int p; + +#ifdef DEBUG + printf( "read_jpeg_header: assembled %d byte ICC profile\n", + data_length ); +#endif /*DEBUG*/ + + if( !(data = im_malloc( NULL, data_length )) ) + return( -1 ); + + p = 0; + for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) { + memcpy( data + p, app2_data[i], app2_data_length[i] ); + p += app2_data_length[i]; + } + + if( im_meta_set_blob( out, IM_META_ICC_NAME, + (im_callback_fn) im_free, data, data_length ) ) { + im_free( data ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Read a JPEG file header into a VIPS header. + */ +int +im_jpeg2vips_header( const char *name, IMAGE *out ) +{ + struct jpeg_decompress_struct cinfo; + ErrorManager eman; + FILE *fp; + + /* Make jpeg decompress object. + */ + cinfo.err = jpeg_std_error( &eman.pub ); + eman.pub.error_exit = new_error_exit; + eman.pub.output_message = new_output_message; + eman.fp = NULL; + if( setjmp( eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + jpeg_destroy_decompress( &cinfo ); + + return( -1 ); + } + jpeg_create_decompress( &cinfo ); + + /* Make input. + */ +#ifdef BINARY_OPEN + if( !(fp = fopen( name, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( name, "r" )) ) { +#endif /*BINARY_OPEN*/ + jpeg_destroy_decompress( &cinfo ); + im_error( "im_jpeg2vips_header", + _( "unable to open \"%s\"" ), name ); + + return( -1 ); + } + eman.fp = fp; + jpeg_stdio_src( &cinfo, fp ); + + /* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile). + */ + jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff ); + jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff ); + + /* Read! + */ + if( read_jpeg_header( &cinfo, out ) ) { + jpeg_destroy_decompress( &cinfo ); + fclose( fp ); + return( -1 ); + } + + /* Close and tidy. + */ + fclose( fp ); + eman.fp = NULL; + jpeg_destroy_decompress( &cinfo ); + + if( eman.pub.num_warnings != 0 ) { + im_warn( "im_jpeg2vips_header", + _( "read header gave %ld warnings" ), + eman.pub.num_warnings ); + im_warn( "im_jpeg2vips_header", "%s", im_error_buffer() ); + } + + return( 0 ); +} + +/* Read a cinfo to a VIPS image. + */ +static int +read_jpeg_image( struct jpeg_decompress_struct *cinfo, IMAGE *out ) +{ + int y, sz; + JSAMPROW row_pointer[1]; + + /* Check VIPS. + */ + if( im_outcheck( out ) || im_setupout( out ) ) + return( -1 ); + + /* Get size of output line and make a buffer. + */ + sz = cinfo->output_width * cinfo->output_components; + row_pointer[0] = (JSAMPLE *) (*cinfo->mem->alloc_large) + ( (j_common_ptr) cinfo, JPOOL_IMAGE, sz ); + + /* Start up decompressor. + */ + jpeg_start_decompress( cinfo ); + + /* Process image. + */ + for( y = 0; y < out->Ysize; y++ ) { + if( jpeg_read_scanlines( cinfo, &row_pointer[0], 1 ) != 1 ) { + im_error( "im_jpeg2vips", _( "truncated JPEG file" ) ); + return( -1 ); + } + if( im_writeline( y, out, row_pointer[0] ) ) + return( -1 ); + } + + /* Stop decompressor. + */ + jpeg_finish_decompress( cinfo ); + + return( 0 ); +} + +/* Read a JPEG file into a VIPS image. + */ +int +im_jpeg2vips( const char *name, IMAGE *out ) +{ + struct jpeg_decompress_struct cinfo; + ErrorManager eman; + FILE *fp; + + /* Make jpeg compression object. + */ + cinfo.err = jpeg_std_error( &eman.pub ); + eman.pub.error_exit = new_error_exit; + eman.pub.output_message = new_output_message; + eman.fp = NULL; + if( setjmp( eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + jpeg_destroy_decompress( &cinfo ); + + return( -1 ); + } + jpeg_create_decompress( &cinfo ); + + /* Make input. + */ +#ifdef BINARY_OPEN + if( !(fp = fopen( name, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( name, "r" )) ) { +#endif /*BINARY_OPEN*/ + jpeg_destroy_decompress( &cinfo ); + im_error( "im_jpeg2vips", _( "unable to open \"%s\"" ), name ); + + return( -1 ); + } + eman.fp = fp; + jpeg_stdio_src( &cinfo, fp ); + + /* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile). + */ + jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff ); + jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff ); + + /* Convert! + */ + if( read_jpeg_header( &cinfo, out ) || + read_jpeg_image( &cinfo, out ) ) { + jpeg_destroy_decompress( &cinfo ); + fclose( fp ); + return( -1 ); + } + + /* Close and tidy. + */ + fclose( fp ); + eman.fp = NULL; + jpeg_destroy_decompress( &cinfo ); + + if( eman.pub.num_warnings != 0 ) { + im_warn( "im_jpeg2vips", _( "read gave %ld warnings" ), + eman.pub.num_warnings ); + im_warn( "im_jpeg2vips", "%s", im_error_buffer() ); + } + + return( 0 ); +} + +#endif /*HAVE_JPEG*/ diff --git a/libsrc/conversion/im_lrjoin.c b/libsrc/conversion/im_lrjoin.c new file mode 100644 index 00000000..c4e11fcf --- /dev/null +++ b/libsrc/conversion/im_lrjoin.c @@ -0,0 +1,89 @@ +/* @(#) To join two images left right. The resultant image has + * @(#) Xsize = im1.Xsize + im2.Xsize and Ysize the min of im1.Ysize, im2.Ysize + * @(#) Input images should have the same number of Bands and BandFmt + * @(#) + * @(#) Usage: + * @(#) int im_lrjoin( IMAGE *left, IMAGE *right, IMAGE *out) + * @(#) IMAGE *left, *right, *out; + * @(#) + * @(#) + * + * Copyright 1990, 1991: Kirk Martinez, N. Dessipris + * Author: Kirk Martinez, N. Dessipris + * Written on: 9/6/90 + * Modified on: 17/04/1991 + * 31/8/93 JC + * - args to memcpy() were reversed + * 14/11/94 JC + * - tided up and ANSIfied + * - now accepts IM_CODING_LABQ + * - memory leaks removed + * - bug in calculation of output Xsize removed (thanks Thomson!) + * - bug in checking of image compatibility fixed + * 23/10/95 JC + * - rewritten in terms of im_insert() + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_lrjoin( IMAGE *left, IMAGE *right, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_lrjoin:1", "p" ); + + /* Paste right and left together. + */ + if( !t1 || im_insert( left, right, t1, left->Xsize, 0 ) ) + return( -1 ); + + /* Extract the part which the old im_lrjoin() would have made. + */ + if( im_extract_area( t1, out, + 0, 0, t1->Xsize, IM_MIN( left->Ysize, right->Ysize ) ) ) + return( -1 ); + + out->Xoffset = left->Xsize; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/conversion/im_magick2vips.c b/libsrc/conversion/im_magick2vips.c new file mode 100644 index 00000000..b6f6ea48 --- /dev/null +++ b/libsrc/conversion/im_magick2vips.c @@ -0,0 +1,621 @@ +/* Read a file using libMagick + * + * 7/1/03 JC + * - from im_tiff2vips + * 3/2/03 JC + * - some InitializeMagick() fail with NULL arg + * 2/11/04 + * - im_magick2vips_header() also checks sensible width/height + * 28/10/05 + * - copy attributes to meta + * - write many-frame images as a big column if all frames have identical + * width/height/bands/depth + * 31/3/06 + * - test for magick attr support + * 8/5/06 + * - set RGB16/GREY16 if appropriate + * 10/8/07 + * - support 32/64 bit imagemagick too + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_MAGICK + +#include + +int +im_magick2vips( const char *filename, IMAGE *im ) +{ + im_error( "im_magick2vips", _( "libMagick support disabled" ) ); + return( -1 ); +} + +int +im_magick2vips_header( const char *filename, IMAGE *im ) +{ + im_error( "im_magick2vips", _( "libMagick support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_MAGICK*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What we track during a read call. + */ +typedef struct _Read { + char *filename; + IMAGE *im; + + Image *image; + ImageInfo *image_info; + ExceptionInfo exception; + + int n_frames; + Image **frames; + int frame_height; + + /* Mutex to serialise calls to libMagick during threaded read. + */ + GMutex *lock; +} Read; + +static int +read_destroy( Read *read ) +{ +#ifdef DEBUG + printf( "im_magick2vips: read_destroy: %s\n", read->filename ); +#endif /*DEBUG*/ + + IM_FREEF( DestroyImage, read->image ); + IM_FREEF( DestroyImageInfo, read->image_info ); + IM_FREE( read->frames ); + IM_FREE( read->filename ); + DestroyExceptionInfo( &read->exception ); + IM_FREEF( g_mutex_free, read->lock ); + im_free( read ); + + return( 0 ); +} + +static Read * +read_new( const char *filename, IMAGE *im ) +{ + Read *read; + static int inited = 0; + + if( !inited ) { + InitializeMagick( "" ); + inited = 1; + } + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + read->filename = im_strdup( NULL, filename ); + read->im = im; + read->image = NULL; + read->image_info = CloneImageInfo( NULL ); + GetExceptionInfo( &read->exception ); + read->n_frames = 0; + read->frames = NULL; + read->frame_height = 0; + read->lock = g_mutex_new(); + + if( im_add_close_callback( im, + (im_callback_fn) read_destroy, read, NULL ) ) { + read_destroy( read ); + return( NULL ); + } + + if( !read->filename || !read->image_info ) + return( NULL ); + + im_strncpy( read->image_info->filename, filename, MaxTextExtent ); + +#ifdef DEBUG + printf( "im_magick2vips: read_new: %s\n", read->filename ); +#endif /*DEBUG*/ + + return( read ); +} + +static int +get_bands( Image *image ) +{ + int bands; + + switch( image->colorspace ) { + case GRAYColorspace: + bands = 1; + break; + + case RGBColorspace: + bands = 3; + break; + + case sRGBColorspace: + bands = 3; + break; + + case CMYKColorspace: + bands = 4; + break; + + default: + im_error( "im_magick2vips", _( "unsupported colorspace %d" ), + (int) image->colorspace ); + return( -1 ); + } + + /* Alpha as well? + */ + if( image->matte ) { + assert( image->colorspace != CMYKColorspace ); + bands += 1; + } + + return( bands ); +} + +static int +parse_header( Read *read ) +{ + IMAGE *im = read->im; + Image *image = read->image; + + const ImageAttribute *attr; + Image *p; + int i; + + im->Xsize = image->columns; + im->Ysize = image->rows; + read->frame_height = image->rows; + if( (im->Bands = get_bands( image )) < 0 ) + return( -1 ); + + switch( image->depth ) { + case 8: + im->BandFmt = IM_BANDFMT_UCHAR; + im->Bbits = IM_BBITS_BYTE; + break; + + case 16: + im->BandFmt = IM_BANDFMT_USHORT; + im->Bbits = IM_BBITS_SHORT; + break; + +#ifdef UseHDRI + case 32: + im->BandFmt = IM_BANDFMT_FLOAT; + im->Bbits = IM_BBITS_FLOAT; + break; + + case 64: + im->BandFmt = IM_BANDFMT_DOUBLE; + im->Bbits = IM_BBITS_DOUBLE; + break; +#else /*!UseHDRI*/ + case 32: + im->BandFmt = IM_BANDFMT_UINT; + im->Bbits = IM_BBITS_INT; + break; + + /* No 64-bit int format in VIPS. + */ +#endif /*UseHDRI*/ + + default: + im_error( "im_magick2vips", _( "unsupported bit depth %d" ), + (int) image->depth ); + return( -1 ); + } + + switch( image->colorspace ) { + case GRAYColorspace: + if( im->BandFmt == IM_BANDFMT_USHORT ) + im->Type = IM_TYPE_GREY16; + else + im->Type = IM_TYPE_B_W; + break; + + case RGBColorspace: + if( im->BandFmt == IM_BANDFMT_USHORT ) + im->Type = IM_TYPE_RGB16; + else + im->Type = IM_TYPE_RGB; + break; + + case sRGBColorspace: + if( im->BandFmt == IM_BANDFMT_USHORT ) + im->Type = IM_TYPE_RGB16; + else + im->Type = IM_TYPE_sRGB; + break; + + case CMYKColorspace: + im->Type = IM_TYPE_CMYK; + break; + + default: + im_error( "im_magick2vips", _( "unsupported colorspace %d" ), + (int) image->colorspace ); + return( -1 ); + } + + switch( image->units ) { + case PixelsPerInchResolution: + im->Xres = image->x_resolution / 25.4; + im->Yres = image->y_resolution / 25.4; + break; + + case PixelsPerCentimeterResolution: + im->Xres = image->x_resolution / 10.0; + im->Yres = image->y_resolution / 10.0; + break; + + default: + im->Xres = 1.0; + im->Yres = 1.0; + break; + } + + /* Other fields. + */ + im->Coding = IM_CODING_NONE; + +#ifdef HAVE_GETNEXTIMAGEATTRIBUTE + /* Gah, magick6.something and later only. Attach any attributes. + */ + ResetImageAttributeIterator( image ); + while( (attr = GetNextImageAttribute( image )) ) { + char name_text[256]; + VBuf name; + + im_buf_init_static( &name, name_text, 256 ); + im_buf_appendf( &name, "magick-%s", attr->key ); + im_meta_set_string( im, im_buf_all( &name ), attr->value ); + +#ifdef DEBUG + printf( "key = \"%s\", value = \"%s\"\n", + attr->key, attr->value ); +#endif /*DEBUG*/ + } +#endif /*HAVE_GETNEXTIMAGEATTRIBUTE*/ + + /* Do we have a set of equal-sized frames? Append them. + + FIXME ... there must be an attribute somewhere from dicom read + which says this is a volumetric image + + */ + read->n_frames = 0; + for( p = image; p; (p = GetNextImageInList( p )) ) { + if( p->columns != im->Xsize || + p->rows != im->Ysize || + p->depth != im->Bbits || + get_bands( p ) != im->Bands ) + break; + + read->n_frames += 1; + } + if( p ) + /* Nope ... just do the first image in the list. + */ + read->n_frames = 1; + + /* Record frame pointers. + */ + im->Ysize *= read->n_frames; + if( !(read->frames = IM_ARRAY( NULL, read->n_frames, Image * )) ) + return( -1 ); + p = image; + for( i = 0; i < read->n_frames; i++ ) { + read->frames[i] = p; + p = GetNextImageInList( p ); + } + + return( 0 ); +} + +/* Divide by this to get 0 - MAX from a Quantum. Eg. consider QuantumRange == + * 65535, MAX == 255 (a Q16 ImageMagic representing an 8-bit image). Make sure + * this can't be zero (if QuantumRange < MAX) .. can happen if we have a Q8 + * ImageMagick trying to represent a 16-bit image. + */ +#define SCALE( MAX ) \ + (QuantumRange < (MAX) ? \ + 1 : \ + ((QuantumRange + 1) / ((MAX) + 1))) + +#define GRAY_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) \ + q[x] = pixels[x].green / SCALE( MAX ); \ +} + +#define GRAYA_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) { \ + q[0] = pixels[x].green / SCALE( MAX ); \ + q[1] = pixels[x].opacity / SCALE( MAX ); \ + \ + q += 2; \ + } \ +} + +#define RGB_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) { \ + q[0] = pixels[x].red / SCALE( MAX ); \ + q[1] = pixels[x].green / SCALE( MAX ); \ + q[2] = pixels[x].blue / SCALE( MAX ); \ + \ + q += 3; \ + } \ +} + +#define RGBA_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) { \ + q[0] = pixels[x].red / SCALE( MAX ); \ + q[1] = pixels[x].green / SCALE( MAX ); \ + q[2] = pixels[x].blue / SCALE( MAX ); \ + q[3] = pixels[x].opacity / SCALE( MAX ); \ + \ + q += 4; \ + } \ +} + +static void +unpack_pixels( IMAGE *im, PEL *q8, PixelPacket *pixels, int n ) +{ + int x; + + switch( im->Bands ) { + case 1: + /* Gray. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + GRAY_LOOP( unsigned char, 255 ); break; + case IM_BANDFMT_USHORT: + GRAY_LOOP( unsigned short, 65535 ); break; + case IM_BANDFMT_UINT: + GRAY_LOOP( unsigned int, 4294967295UL ); break; + case IM_BANDFMT_DOUBLE: + GRAY_LOOP( double, QuantumRange ); break; + + default: + assert( 0 ); + } + break; + + case 2: + /* Gray plus alpha. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + GRAYA_LOOP( unsigned char, 255 ); break; + case IM_BANDFMT_USHORT: + GRAYA_LOOP( unsigned short, 65535 ); break; + case IM_BANDFMT_UINT: + GRAYA_LOOP( unsigned int, 4294967295UL ); break; + case IM_BANDFMT_DOUBLE: + GRAYA_LOOP( double, QuantumRange ); break; + + default: + assert( 0 ); + } + break; + + case 3: + /* RGB. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + RGB_LOOP( unsigned char, 255 ); break; + case IM_BANDFMT_USHORT: + RGB_LOOP( unsigned short, 65535 ); break; + case IM_BANDFMT_UINT: + RGB_LOOP( unsigned int, 4294967295UL ); break; + case IM_BANDFMT_DOUBLE: + RGB_LOOP( double, QuantumRange ); break; + + default: + assert( 0 ); + } + break; + + case 4: + /* RGBA or CMYK. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + RGBA_LOOP( unsigned char, 255 ); break; + case IM_BANDFMT_USHORT: + RGBA_LOOP( unsigned short, 65535 ); break; + case IM_BANDFMT_UINT: + RGBA_LOOP( unsigned int, 4294967295UL ); break; + case IM_BANDFMT_DOUBLE: + RGBA_LOOP( double, QuantumRange ); break; + + default: + assert( 0 ); + } + break; + + default: + assert( 0 ); + } +} + +static PixelPacket * +get_pixels( Image *image, int left, int top, int width, int height ) +{ + PixelPacket *pixels; + + if( !(pixels = GetImagePixels( image, left, top, width, height )) ) + return( NULL ); + +/* Can't happen if red/green/blue are doubles. + */ +#ifndef UseHDRI + /* Unpack palette. + */ + if( image->storage_class == PseudoClass ) { + IndexPacket *indexes = GetIndexes( image ); + int i; + + for( i = 0; i < width * height; i++ ) { + IndexPacket x = indexes[i]; + + if( x < image->colors ) { + pixels[i].red = image->colormap[x].red; + pixels[i].green = image->colormap[x].green; + pixels[i].blue = image->colormap[x].blue; + } + } + } +#endif /*UseHDRI*/ + + return( pixels ); +} + +static int +magick_fill_region( REGION *out, void *dummy, Read *read ) +{ + Rect *r = &out->valid; + int y; + + for( y = 0; y < r->height; y++ ) { + int top = r->top + y; + int frame = top / read->frame_height; + int line = top % read->frame_height; + + PixelPacket *pixels; + + g_mutex_lock( read->lock ); + pixels = get_pixels( read->frames[frame], + r->left, line, r->width, 1 ); + g_mutex_unlock( read->lock ); + + if( !pixels ) { + im_error( "im_magick2vips", + _( "unable to read pixels" ) ); + return( -1 ); + } + + unpack_pixels( read->im, + (PEL *) IM_REGION_ADDR( out, r->left, top ), + pixels, r->width ); + } + + return( 0 ); +} + +int +im_magick2vips( const char *filename, IMAGE *im ) +{ + Read *read; + + if( !(read = read_new( filename, im )) ) + return( -1 ); + + read->image = ReadImage( read->image_info, &read->exception ); + if( !read->image ) { + im_error( "im_magick2vips", _( "unable to read file \"%s\"\n" + "libMagick error: %s %s" ), + filename, + read->exception.reason, read->exception.description ); + return( -1 ); + } + + if( parse_header( read ) || + im_poutcheck( im ) || + im_generate( im, NULL, magick_fill_region, NULL, read, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_magick2vips_header( const char *filename, IMAGE *im ) +{ + Read *read; + + if( !(read = read_new( filename, im )) ) + return( -1 ); + + read->image = PingImage( read->image_info, &read->exception ); + if( !read->image ) { + im_error( "im_magick2vips", _( "unable to ping file " + "\"%s\"\nlibMagick error: %s %s" ), + filename, + read->exception.reason, read->exception.description ); + return( -1 ); + } + + if( parse_header( read ) ) + return( -1 ); + + if( im->Xsize <= 0 || im->Ysize <= 0 ) { + im_error( "im_magick2vips", _( "bad image size" ) ); + return( -1 ); + } + + return( 0 ); +} + +#endif /*HAVE_MAGICK*/ diff --git a/libsrc/conversion/im_mask2vips.c b/libsrc/conversion/im_mask2vips.c new file mode 100644 index 00000000..8f864821 --- /dev/null +++ b/libsrc/conversion/im_mask2vips.c @@ -0,0 +1,97 @@ +/* @(#) Function to write a DOUBLEMASK to an IMAGE. + * @(#) + * @(#) int + * @(#) im_mask2vips( DOUBLEMASK *in, IMAGE *out ) + * @(#) + * @(#) The function returns -1 on error and 0 on success + * Author: J.Cupitt + * Written on: 6/6/94 + * Modified on: + * 7/10/94 JC + * - new IM_ARRAY() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_mask2vips( DOUBLEMASK *in, IMAGE *out ) +{ + int x, y; + double *buf, *p, *q; + + /* Check the mask. + */ + if( !in || !in->coeff ) { + im_errormsg( "im_mask2vips: bad input mask" ); + return( -1 ); + } + + /* Make the output image. + */ + im_initdesc( out, in->xsize, in->ysize, 1, + IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, + IM_CODING_NONE, + IM_TYPE_B_W, + 1.0, 1.0, + 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Make an output buffer. + */ + if( !(buf = IM_ARRAY( out, in->xsize, double )) ) + return( -1 ); + + /* Write! + */ + for( p = in->coeff, y = 0; y < out->Ysize; y++ ) { + q = buf; + + for( x = 0; x < out->Xsize; x++ ) + *q++ = *p++; + + if( im_writeline( y, out, (void *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + diff --git a/libsrc/conversion/im_msb.c b/libsrc/conversion/im_msb.c new file mode 100644 index 00000000..75d4f576 --- /dev/null +++ b/libsrc/conversion/im_msb.c @@ -0,0 +1,344 @@ +/* @(#) Convert a signed or unsigned, char or short or int image into unsigned + * @(#) char very quickly, by discarding the lower order bits. Signed values + * @(#) are converted to unsigned by adding 128. + * @(#) + * @(#) int + * @(#) im_msb( + * @(#) IMAGE *in, + * @(#) IMAGE *out + * @(#) ); + * @(#) + * @(#) As above, but also discard all but the specified band: + * @(#) + * @(#) int + * @(#) im_msb( + * @(#) IMAGE *in, + * @(#) IMAGE *out, + * @(#) int band + * @(#) ); + * + * Copyright: 2006, The Nottingham Trent University + * + * Author: Tom Vajzovic + * + * Written on: 2006-03-13 + * 27/9/06 + * - removed extra im_free() in im_copy() fallback + * 4/10/06 + * - removed warning on uchar fallback: it happens a lot with nip2 and + * isn't very serious + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H */ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + +/** MACROS **/ +/* BAND FORMAT TESTS */ + +#define SIZEOF_BAND(band_fmt) ( \ + ( IM_BANDFMT_UCHAR == (band_fmt) ) ? sizeof(unsigned char) \ + : ( IM_BANDFMT_CHAR == (band_fmt) ) ? sizeof(signed char) \ + : ( IM_BANDFMT_USHORT == (band_fmt) ) ? sizeof(unsigned short int) \ + : ( IM_BANDFMT_SHORT == (band_fmt) ) ? sizeof(signed short int) \ + : ( IM_BANDFMT_UINT == (band_fmt) ) ? sizeof(unsigned int) \ + : ( IM_BANDFMT_INT == (band_fmt) ) ? sizeof(signed int) \ + : ( IM_BANDFMT_FLOAT == (band_fmt) ) ? sizeof(float) \ + : ( IM_BANDFMT_DOUBLE == (band_fmt) ) ? sizeof(double) \ + : ( IM_BANDFMT_COMPLEX == (band_fmt) ) ? ( 2 * sizeof(float) ) \ + : ( 2 * sizeof(double) ) ) + +#define BANDFMT_SHORT_CHAR(band_fmt) ( \ + IM_BANDFMT_SHORT == (band_fmt) \ + || IM_BANDFMT_USHORT == (band_fmt) \ + || IM_BANDFMT_CHAR == (band_fmt) \ + || IM_BANDFMT_UCHAR == (band_fmt) ) + +#define BANDFMT_ANY_INT(band_fmt) ( \ + IM_BANDFMT_INT == (band_fmt) \ + || IM_BANDFMT_UINT == (band_fmt) \ + || BANDFMT_SHORT_CHAR(band_fmt) ) + +#define BANDFMT_UNSIGNED(band_fmt) ( \ + IM_BANDFMT_UINT == (band_fmt) \ + || IM_BANDFMT_USHORT == (band_fmt) \ + || IM_BANDFMT_UCHAR == (band_fmt) ) + +/* IMAGE TESTS */ + +#define IM_ANY_INT(im) BANDFMT_ANY_INT((im)-> BandFmt) +#define IM_UNSIGNED(im) BANDFMT_UNSIGNED((im)-> BandFmt) +#define IM_UNCODED(im) ( IM_CODING_NONE == (im)-> Coding ) + +/** LOCAL FUNCTIONS DECLARATIONS **/ +static void byte_select (unsigned char *in, unsigned char *out, int n, + size_t * params); +static void byte_select_flip (unsigned char *in, unsigned char *out, int n, + size_t * params); +static void msb_labq (unsigned char *in, unsigned char *out, int n); + +/** EXPORTED FUNCTIONS **/ + +int +im_msb (IMAGE * in, IMAGE * out) +{ +#define FUNCTION_NAME "im_msb" + + size_t *params; + im_wrapone_fn func; + +#define index (params[0]) +#define width (params[1]) +#define repeat (params[2]) + + if (im_piocheck (in, out)) + return -1; + + /* Stops a used-before-set warning. + */ + params = NULL; + + if (IM_UNCODED (in)) + { + + if (!IM_ANY_INT (in)) + { + im_error (FUNCTION_NAME, _("char, short or int only")); + return -1; + } + + params = IM_ARRAY (out, 3, size_t); + + if (!params) + return -1; + + width = SIZEOF_BAND (in->BandFmt); + +#ifdef WORDS_BIGENDIAN + index = 0; +#else + index = width - 1; +#endif + + repeat = in->Bands; + + if (IM_UNSIGNED (in)) + func = (im_wrapone_fn) byte_select; + else + func = (im_wrapone_fn) byte_select_flip; + + if (1 == width && (im_wrapone_fn) byte_select == func) + { + return im_copy (in, out); + } + } + + else if (IM_CODING_LABQ == in->Coding) + + func = (im_wrapone_fn) msb_labq; + + else + { + im_error (FUNCTION_NAME, _("unknown coding")); + return -1; + } + + if (im_cp_desc (out, in)) + return -1; + + out->Bbits = sizeof (unsigned char) << 3; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + + return im_wrapone (in, out, func, (void *) params, NULL); + +#undef index +#undef width +#undef repeat + +#undef FUNCTION_NAME +} + +int +im_msb_band (IMAGE * in, IMAGE * out, int band) +{ +#define FUNCTION_NAME "im_msb_band" + + size_t *params; + im_wrapone_fn func; + +#define index (params[0]) +#define width (params[1]) +#define repeat (params[2]) + + if (band < 0) + { + im_error (FUNCTION_NAME, _("bad arguments")); + return -1; + } + + if (im_piocheck (in, out)) + return -1; + + params = IM_ARRAY (out, 3, size_t); + + if (!params) + return -1; + + if (IM_UNCODED (in)) + { + + if (!IM_ANY_INT (in)) + { + im_error (FUNCTION_NAME, _("char, short or int only")); + return -1; + } + + if (band >= in->Bands) + { + im_error (FUNCTION_NAME, _("image does not have that many bands")); + return -1; + } + + width = SIZEOF_BAND (in->BandFmt); + +#ifdef WORDS_BIGENDIAN + index = width * band; +#else + index = (width * (band + 1)) - 1; +#endif + + width *= in->Bands; + repeat = 1; + + if (IM_UNSIGNED (in)) + func = (im_wrapone_fn) byte_select; + else + func = (im_wrapone_fn) byte_select_flip; + } + + else if (IM_CODING_LABQ == in->Coding) + { + + if (band > 2) + { + im_error (FUNCTION_NAME, _("image does not have that many bands")); + return -1; + } + width = 4; + repeat = 1; + index = band; + + if (band) + func = (im_wrapone_fn) byte_select_flip; + else + func = (im_wrapone_fn) byte_select; + } + else + { + im_error (FUNCTION_NAME, _("unknown coding")); + return -1; + } + + if (im_cp_desc (out, in)) + return -1; + + out->Bands = 1; + out->Bbits = sizeof (unsigned char) << 3; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + + return im_wrapone (in, out, func, (void *) params, NULL); + +#undef index +#undef width +#undef repeat + +#undef FUNCTION_NAME +} + +/** LOCAL FUNCTIONS DEFINITIONS **/ +static void +byte_select (unsigned char *in, unsigned char *out, int n, size_t * params) +{ +#define index (params[0]) +#define width (params[1]) +#define repeat (params[2]) + + unsigned char *stop = out + n * repeat; + + for (in += index; out < stop; in += width, ++out) + *out = *in; + +#undef index +#undef width +#undef repeat +} + +static void +byte_select_flip (unsigned char *in, unsigned char *out, int n, + size_t * params) +{ +#define index (params[0]) +#define width (params[1]) +#define repeat (params[2]) + + unsigned char *stop = out + n * repeat; + + for (in += index; out < stop; in += width, ++out) + *out = 0x80 ^ *in; + +#undef index +#undef width +#undef repeat +} + +static void +msb_labq (unsigned char *in, unsigned char *out, int n) +{ + unsigned char *stop = in + (n << 2); + + for (; in < stop; in += 4, out += 3) + { + out[0] = in[0]; + out[1] = 0x80 ^ in[1]; + out[2] = 0x80 ^ in[2]; + } +} diff --git a/libsrc/conversion/im_png2vips.c b/libsrc/conversion/im_png2vips.c new file mode 100644 index 00000000..a0d91ea8 --- /dev/null +++ b/libsrc/conversion/im_png2vips.c @@ -0,0 +1,381 @@ +/* Convert 1 to 4-band 8 or 16-bit VIPS images to/from PNG. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 22/2/05 + * - read non-interlaced PNG with a line buffer (thanks Michel Brabants) + * 11/1/06 + * - read RGBA palette-ized images more robustly (thanks Tom) + * 20/4/06 + * - auto convert to sRGB/mono (with optional alpha) for save + * 1/5/06 + * - from vips_png.c + * 8/5/06 + * - set RGB16/GREY16 if appropriate + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_PNG + +#include + +int +im_png2vips( const char *name, IMAGE *out ) +{ + im_error( "im_png2vips", _( "PNG support disabled" ) ); + return( -1 ); +} + +int +im_png2vips_header( const char *name, IMAGE *out ) +{ + im_error( "im_png2vips_header", _( "PNG support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_PNG*/ + +#include +#include +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#if PNG_LIBPNG_VER < 10003 +#error "PNG library too old." +#endif + +static void +user_error_function( png_structp png_ptr, png_const_charp error_msg ) +{ + im_error( "im_png2vips", _( "PNG error: \"%s\"" ), error_msg ); +} + +static void +user_warning_function( png_structp png_ptr, png_const_charp warning_msg ) +{ + im_error( "im_png2vips", _( "PNG warning: \"%s\"" ), warning_msg ); +} + +/* What we track during a PNG read. + */ +typedef struct { + char *name; + IMAGE *out; + + FILE *fp; + png_structp pPng; + png_infop pInfo; + png_bytep *row_pointer; + png_bytep data; +} Read; + +static void +read_destroy( Read *read ) +{ + if( read->name ) { + im_free( read->name ); + read->name = NULL; + } + if( read->fp ) { + fclose( read->fp ); + read->fp = NULL; + } + if( read->pPng ) + png_destroy_read_struct( &read->pPng, &read->pInfo, NULL ); + if( read->row_pointer ) { + im_free( read->row_pointer ); + read->row_pointer = NULL; + } + if( read->data ) { + im_free( read->data ); + read->data = NULL; + } + + im_free( read ); +} + +static Read * +read_new( const char *name, IMAGE *out ) +{ + Read *read; + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + + read->name = im_strdup( NULL, name ); + read->out = out; + read->fp = NULL; + read->pPng = NULL; + read->pInfo = NULL; + read->row_pointer = NULL; + read->data = NULL; + +#ifdef BINARY_OPEN + if( !(read->fp = fopen( name, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(read->fp = fopen( name, "r" )) ) { +#endif /*BINARY_OPEN*/ + read_destroy( read ); + im_error( "im_png2vips", _( "unable to open \"%s\"" ), name ); + return( NULL ); + } + + if( !(read->pPng = png_create_read_struct( + PNG_LIBPNG_VER_STRING, NULL, + user_error_function, user_warning_function )) ) { + read_destroy( read ); + return( NULL ); + } + + /* Catch PNG errors from png_create_info_struct(). + */ + if( setjmp( read->pPng->jmpbuf ) ) { + read_destroy( read ); + return( NULL ); + } + + if( !(read->pInfo = png_create_info_struct( read->pPng )) ) { + read_destroy( read ); + return( NULL ); + } + + return( read ); +} + +/* Yuk! Have to malloc enough space for the whole image. Interlaced PNG + * is not really suitable for large objects ... + */ +static int +png2vips_interlace( Read *read ) +{ + const int rowbytes = IM_IMAGE_SIZEOF_LINE( read->out ); + int y; + + if( !(read->row_pointer = IM_ARRAY( NULL, + read->pInfo->height, png_bytep )) ) + return( -1 ); + if( !(read->data = (png_bytep) im_malloc( NULL, + read->pInfo->height * rowbytes )) ) + return( -1 ); + + for( y = 0; y < (int) read->pInfo->height; y++ ) + read->row_pointer[y] = read->data + y * rowbytes; + if( im_outcheck( read->out ) || + im_setupout( read->out ) || + setjmp( read->pPng->jmpbuf ) ) + return( -1 ); + + png_read_image( read->pPng, read->row_pointer ); + + for( y = 0; y < (int) read->pInfo->height; y++ ) + if( im_writeline( y, read->out, read->row_pointer[y] ) ) + return( -1 ); + + return( 0 ); +} + +/* Noninterlaced images can be read without needing enough RAM for the whole + * image. + */ +static int +png2vips_noninterlace( Read *read ) +{ + const int rowbytes = IM_IMAGE_SIZEOF_LINE( read->out ); + int y; + + if( !(read->data = (png_bytep) im_malloc( NULL, rowbytes )) ) + return( -1 ); + if( im_outcheck( read->out ) || + im_setupout( read->out ) || + setjmp( read->pPng->jmpbuf ) ) + return( -1 ); + + for( y = 0; y < (int) read->pInfo->height; y++ ) { + png_read_row( read->pPng, read->data, NULL ); + + if( im_writeline( y, read->out, read->data ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read a PNG file (header) into a VIPS (header). + */ +static int +png2vips( Read *read, int header_only ) +{ + int bands, bpp, type; + + if( setjmp( read->pPng->jmpbuf ) ) + return( -1 ); + + png_init_io( read->pPng, read->fp ); + png_read_info( read->pPng, read->pInfo ); + + /* png_get_channels() gives us 1 band for palette images ... so look + * at colour_type for output bands. + */ + switch( read->pInfo->color_type ) { + case PNG_COLOR_TYPE_PALETTE: + bands = 3; + + /* Don't know if this is really correct. If there are + * transparent pixels, assume we're going to output RGBA. + */ + if( read->pInfo->num_trans ) + bands = 4; + + break; + + case PNG_COLOR_TYPE_GRAY: bands = 1; break; + case PNG_COLOR_TYPE_GRAY_ALPHA: bands = 2; break; + case PNG_COLOR_TYPE_RGB: bands = 3; break; + case PNG_COLOR_TYPE_RGB_ALPHA: bands = 4; break; + + default: + im_error( "im_png2vips", _( "unsupported colour type" ) ); + return( -1 ); + } + + /* 8 or 16 bit. + */ + bpp = read->pInfo->bit_depth > 8 ? 2 : 1; + + if( bpp > 1 ) { + if( bands < 3 ) + type = IM_TYPE_GREY16; + else + type = IM_TYPE_RGB16; + } + else { + if( bands < 3 ) + type = IM_TYPE_B_W; + else + type = IM_TYPE_sRGB; + } + + /* Expand palette images. + */ + if( read->pInfo->color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_expand( read->pPng ); + + /* Expand <8 bit images to full bytes. + */ + if( read->pInfo->bit_depth < 8 ) + png_set_packing( read->pPng ); + + /* If we're an INTEL byte order machine and this is 16bits, we need + * to swap bytes. + */ + if( read->pInfo->bit_depth > 8 && !im_amiMSBfirst() ) + png_set_swap( read->pPng ); + + /* Set VIPS header. + */ + im_initdesc( read->out, + read->pInfo->width, read->pInfo->height, bands, + bpp == 1 ? IM_BBITS_BYTE : IM_BBITS_SHORT, + bpp == 1 ? IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT, + IM_CODING_NONE, type, 1.0, 1.0, 0, 0 ); + + if( !header_only ) { + if( png_set_interlace_handling( read->pPng ) > 1 ) { + if( png2vips_interlace( read ) ) + return( -1 ); + } + else { + if( png2vips_noninterlace( read ) ) + return( -1 ); + } + } + + return( 0 ); +} + +/* Read a PNG file header into a VIPS header. + */ +int +im_png2vips_header( const char *name, IMAGE *out ) +{ + Read *read; + + if( !(read = read_new( name, out )) ) + return( -1 ); + + if( png2vips( read, 1 ) ) { + read_destroy( read ); + return( -1 ); + } + + read_destroy( read ); + + return( 0 ); +} + +/* Read a PNG file into a VIPS image. + */ +int +im_png2vips( const char *name, IMAGE *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "im_png2vips: reading \"%s\"\n", name ); +#endif /*DEBUG*/ + + if( !(read = read_new( name, out )) ) + return( -1 ); + + if( png2vips( read, 0 ) ) { + read_destroy( read ); + return( -1 ); + } + + read_destroy( read ); + + return( 0 ); +} + +#endif /*HAVE_PNG*/ diff --git a/libsrc/conversion/im_ppm2vips.c b/libsrc/conversion/im_ppm2vips.c new file mode 100644 index 00000000..2e188dc8 --- /dev/null +++ b/libsrc/conversion/im_ppm2vips.c @@ -0,0 +1,440 @@ +/* Read a ppm file. + * + * Stephen Chan ... original code + * + * 21/11/00 JC + * - hacked for VIPS + * - reads ppm/pgm/pbm + * - mmaps binary pgm/ppm + * - reads all ascii formats (slowly!) + * 22/11/00 JC + * - oops, ascii read was broken + * - does 16/32 bit ascii now as well + * 24/5/01 + * - im_ppm2vips_header() added + * 22/5/04 + * - does 16/32 bit binary too + * - tiny fix for missing file close on read error + * 19/8/05 + * - use im_raw2vips() for binary read + * 9/9/05 + * - tiny cleanups + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* The largest number/field/whatever we can read. + */ +#define IM_MAX_THING (80) + +static void +skip_line( FILE *fp ) +{ + int ch; + + while( (ch = fgetc( fp )) != '\n' ) + ; +} + +static void +skip_white_space( FILE *fp ) +{ + int ch; + + while( isspace( ch = fgetc( fp ) ) ) + ; + ungetc( ch, fp ); + + if( ch == '#' ) { + skip_line( fp ); + skip_white_space( fp ); + } +} + +static int +read_uint( FILE *fp ) +{ + int i; + char buf[IM_MAX_THING]; + int ch; + + skip_white_space( fp ); + + /* Stop complaints about used-before-set on ch. + */ + ch = -1; + + for( i = 0; i < IM_MAX_THING - 1 && isdigit( ch = fgetc( fp ) ); i++ ) + buf[i] = ch; + buf[i] = '\0'; + + if( i == 0 ) { + im_error( "im_ppm2vips", _( "bad unsigned int" ) ); + return( -1 ); + } + + ungetc( ch, fp ); + + return( atoi( buf ) ); +} + +static int +read_header( FILE *fp, IMAGE *out, int *bits, int *ascii ) +{ + int width, height, bands, fmt, type; + int i; + char buf[IM_MAX_THING]; + int max_value; + + /* ppm types. + */ + static char *magic_names[] = { + "P1", /* pbm ... 1 band 1 bit, ascii */ + "P2", /* pgm ... 1 band many bit, ascii */ + "P3", /* ppm ... 3 band many bit, ascii */ + "P4", /* pbm ... 1 band 1 bit, binary */ + "P5", /* pgm ... 1 band 8 bit, binary */ + "P6" /* ppm ... 3 band 8 bit, binary */ + }; + + /* Characteristics, indexed by ppm type. + */ + static int lookup_bits[] = { + 1, 8, 8, 1, 8, 8 + }; + static int lookup_bands[] = { + 1, 1, 3, 1, 1, 3 + }; + static int lookup_ascii[] = { + 1, 1, 1, 0, 0, 0 + }; + + /* Read in the magic number. + */ + buf[0] = fgetc( fp ); + buf[1] = fgetc( fp ); + buf[2] = '\0'; + + for( i = 0; i < IM_NUMBER( magic_names ); i++ ) + if( strcmp( magic_names[i], buf ) == 0 ) + break; + if( i == IM_NUMBER( magic_names ) ) { + im_error( "im_ppm2vips", _( "bad magic number" ) ); + return( -1 ); + } + *bits = lookup_bits[i]; + bands = lookup_bands[i]; + *ascii = lookup_ascii[i]; + + /* Read in size. + */ + if( (width = read_uint( fp )) < 0 || + (height = read_uint( fp )) < 0 ) + return( -1 ); + + /* Read in max value for >1 bit images. + */ + if( *bits > 1 ) { + if( (max_value = read_uint( fp )) < 0 ) + return( -1 ); + + if( max_value > 255 ) + *bits = 16; + if( max_value > 65535 ) + *bits = 32; + } + + /* For binary images, there is always exactly 1 more whitespace + * character before the data starts. + */ + if( !*ascii && !isspace( fgetc( fp ) ) ) { + im_error( "im_ppm2vips", + _( "not whitespace before start of binary data" ) ); + return( -1 ); + } + + /* Choose a VIPS bandfmt. + */ + switch( *bits ) { + case 1: + case 8: + fmt = IM_BANDFMT_UCHAR; + break; + + case 16: + fmt = IM_BANDFMT_USHORT; + break; + + case 32: + fmt = IM_BANDFMT_UINT; + break; + + default: + assert( 0 ); + } + + if( bands == 1 ) { + if( fmt == IM_BANDFMT_USHORT ) + type = IM_TYPE_GREY16; + else + type = IM_TYPE_B_W; + } + else { + if( fmt == IM_BANDFMT_USHORT ) + type = IM_TYPE_RGB16; + else if( fmt == IM_BANDFMT_UINT ) + type = IM_TYPE_RGB; + else + type = IM_TYPE_sRGB; + } + + im_initdesc( out, width, height, bands, (*bits == 1) ? 8 : *bits, fmt, + IM_CODING_NONE, + type, + 1.0, 1.0, + 0, 0 ); + + return( 0 ); +} + +/* Read a ppm/pgm file using mmap(). + */ +static int +read_mmap( FILE *fp, const char *filename, IMAGE *out ) +{ + const int header_offset = ftell( fp ); + IMAGE *t[2]; + im_arch_type arch = im_amiMSBfirst() ? + IM_ARCH_NATIVE : IM_ARCH_BYTE_SWAPPED; + + if( im_open_local_array( out, t, 2, "im_ppm2vips", "p" ) || + im_raw2vips( filename, t[0], + out->Xsize, out->Ysize, + IM_IMAGE_SIZEOF_PEL( out ), header_offset ) || + im_copy_morph( t[0], t[1], + out->Bands, out->BandFmt, out->Coding ) || + im_copy_from( t[1], out, arch ) ) + return( -1 ); + + return( 0 ); +} + +/* Read an ascii ppm/pgm file. + */ +static int +read_ascii( FILE *fp, IMAGE *out ) +{ + int x, y; + PEL *buf; + + if( im_outcheck( out ) || im_setupout( out ) || + !(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize * out->Bands; x++ ) { + int val; + + if( (val = read_uint( fp )) < 0 ) + return( -1 ); + + switch( out->BandFmt ) { + case IM_BANDFMT_UCHAR: + buf[x] = IM_CLIP( 0, val, 255 ); + break; + + case IM_BANDFMT_USHORT: + ((unsigned short *) buf)[x] = + IM_CLIP( 0, val, 65535 ); + break; + + case IM_BANDFMT_UINT: + ((unsigned int *) buf)[x] = val; + break; + + default: + assert( 0 ); + } + } + + if( im_writeline( y, out, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read an ascii 1 bit file. + */ +static int +read_1bit_ascii( FILE *fp, IMAGE *out ) +{ + int x, y; + PEL *buf; + + if( im_outcheck( out ) || im_setupout( out ) || + !(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize * out->Bands; x++ ) { + int val; + + if( (val = read_uint( fp )) < 0 ) + return( -1 ); + + if( val == 1 ) + buf[x] = 0; + else + buf[x] = 255; + } + + if( im_writeline( y, out, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read a 1 bit binary file. + */ +static int +read_1bit_binary( FILE *fp, IMAGE *out ) +{ + int x, y, i; + int bits; + PEL *buf; + + if( im_outcheck( out ) || im_setupout( out ) || + !(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + bits = fgetc( fp ); + for( i = 0, y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize * out->Bands; x++, i++ ) { + buf[x] = (bits & 128) ? 255 : 0; + bits <<= 1; + if( (i & 7) == 7 ) + bits = fgetc( fp ); + } + + if( im_writeline( y, out, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +parse_ppm( FILE *fp, const char *filename, IMAGE *out ) +{ + int bits; + int ascii; + + if( read_header( fp, out, &bits, &ascii ) ) + return( -1 ); + + /* What sort of read are we doing? + */ + if( !ascii && bits >= 8 ) + return( read_mmap( fp, filename, out ) ); + else if( !ascii && bits == 1 ) + return( read_1bit_binary( fp, out ) ); + else if( ascii && bits == 1 ) + return( read_1bit_ascii( fp, out ) ); + else + return( read_ascii( fp, out ) ); +} + +int +im_ppm2vips_header( const char *filename, IMAGE *out ) +{ + FILE *fp; + int bits; + int ascii; + +#ifdef BINARY_OPEN + if( !(fp = fopen( filename, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( filename, "r" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im_ppm2vips_header", + _( "unable to open \"%s\"" ), filename ); + return( -1 ); + } + + if( read_header( fp, out, &bits, &ascii ) ) { + fclose( fp ); + return( -1 ); + } + + fclose( fp ); + + return( 0 ); +} + +int +im_ppm2vips( const char *filename, IMAGE *out ) +{ + FILE *fp; + +#ifdef BINARY_OPEN + if( !(fp = fopen( filename, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( filename, "r" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im_ppm2vips", + _( "unable to open \"%s\"" ), filename ); + return( -1 ); + } + + if( parse_ppm( fp, filename, out ) ) { + fclose( fp ); + return( -1 ); + } + + fclose( fp ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_print.c b/libsrc/conversion/im_print.c new file mode 100644 index 00000000..563bf0ed --- /dev/null +++ b/libsrc/conversion/im_print.c @@ -0,0 +1,52 @@ +/* im_print(): print a string to stdout + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Print a string to stdout, with a "\n". Sometimes useful for debugging + * language bindings. + */ +int +im_print( const char *message ) +{ + printf( "%s\n", message ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_raw2vips.c b/libsrc/conversion/im_raw2vips.c new file mode 100644 index 00000000..e8f50605 --- /dev/null +++ b/libsrc/conversion/im_raw2vips.c @@ -0,0 +1,64 @@ +/* Read raw image. Just a wrapper over im_binfile(). + * + * 3/8/05 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_raw2vips( const char *filename, IMAGE *out, + int width, int height, int bpp, int offset ) +{ + IMAGE *t; + + if( !(t = im_binfile( filename, width, height, bpp, offset )) ) + return( -1 ); + if( im_add_close_callback( out, + (im_callback_fn) im_close, t, NULL ) ) { + im_close( t ); + return( -1 ); + } + if( im_copy( t, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_recomb.c b/libsrc/conversion/im_recomb.c new file mode 100644 index 00000000..009e1772 --- /dev/null +++ b/libsrc/conversion/im_recomb.c @@ -0,0 +1,167 @@ +/* @(#) Recombination of bands of image: perform a matrix mult of the form + * @(#) + * @(#) a1 b11 b21 .. bm1 c1 + * @(#) a2 b12 b22 .. c2 + * @(#) . = . . x . + * @(#) . . . + * @(#) + * @(#) an b1n bmn cm + * @(#) + * @(#) Where A is an n band output image, C is an m band input image and B + * @(#) is an mxn matrix of floats. Can be used with 3x3 matrix to perform + * @(#) simple colour space transforms; 7x30 matrix to shrink 3rd order + * @(#) development of 3 filter system to IM_TYPE_XYZ etc. + * @(#) + * @(#) Output is always float, unless input is double, in which case output + * @(#) is double. Does not work for complex images. + * @(#) + * @(#) Usage: + * @(#) im_recomb( imagein, imageout, mat ) + * @(#) IMAGE *imagein, *imageout; + * @(#) DOUBLEMASK *mat; + * @(#) + * @(#) Returns: -1 on error, else 0 + * 21/6/95 JC + * - mildly modernised + * 14/3/96 JC + * - better error checks, partial + * - proper rounding behaviour for int types + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Inner loop. + */ +#define LOOP(INTYPE, OUTTYPE) \ +{\ + INTYPE *p = (INTYPE *) bin;\ + OUTTYPE *q = (OUTTYPE *) bout;\ + \ + for( i = 0; i < width; i++ ) {\ + double *m = mat->coeff;\ + \ + for( v = 0; v < mat->ysize; v++ ) {\ + double t = 0.0;\ + \ + for( u = 0; u < mat->xsize; u++ )\ + t += *m++ * p[u];\ + \ + *q++ = (OUTTYPE) t;\ + }\ + \ + p += mat->xsize;\ + }\ +} + +/* Process a buffer of PELs. + */ +static int +recomb_buf( void *bin, void *bout, int width, IMAGE *in, DOUBLEMASK *mat ) +{ + int i; + int u, v; + + /* Do the processing. + */ + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOP( unsigned char, float ); break; + case IM_BANDFMT_CHAR: LOOP( signed char, float ); break; + case IM_BANDFMT_USHORT: LOOP( unsigned short, float ); break; + case IM_BANDFMT_SHORT: LOOP( signed short, float ); break; + case IM_BANDFMT_UINT: LOOP( unsigned int, float ); break; + case IM_BANDFMT_INT: LOOP( signed int, float ); break; + case IM_BANDFMT_FLOAT: LOOP( float, float ); break; + case IM_BANDFMT_DOUBLE: LOOP( double, double ); break; + + default: + im_errormsg( "im_recomb: unsupported input type" ); + return( -1 ); + } + + return( 0 ); +} + +/* Start here. + */ +int +im_recomb( IMAGE *in, IMAGE *out, DOUBLEMASK *mat ) +{ + DOUBLEMASK *mcpy; + + /* Check input image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_errormsg( "im_recomb: uncoded non-complex only" ); + return( -1 ); + } + if( in->Bands != mat->xsize ) { + im_errormsg( "im_recomb: bands in must equal matrix width" ); + return( -1 ); + } + + /* Prepare the output image + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = mat->ysize; + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + + /* Take a copy of the matrix. + */ + if( !(mcpy = im_dup_dmask( mat, "conv_mask" )) ) + return( -1 ); + if( im_add_close_callback( out, + (im_callback_fn) im_free_dmask, mcpy, NULL ) ) { + im_free_dmask( mcpy ); + return( -1 ); + } + + /* And process! + */ + if( im_wrapone( in, out, (im_wrapone_fn) recomb_buf, in, mcpy ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_replicate.c b/libsrc/conversion/im_replicate.c new file mode 100644 index 00000000..a3b69c3b --- /dev/null +++ b/libsrc/conversion/im_replicate.c @@ -0,0 +1,156 @@ +/* replicate an image x times horizontally and vertically + * + * JC, 30 sep 03 + * + * 15/4/04 + * - some optimisations for some cases + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG +#define DEBUG_PAINT +#define DEBUG_MAKE + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +replicate_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + Rect *r = &or->valid; + int twidth = in->Xsize; + int theight = in->Ysize; + int x, y; + Rect tile; + + /* Find top left of tiles we need. + */ + int xs = (r->left / twidth) * twidth; + int ys = (r->top / theight) * theight; + + /* The tile enclosing the top-left corner of the requested area. + */ + tile.left = xs; + tile.top = ys; + tile.width = twidth; + tile.height = theight; + + /* If the request fits inside a single tile, we can just pointer-copy. + */ + if( im_rect_includesrect( &tile, r ) ) { + Rect irect; + + /* Translate request to input space. + */ + irect = *r; + irect.left -= xs; + irect.top -= ys; + if( im_prepare( ir, &irect ) ) + return( -1 ); + + if( im_region_region( or, ir, r, irect.left, irect.top ) ) + return( -1 ); + + return( 0 ); + } + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += theight ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += twidth ) { + Rect paint; + + /* Whole tile at x, y + */ + tile.left = x; + tile.top = y; + tile.width = twidth; + tile.height = theight; + + /* Which parts touch the area of the output we are + * building. + */ + im_rect_intersectrect( &tile, r, &paint ); + + /* Translate back to ir coordinates. + */ + paint.left -= x; + paint.top -= y; + + assert( !im_rect_isempty( &paint ) ); + + /* Render into or. + */ + if( im_prepare_to( ir, or, &paint, + paint.left + x, + paint.top + y ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_replicate( IMAGE *in, IMAGE *out, int across, int down ) +{ + if( across <= 0 || down <= 0 ) { + im_errormsg( "im_replicate: bad parameters" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize *= across; + out->Ysize *= down; + + /* We can render small tiles with pointer copies. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + if( im_generate( out, + im_start_one, replicate_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/conversion/im_ri2c.c b/libsrc/conversion/im_ri2c.c new file mode 100644 index 00000000..3d5d2dfd --- /dev/null +++ b/libsrc/conversion/im_ri2c.c @@ -0,0 +1,170 @@ +/* @(#) Join two images to make a complex. If one of the inputs + * @(#) is a double, the output is IM_BANDFMT_DPCOMPLEX, otherwise it is IM_BANDFMT_COMPLEX. + * @(#) + * @(#) im_ri2c( IMAGE *in1, IMAGE *in2, IMAGE *out ) + * @(#) + * @(#) Returns: -1 on error, else 0 + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : 10/04/1990 + * 16/11/94 JC + * - rewritten with partials + * - more general + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Join two float buffers to make a complex. + */ +static void +join_float( float **p, float *q, int n, IMAGE *im ) +{ + int x; + int len = n * im->Bands; + float *p1 = p[0]; + float *p2 = p[1]; + + for( x = 0; x < len; x++ ) { + q[0] = *p1++; + q[1] = *p2++; + + q += 2; + } +} + +/* Join two double buffers to make a complex. + */ +static void +join_double( double **p, double *q, int n, IMAGE *im ) +{ + int x; + int len = n * im->Bands; + double *p1 = p[0]; + double *p2 = p[1]; + + for( x = 0; x < len; x++ ) { + q[0] = *p1++; + q[1] = *p2++; + + q += 2; + } +} + +/* Type conversion. + */ +static IMAGE * +convert( IMAGE *out, IMAGE *in, int (*cvt_fn)( IMAGE *, IMAGE * ) ) +{ + IMAGE *t1 = im_open_local( out, "Type conversion", "p" ); + + if( !t1 ) + return( NULL ); + + if( cvt_fn( in, t1 ) ) + return( NULL ); + + return( t1 ); +} + +int +im_ri2c( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + extern int im_clip2f( IMAGE *, IMAGE * ); + extern int im_clip2d( IMAGE *, IMAGE * ); + + /* Check input image. We don't need to check that sizes match -- + * im_wrapmany does this for us. + */ + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_errormsg( "im_ri2c: inputs should be uncoded" ); + return( -1 ); + } + if( im_iscomplex( in1 ) || im_iscomplex( in2 ) ) { + im_errormsg( "im_ri2c: inputs already complex" ); + return( -1 ); + } + + /* Prepare the output image. If either of the inputs is DOUBLE, we are + * DPCOMPLEX; otherwise we are COMPLEX. + */ + if( im_cp_descv( out, in1, in2, NULL ) ) + return( -1 ); + if( in1->BandFmt == IM_BANDFMT_DOUBLE || + in2->BandFmt == IM_BANDFMT_DOUBLE ) { + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + } + else { + out->Bbits = IM_BBITS_COMPLEX; + out->BandFmt = IM_BANDFMT_COMPLEX; + } + + /* Float inputs up to correct type. Note that if they are already the + * right type, this operation becomes a NOOP. + */ + if( out->BandFmt == IM_BANDFMT_COMPLEX ) { + in1 = convert( out, in1, im_clip2f ); + in2 = convert( out, in2, im_clip2f ); + } + else { + in1 = convert( out, in1, im_clip2d ); + in2 = convert( out, in2, im_clip2d ); + } + if( !in1 || !in2 ) + return( -1 ); + + /* Process! + */ + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( out->BandFmt == IM_BANDFMT_COMPLEX ) { + if( im_wrapmany( invec, out, + (im_wrapmany_fn) join_float, out, NULL ) ) + return( -1 ); + } + else { + if( im_wrapmany( invec, out, + (im_wrapmany_fn) join_double, out, NULL ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_rightshift_size.c b/libsrc/conversion/im_rightshift_size.c new file mode 100644 index 00000000..4ce46917 --- /dev/null +++ b/libsrc/conversion/im_rightshift_size.c @@ -0,0 +1,298 @@ +/* @(#) Decrease the size of an image by a power-of-two factor, summing the + * @(#) values of pixels, and shifting to give output of the specified band + * @(#) format. + * @(#) + * @(#) int + * @(#) im_rightshift_size( + * @(#) IMAGE *in, + * @(#) IMAGE *out, + * @(#) int xshift, + * @(#) int yshift, + * @(#) int band_fmt + * @(#) ); + * @(#) + * + * Copyright: 2006, Tom Vajzovic + * + * Author: Tom Vajzovic + * + * Written on: 2006-07-26 + * + * 2006-12-17 tcv: + * - rewrite generator function macros to produce many smaller functions + * - remove post-shift-up functionality + * 2007-02-02 jc + * - added return 0; on success + * - FUNCTION_NAME updated + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H */ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +/** LOCAL FUNCTION DECLARATIONS **/ + +/* function prototype macro */ +#define GEN_FUNC( SHIFT_MACRO, FROM_T, TO_T, SUM_T ) \ +static int gen_ ## SHIFT_MACRO ## _ ## FROM_T ## _to_ ## TO_T ## _with_ ## SUM_T( \ + REGION *to_make, REGION *make_from, void *unrequired, int *params ); + +/* macros to call function prototype macro for all possible combinations of types and macros */ +#define GEN_FUNCS_TO( SIGN, FROM_T, TO_T ) \ + GEN_FUNC( PRE_POST_SHIFT, FROM_T, TO_T, SIGN ## 64 ) \ + GEN_FUNC( POST_SHIFT, FROM_T, TO_T, SIGN ## 64 ) \ + GEN_FUNC( POST_SHIFT, FROM_T, TO_T, SIGN ## 32 ) \ + GEN_FUNC( NO_SHIFT, FROM_T, TO_T, SIGN ## 32 ) + +#define GEN_FUNCS_FROM( SIGN, FROM_T ) \ + GEN_FUNCS_TO( SIGN, FROM_T, SIGN ## 32 ) \ + GEN_FUNCS_TO( SIGN, FROM_T, SIGN ## 16 ) \ + GEN_FUNCS_TO( SIGN, FROM_T, SIGN ## 8 ) + +#define GEN_FUNCS_SIGN( SIGN ) \ + GEN_FUNCS_FROM( SIGN, SIGN ## 32 ) \ + GEN_FUNCS_FROM( SIGN, SIGN ## 16 ) \ + GEN_FUNCS_FROM( SIGN, SIGN ## 8 ) + +GEN_FUNCS_SIGN( gint ) +GEN_FUNCS_SIGN( guint ) + + +/** FUNCTION DEFINITIONS **/ + +int +im_rightshift_size( IMAGE *in, IMAGE *out, int xshift, int yshift, int band_fmt ){ +#define FUNCTION_NAME "im_rightshift_size" + int *params; + int needbits; + int outbits; + + if( im_piocheck( in, out ) ) + return -1; + + if( xshift < 0 || yshift < 0 ){ + im_error( FUNCTION_NAME, _( "bad arguments" ) ); + return -1; + } + if( ! xshift && ! yshift ){ + im_warn( FUNCTION_NAME, _( "shift by zero: falling back to im_copy" ) ); + return im_copy( in, out ); + } + if( ! in-> Xsize >> xshift || ! in-> Ysize >> yshift ){ + im_error( FUNCTION_NAME, _( "would result in zero size output image" ) ); + return -1; + } + if( ! im_isint( in ) ){ + im_error( FUNCTION_NAME, _( "integer type images only" ) ); + return -1; + } + if( IM_CODING_NONE != in->Coding ){ + im_error( FUNCTION_NAME, _( "uncoded images only" ) ); + return -1; + } + if( im_isuint( in ) ){ + if( IM_BANDFMT_UCHAR != band_fmt && IM_BANDFMT_USHORT != band_fmt && IM_BANDFMT_UINT != band_fmt ){ + im_error( FUNCTION_NAME, _( "unsigned input means that output must be unsigned int, short or char" ) ); + return -1; + } + } + else { + if( IM_BANDFMT_CHAR != band_fmt && IM_BANDFMT_SHORT != band_fmt && IM_BANDFMT_INT != band_fmt ){ + im_error( FUNCTION_NAME, _( "signed input means that output must be signed int, short or char" ) ); + return -1; + } + } + outbits= im_bits_of_fmt( band_fmt ); + + if( -1 == outbits ) + return -1; + + needbits= im_bits_of_fmt( in-> BandFmt ) + xshift + yshift; + + params= IM_ARRAY( out, 4, int ); + + if( ! params ) + return -1; + + params[ 0 ]= xshift; /* xshift */ + params[ 1 ]= yshift; /* yshift */ + + if( im_cp_desc( out, in ) ) + return -1; + + out-> Xsize >>= xshift; + out-> Ysize >>= yshift; + out-> Xres/= ( 1 << xshift ); /* x scale */ + out-> Yres/= ( 1 << yshift ); /* y scale */ + out-> BandFmt= band_fmt; + out-> Bbits= outbits; + + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return -1; + +#define RETURN_MACRO( MACRO, FROM_T, TO_T, SUM_T ) \ + return im_generate( out, im_start_one, gen_ ## MACRO ## _ ## FROM_T ## _to_ ## TO_T ## _with_ ## SUM_T, im_stop_one, in, params ); + +#define RETURN_MACRO_OUTS( MACRO, SUM_SIZE, OUT_SIZE ) switch( in-> BandFmt ){ \ + \ + case IM_BANDFMT_CHAR: \ + RETURN_MACRO( MACRO, gint8, gint ## OUT_SIZE, gint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_UCHAR: \ + RETURN_MACRO( MACRO, guint8, guint ## OUT_SIZE, guint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_SHORT: \ + RETURN_MACRO( MACRO, gint16, gint ## OUT_SIZE, gint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_USHORT: \ + RETURN_MACRO( MACRO, guint16, guint ## OUT_SIZE, guint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_INT: \ + RETURN_MACRO( MACRO, gint32, gint ## OUT_SIZE, gint ## SUM_SIZE ) \ + \ + case IM_BANDFMT_UINT: \ + RETURN_MACRO( MACRO, guint32, guint ## OUT_SIZE, guint ## SUM_SIZE ) \ +} + +#define RETURN_MACRO_SUMSS( MACRO, SUM_SIZE ) switch( out-> Bbits ){ \ + case 32: \ + RETURN_MACRO_OUTS( MACRO, SUM_SIZE, 32 ) \ + case 16: \ + RETURN_MACRO_OUTS( MACRO, SUM_SIZE, 16 ) \ + case 8: \ + RETURN_MACRO_OUTS( MACRO, SUM_SIZE, 8 ) \ +} + + if( needbits > 64 ){ + params[ 2 ]= needbits - 64; + params[ 3 ]= 64 - outbits; + RETURN_MACRO_SUMSS( PRE_POST_SHIFT, 64 ) + } + if( needbits > 32 ){ + params[ 3 ]= needbits - outbits; + RETURN_MACRO_SUMSS( POST_SHIFT, 64 ) + } + if( needbits > outbits ){ + params[ 3 ]= needbits - outbits; + RETURN_MACRO_SUMSS( POST_SHIFT, 32 ) + } + + RETURN_MACRO_SUMSS( NO_SHIFT, 32 ) + + return 0; + +#undef FUNCTION_NAME +} + +/** LOCAL FUNCTION DEFINITIONS **/ + +/* macros used in the function creating macro */ +#define PRE_POST_SHIFT() pixel+= from[ fx ] >> preshift; \ + to[ tx ]= pixel >> postshift; + +#define POST_SHIFT() pixel+= from[ fx ]; \ + to[ tx ]= pixel >> postshift; + +#define NO_SHIFT() pixel+= from[ fx ]; \ + to[ tx ]= pixel; + +/* function creating macro */ +#undef GEN_FUNC +#define GEN_FUNC( SHIFT_MACRO, FROM_T, TO_T, SUM_T ) \ +static int gen_ ## SHIFT_MACRO ## _ ## FROM_T ## _to_ ## TO_T ## _with_ ## SUM_T( \ + REGION *to_make, REGION *make_from, void *unrequired, int *params ){ \ + \ + int xshift= params[0]; \ + int yshift= params[1]; \ + int preshift= params[2]; \ + int postshift= params[3]; \ + Rect need= { \ + to_make-> valid. left << xshift, \ + to_make-> valid. top << yshift, \ + to_make-> valid. width << xshift, \ + to_make-> valid. height << yshift \ + }; \ + TO_T *to_start= (TO_T*) IM_REGION_ADDR( to_make, to_make-> valid. left, to_make-> valid. top ); \ + TO_T *to, *ty_stop; \ + SUM_T pixel; \ + FROM_T *from_start, *from_row, *from, *fy_stop; \ + \ + int band, tx, fx; \ + int tx_max= to_make-> valid. width * to_make-> im-> Bands; \ + int fx_max= to_make-> im-> Bands << xshift; \ + \ + size_t t_skip= IM_REGION_LSKIP( to_make ) / sizeof( TO_T ); \ + size_t f_skip; \ + \ + if( im_prepare( make_from, &need ) || ! im_rect_includesrect( &make_from-> valid, &need ) ) \ + return -1; \ + \ + from_start= (FROM_T*) IM_REGION_ADDR( make_from, need. left, need. top ); \ + f_skip= IM_REGION_LSKIP( make_from ) / sizeof( FROM_T ); \ + \ + for( band= 0; band < make_from-> im-> Bands; ++band ){ \ + \ + to= to_start + band; \ + ty_stop= to + t_skip * to_make-> valid. height; \ + \ + from_row= from_start + band; \ + \ + for( ; to < ty_stop; to+= t_skip, from_row+= f_skip << yshift ) \ + for( tx= 0; tx < tx_max; tx+= to_make-> im-> Bands ){ \ + \ + from= from_row + ( tx << xshift ); \ + fy_stop= from + ( f_skip << yshift ); \ + \ + pixel= 0; \ + \ + for( ; from < fy_stop; from+= f_skip ) \ + for( fx= 0; fx < fx_max; fx+= to_make-> im-> Bands ) \ + SHIFT_MACRO() \ + } \ + } \ + return 0; \ +} + +/* create functions for all possible combinations of types and macros */ +GEN_FUNCS_SIGN( gint ) +GEN_FUNCS_SIGN( guint ) diff --git a/libsrc/conversion/im_rot180.c b/libsrc/conversion/im_rot180.c new file mode 100644 index 00000000..2a13fd18 --- /dev/null +++ b/libsrc/conversion/im_rot180.c @@ -0,0 +1,158 @@ +/* @(#) rotates an image 180 degrees + * @(#) Usage: + * @(#) im_rot180(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1991, N. Dessipris + * Written on: 28/10/91 + * Updated on: 2/4/92, J.Cupitt + * bugs in im_la90rot fixed, now works for any type. + * 19/7/93 JC + * - IM_CODING_LABQ allowed now + * 15/11/94 JC + * - name changed + * - memory leaks fixed + * 8/2/95 JC + * - oops! memory allocation problem fixed + * 18/5/95 JC + * - IM_MAXLINES increased + * 13/8/96 JC + * - rewritten for partials + * - adapted from im_rot90 + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Rotate a small piece. + */ +static int +rot180_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + /* Output area. + */ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int x, y; + + /* Pixel geometry. + */ + int ps; + + /* Find the area of the input image we need. + */ + Rect need; + + need.left = in->Xsize - ri; + need.top = in->Ysize - bo; + need.width = r->width; + need.height = r->height; + if( im_prepare( ir, &need ) ) + return( -1 ); + + /* Find PEL size and line skip for ir. + */ + ps = IM_IMAGE_SIZEOF_PEL( in ); + + /* Rotate the bit we now have. + */ + for( y = to; y < bo; y++ ) { + /* Start of this output line. + */ + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Corresponding position in ir. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, + need.left + need.width - 1, + need.top + need.height - (y - to) - 1 ); + + /* Blap across! + */ + for( x = le; x < ri; x++ ) { + memcpy( q, p, ps ); + q += ps; + p -= ps; + } + } + + return( 0 ); +} + +int +im_rot180( IMAGE *in, IMAGE *out ) +{ + /* Make output image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_rot180", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* We are fastest with thinstrip. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, rot180_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = in->Xsize; + out->Yoffset = in->Ysize; + + return( 0 ); +} + diff --git a/libsrc/conversion/im_rot270.c b/libsrc/conversion/im_rot270.c new file mode 100644 index 00000000..5e5bdd10 --- /dev/null +++ b/libsrc/conversion/im_rot270.c @@ -0,0 +1,162 @@ +/* @(#) rotates an image 270 degrees + * @(#) Usage: + * @(#) im_rot270(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1991, N. Dessipris + * Written on: 28/10/91 + * Updated on: 2/4/92, J.Cupitt + * bugs in im_la90rot fixed, now works for any type. + * 19/7/93 JC + * - IM_CODING_LABQ allowed now + * 15/11/94 JC + * - name changed + * - memory leaks fixed + * 8/2/95 JC + * - oops! memory allocation problem fixed + * 18/5/95 JC + * - IM_MAXLINES increased + * 13/8/96 JC + * - rewritten for partials + * 6/11/02 JC + * - speed-up ... replace memcpy() with a loop for small pixels + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Rotate a small piece. + */ +static int +rot270_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + /* Output area. + */ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int x, y, i; + + /* Pixel geometry. + */ + int ps, ls; + + /* Find the area of the input image we need. + */ + Rect need; + + need.left = in->Xsize - bo; + need.top = le; + need.width = r->height; + need.height = r->width; + if( im_prepare( ir, &need ) ) + return( -1 ); + + /* Find PEL size and line skip for ir. + */ + ps = IM_IMAGE_SIZEOF_PEL( in ); + ls = IM_REGION_LSKIP( ir ); + + /* Rotate the bit we now have. + */ + for( y = to; y < bo; y++ ) { + /* Start of this output line. + */ + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Corresponding position in ir. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, + need.left + need.width - (y - to) - 1, + need.top ); + + for( x = le; x < ri; x++ ) { + for( i = 0; i < ps; i++ ) + q[i] = p[i]; + + q += ps; + p += ls; + } + } + + return( 0 ); +} + +int +im_rot270( IMAGE *in, IMAGE *out ) +{ + /* Make output image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_rot270", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Ysize; + out->Ysize = in->Xsize; + + /* We want smalltile if possible. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, rot270_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = in->Xsize; + + return( 0 ); +} + diff --git a/libsrc/conversion/im_rot90.c b/libsrc/conversion/im_rot90.c new file mode 100644 index 00000000..9ee815ff --- /dev/null +++ b/libsrc/conversion/im_rot90.c @@ -0,0 +1,162 @@ +/* @(#) rotates an image 90 degrees + * @(#) Usage: + * @(#) im_rot90(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * Copyright: 1991, N. Dessipris + * Written on: 28/10/91 + * Updated on: 2/4/92, J.Cupitt + * bugs in im_la90rot fixed, now works for any type. + * 19/7/93 JC + * - IM_CODING_LABQ allowed now + * 15/11/94 JC + * - name changed + * - memory leaks fixed + * 8/2/95 JC + * - oops! memory allocation problem fixed + * 18/5/95 JC + * - IM_MAXLINES increased + * 13/8/96 JC + * - rewritten for partials + * 6/11/02 JC + * - speed-up ... replace memcpy() with a loop for small pixels + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Rotate a small piece. + */ +static int +rot90_gen( REGION *or, REGION *ir, IMAGE *in ) +{ + /* Output area. + */ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int x, y, i; + + /* Pixel geometry. + */ + int ps, ls; + + /* Find the area of the input image we need. + */ + Rect need; + + need.left = to; + need.top = in->Ysize - ri; + need.width = r->height; + need.height = r->width; + if( im_prepare( ir, &need ) ) + return( -1 ); + + /* Find PEL size and line skip for ir. + */ + ps = IM_IMAGE_SIZEOF_PEL( in ); + ls = IM_REGION_LSKIP( ir ); + + /* Rotate the bit we now have. + */ + for( y = to; y < bo; y++ ) { + /* Start of this output line. + */ + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Corresponding position in ir. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, + need.left + y - to, + need.top + need.height - 1 ); + + for( x = le; x < ri; x++ ) { + for( i = 0; i < ps; i++ ) + q[i] = p[i]; + + q += ps; + p -= ls; + } + } + + return( 0 ); +} + +int +im_rot90( IMAGE *in, IMAGE *out ) +{ + /* Make output image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_rot90", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Ysize; + out->Ysize = in->Xsize; + + /* We want smalltile if possible. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, rot90_gen, im_stop_one, in, NULL ) ) + return( -1 ); + + out->Xoffset = in->Ysize; + out->Yoffset = 0; + + return( 0 ); +} + diff --git a/libsrc/conversion/im_scale.c b/libsrc/conversion/im_scale.c new file mode 100644 index 00000000..39427986 --- /dev/null +++ b/libsrc/conversion/im_scale.c @@ -0,0 +1,98 @@ +/* @(#) Scale an image so that min(im) maps to 0 and max(im) maps to 255. If + * @(#) min(im)==max(im), then we return black. Any non-complex type. + * @(#) + * @(#) int + * @(#) im_scale( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Author: John Cupitt + * Written on: 22/4/93 + * Modified on: + * 30/6/93 JC + * - adapted for partial v2 + * - ANSI + * 31/8/93 JC + * - calculation of offset now includes scale + * 8/5/06 + * - set Type on output too + * 16/10/06 + * - what? no, don't set Type, useful to be able to scale histograms, for + * example + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Scale, as noted above. + */ +int +im_scale( IMAGE *in, IMAGE *out ) +{ + DOUBLEMASK *stats; + IMAGE *t = im_open_local( out, "im_scale:1", "p" ); + double mx, mn; + double scale, offset; + + /* Measure min and max, calculate transform. + */ + if( !t || !(stats = im_stats( in )) ) + return( -1 ); + mn = stats->coeff[0]; + mx = stats->coeff[1]; + im_free_dmask( stats ); + + if( mn == mx ) + /* Range of zero: just return black. + */ + return( im_black( out, in->Xsize, in->Ysize, in->Bands ) ); + scale = 255.0 / (mx - mn); + offset = -(mn * scale); + + /* Transform! + */ + if( im_lintra( scale, in, offset, t ) || + im_clip( t, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_scaleps.c b/libsrc/conversion/im_scaleps.c new file mode 100644 index 00000000..5a0436d3 --- /dev/null +++ b/libsrc/conversion/im_scaleps.c @@ -0,0 +1,98 @@ +/* @(#) transform with log10(1.0 + pow(x, 0.25)) + .5, then scale so max + * @(#) == 255. + * @(#) + * @(#) int im_scaleps(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: 14/03/1991 + * 15/6/93 J.Cupitt + * - externs fixed + * - includes fixed + * 13/2/95 JC + * - ANSIfied + * - cleaned up + * 11/7/02 JC + * - rewritten ... got rid of the stuff for handling -ves, never used + * (and was broken anyway) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Scale, as noted above. + */ +int +im_scaleps( IMAGE *in, IMAGE *out ) +{ + IMAGE *t[4]; + double mx; + double scale; + + if( im_open_local_array( out, t, 4, "im_scaleps-1", "p" ) || + im_max( in, &mx ) ) + return( -1 ); + + if( mx <= 0.0 ) + /* Range of zero: just return black. + */ + return( im_black( out, in->Xsize, in->Ysize, in->Bands ) ); + + scale = 255.0 / log10( 1.0 + pow( mx, .25 ) ); + + /* Transform! + */ + if( im_powtra( in, t[0], 0.25 ) || + im_lintra( 1.0, t[0], 1.0, t[1] ) || + im_log10tra( t[1], t[2] ) || + im_lintra( scale, t[2], 0.0, t[3] ) || + im_clip( t[3], out ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/conversion/im_slice.c b/libsrc/conversion/im_slice.c new file mode 100644 index 00000000..2e1077aa --- /dev/null +++ b/libsrc/conversion/im_slice.c @@ -0,0 +1,157 @@ +/* @(#) Slices an image using two thresholds. Works for any non-complex type. + * @(#) Output has three levels 0 128 and 255. Values below or = t1 are 0, + * @(#) above t2 are 255 and the remaining are 128. + * @(#) Input is either memory mapped or in a buffer. + * @(#) It is implied that t1 is less than t2; however the program checks + * @(#) if they are the wrong way and swaps them + * @(#) + * @(#) int im_slice(in, out, t1, t2) + * @(#) IMAGE *in, *out; + * @(#) double t1, t2; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1991, N. Dessipris + * + * Author: N. Dessipris + * Written on: 15/03/1991 + * Modified on : + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define BRIGHT 255 +#define GREY 128 +#define DARK 0 + +/* Useful: Call a macro with the name, type pairs for all VIPS functions. + */ +#define im_for_all_types(A) \ + case IM_BANDFMT_UCHAR: A(unsigned char); break; \ + case IM_BANDFMT_CHAR: A(signed char); break; \ + case IM_BANDFMT_USHORT: A(unsigned short); break; \ + case IM_BANDFMT_SHORT: A(signed short); break; \ + case IM_BANDFMT_UINT: A(unsigned int); break; \ + case IM_BANDFMT_INT: A(signed int); break; \ + case IM_BANDFMT_FLOAT: A(float); break; + +/* Replacement for im_slice */ +int +im_slice( in, out, t1, t2 ) +IMAGE *in, *out; +double t1, t2; +{ + int x, y, z; + PEL *bu; /* Buffer we write to */ + int s, epl; /* Size and els per line */ + double thresh1, thresh2; + +/* Check our args. */ + if( im_iocheck( in, out ) ) + { + im_errormsg("im_slice: im_iocheck failed"); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) + { + im_errormsg("im_slice: input should be uncoded"); + return( -1 ); + } + +/* Set up the output header. */ + if( im_cp_desc( out, in ) ) + { + im_errormsg("im_slice: im_cp_desc failed"); + return( -1 ); + } + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + if( im_setupout( out ) ) + { + im_errormsg("im_slice: im_setupout failed"); + return( -1 ); + } + + if ( t1 <= t2 ) + { thresh1 = t1; thresh2 = t2; } + else + { thresh1 = t2; thresh2 = t1; } +/* Make buffer for building o/p in. */ + epl = in->Xsize * in->Bands; + s = epl * sizeof( PEL ); + if( (bu = (PEL *) im_malloc( out, (unsigned)s )) == NULL ) + return( -1 ); + +/* Define what we do for each band element type. */ +#define im_slice_loop(TYPE)\ + { TYPE *a = (TYPE *) in->data;\ + \ + for( y = 0; y < in->Ysize; y++ ) {\ + PEL *b = bu;\ + \ + for( x = 0; x < in->Xsize; x++ )\ + for( z = 0; z < in->Bands; z++ ) {\ + double f = (double) *a++;\ + if ( f <= thresh1)\ + *b++ = (PEL)DARK;\ + else if ( f > thresh2 )\ + *b++ = (PEL)BRIGHT;\ + else \ + *b++ = (PEL)GREY;\ + }\ + \ + if( im_writeline( y, out, bu ) )\ + return( -1 );\ + }\ + } + +/* Do the above for all image types. */ + switch( in->BandFmt ) { + im_for_all_types( im_slice_loop ); + + default: + im_errormsg("im_slice: Unknown input format"); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_subsample.c b/libsrc/conversion/im_subsample.c new file mode 100644 index 00000000..5b162a6c --- /dev/null +++ b/libsrc/conversion/im_subsample.c @@ -0,0 +1,241 @@ +/* @(#) Shrink any image by some integer xy factor. Sub-samples! Partial, and + * @(#) quick as a result. + * @(#) + * @(#) int + * @(#) im_subsample( in, out, xshrink, yshrink ) + * @(#) IMAGE *in, *out; + * @(#) int xshrink, yshrink; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * 3/7/95 JC + * - adapted from im_shrink() + * 3/8/02 JC + * - fall back to im_copy() for x/y factors == 1 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Maximum width of input we ask for. + */ +#define IM_MAX_WIDTH (1000) + +/* Our main parameter struct. + */ +typedef struct { + int xshrink; /* Subsample factors */ + int yshrink; +} SubsampleInfo; + +/* Subsample a REGION. We fetch in IM_MAX_WIDTH pixel-wide strips, left-to-right + * across the input. + */ +static int +line_shrink_gen( REGION *or, REGION *ir, IMAGE *in, SubsampleInfo *st ) +{ + Rect *r = &or->valid; + + int le = r->left; + int ri = IM_RECT_RIGHT( r ); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int ps = IM_IMAGE_SIZEOF_PEL( in ); + + int owidth = IM_MAX_WIDTH / st->xshrink; + + Rect s; + int x, y; + int z, b; + + /* Loop down the region. + */ + for( y = to; y < bo; y++ ) { + char *q = IM_REGION_ADDR( or, le, y ); + char *p; + + /* Loop across the region, in owidth sized pieces. + */ + for( x = le; x < ri; x += owidth ) { + /* How many pixels do we make this time? + */ + int ow = IM_MIN( owidth, ri - x ); + + /* Ask for this many from input ... can save a + * little here! + */ + int iw = ow * st->xshrink - (st->xshrink - 1); + + /* Ask for input. + */ + s.left = x * st->xshrink; + s.top = y * st->yshrink; + s.width = iw; + s.height = 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Append new pels to output. + */ + p = IM_REGION_ADDR( ir, s.left, s.top ); + for( z = 0; z < ow; z++ ) { + for( b = 0; b < ps; b++ ) + q[b] = p[b]; + + q += ps; + p += ps * st->xshrink; + } + } + } + + return( 0 ); +} + +/* Fetch one pixel at a time ... good for very large shrinks, or for SMALLTILE + * pipes. + */ +static int +point_shrink_gen( REGION *or, REGION *ir, IMAGE *in, SubsampleInfo *st ) +{ + Rect *r = &or->valid; + + int le = r->left; + int ri = IM_RECT_RIGHT( r ); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int ps = IM_IMAGE_SIZEOF_PEL( in ); + + Rect s; + int x, y; + int b; + + /* Loop down the region. + */ + for( y = to; y < bo; y++ ) { + char *q = IM_REGION_ADDR( or, le, y ); + char *p; + + /* Loop across the region, in owidth sized pieces. + */ + for( x = le; x < ri; x++ ) { + /* Ask for input. + */ + s.left = x * st->xshrink; + s.top = y * st->yshrink; + s.width = 1; + s.height = 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Append new pels to output. + */ + p = IM_REGION_ADDR( ir, s.left, s.top ); + for( b = 0; b < ps; b++ ) + q[b] = p[b]; + q += ps; + } + } + + return( 0 ); +} + +int +im_subsample( IMAGE *in, IMAGE *out, int xshrink, int yshrink ) +{ + SubsampleInfo *st; + + /* Check parameters. + */ + if( xshrink < 1 || yshrink < 1 ) { + im_errormsg( "im_subsample: factors should both be >= 1" ); + return( -1 ); + } + if( xshrink == 1 && yshrink == 1 ) + return( im_copy( in, out ) ); + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Prepare output. Note: we round the output width down! + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Xsize / xshrink; + out->Ysize = in->Ysize / yshrink; + out->Xres = in->Xres / xshrink; + out->Yres = in->Yres / yshrink; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_subsample: image has shrunk to nothing" ); + return( -1 ); + } + + /* Build and attach state struct. + */ + if( !(st = IM_NEW( out, SubsampleInfo )) ) + return( -1 ); + st->xshrink = xshrink; + st->yshrink = yshrink; + + /* Set demand hints. We want THINSTRIP, as we will be demanding a + * large area of input for each output line. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! If this is a very large shrink, then it's + * probably faster to do it a pixel at a time. If this is SMALLTILE, + * then it will hate long lines and we should always do 1 pixel at a + * time. + */ + if( in->dhint == IM_SMALLTILE || xshrink > 10 ) { + if( im_generate( out, + im_start_one, point_shrink_gen, im_stop_one, in, st ) ) + return( -1 ); + } + else { + if( im_generate( out, + im_start_one, line_shrink_gen, im_stop_one, in, st ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_system.c b/libsrc/conversion/im_system.c new file mode 100644 index 00000000..419379bb --- /dev/null +++ b/libsrc/conversion/im_system.c @@ -0,0 +1,226 @@ +/* im_system(): run a command on an image + * + * 7/3/00 JC + * - hacked it in + * 21/10/02 JC + * - use mktemp() if mkstemp() is not available + * 10/3/03 JC + * - out can be NULL + * 23/12/04 + * - use g_mkstemp() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define IM_MAX_STRSIZE (4096) + +#ifdef OS_WIN32 +#define popen(b,m) _popen(b,m) +#define pclose(f) _pclose(f) +#define mktemp(f) _mktemp(f) +#endif /*OS_WIN32*/ + +/* A string being written to ... multiple calls to buf_append add to it, on + * overflow append "..." and block further writes. + */ +typedef struct { + char *base; /* String base */ + int mx; /* Maximum length */ + int i; /* Current write point */ + int full; /* String has filled, block writes */ + int lasti; /* For read-recent */ +} BufInfo; + +/* Set to start state. + */ +static void +buf_rewind( BufInfo *buf ) +{ + buf->i = 0; + buf->lasti = 0; + buf->full = 0; + + strcpy( buf->base, "" ); +} + +/* Init a buf struct. + */ +static void +buf_init( BufInfo *buf, char *base, int mx ) +{ + buf->base = base; + buf->mx = mx; + buf_rewind( buf ); +} + +/* Append string to buf. Error on overflow. + */ +static int +buf_appends( BufInfo *buf, const char *str ) +{ + int len; + int avail; + int cpy; + + if( buf->full ) + return( 0 ); + + /* Amount we want to copy. + */ + len = strlen( str ); + + /* Space available. + */ + avail = buf->mx - buf->i - 4; + + /* Amount we actually copy. + */ + cpy = IM_MIN( len, avail ); + + strncpy( buf->base + buf->i, str, cpy ); + buf->i += cpy; + + if( buf->i >= buf->mx - 4 ) { + buf->full = 1; + strcpy( buf->base + buf->mx - 4, "..." ); + buf->i = buf->mx - 1; + return( 0 ); + } + + return( 1 ); +} + +/* Read all text from buffer. + */ +static char * +buf_all( BufInfo *buf ) +{ + buf->base[buf->i] = '\0'; + return( buf->base ); +} + +/* Do popen(), with printf-style args. + */ +static FILE * +popenf( const char *fmt, const char *mode, ... ) +{ + va_list args; + char buf[IM_MAX_STRSIZE]; + + va_start( args, mode ); + (void) im_vsnprintf( buf, IM_MAX_STRSIZE, fmt, args ); + va_end( args ); + + return( popen( buf, mode ) ); +} + +/* Run a command on an IMAGE ... copy to tmp (if necessary), run + * command on it, unlink (if we copied), return stdout from command. + */ +int +im_system( IMAGE *im, const char *cmd, char **out ) +{ + char *filename = im->filename; + int delete = 0; + FILE *fp; + + if( !im_isfile( im ) ) { + const char *tmpd; + char name[IM_MAX_STRSIZE]; + IMAGE *disc; + + if( !(tmpd = g_getenv( "TMPDIR" )) ) + tmpd = "/tmp"; + strcpy( name, tmpd ); + strcat( name, "/vips_XXXXXX" ); + + close( g_mkstemp( name ) ); + filename = im_strdup( NULL, name ); + + if( !(disc = im_open( filename, "w" )) ) { + unlink( filename ); + free( filename ); + return( -1 ); + } + if( im_copy( im, disc ) ) { + im_close( disc ); + unlink( filename ); + free( filename ); + return( -1 ); + } + im_close( disc ); + delete = 1; + } + + if( (fp = popenf( cmd, "r", filename )) ) { + char line[IM_MAX_STRSIZE]; + BufInfo buf; + char txt_buffer[IM_MAX_STRSIZE]; + + buf_init( &buf, txt_buffer, IM_MAX_STRSIZE ); + while( fgets( line, IM_MAX_STRSIZE, fp ) ) + if( !buf_appends( &buf, line ) ) + break; + pclose( fp ); + + if( out ) + *out = im_strdup( NULL, buf_all( &buf ) ); + } + + if( delete ) { + unlink( filename ); + im_free( filename ); + } + + if( !fp ) { + im_errormsg( "popen: %s", strerror( errno ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_tbjoin.c b/libsrc/conversion/im_tbjoin.c new file mode 100644 index 00000000..52831b8b --- /dev/null +++ b/libsrc/conversion/im_tbjoin.c @@ -0,0 +1,89 @@ +/* @(#) To join two images top bottom. The resultant image has + * @(#) Ysize = im1.Ysize + im2.Ysize and Xsize the min of im1.Xsize, im2.Xsize + * @(#) Input images should have the same number of Bands and BandFmt + * @(#) + * @(#) Usage: + * @(#) int im_tbjoin(top, bottom, out) + * @(#) IMAGE *top, *bottom, *out; + * @(#) + * @(#) + * + * Copyright: 1990, 1991 Kirk Martinez, N. Dessipris + * Author: Kirk Martinez, N. Dessipris + * Written on: 9/6/90 + * Updated: 15/03/1991, N. Dessipris + * 31/8/93 JC + * - externs removed + * 14/11/94 JC + * - tidied up + * - now works for IM_CODING_LABQ too + * - image compatibility bug fixed + * 28/4/95 JC + * - y arg to 2nd set of im_writeline()s was wrong + * 23/10/95 JC + * - rewritten in terms of im_insert() + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_tbjoin( IMAGE *top, IMAGE *bottom, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_tbjoin:1", "p" ); + + /* Paste top and bottom together. + */ + if( !t1 || im_insert( top, bottom, t1, 0, top->Ysize ) ) + return( -1 ); + + /* Extract the part which the old im_tbjoin() would have made. + */ + if( im_extract_area( t1, out, + 0, 0, IM_MIN( top->Xsize, bottom->Xsize ), t1->Ysize ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = top->Ysize; + + return( 0 ); +} diff --git a/libsrc/conversion/im_text.c b/libsrc/conversion/im_text.c new file mode 100644 index 00000000..37427e41 --- /dev/null +++ b/libsrc/conversion/im_text.c @@ -0,0 +1,237 @@ +/* @(#) Make an image containting text as a bitmap. One band uchar output, + * @(#) with 0 - 255 for black - white. + * @(#) + * @(#) int + * @(#) im_text( IMAGE *out, const char *text, const char *font, + * @(#) int width, int alignment, int dpi ) + * @(#) + * @(#) Font is a Pango font specification, eg. "Sans 12", or "Times News + * @(#) Roman Italic 12". text should be coded as utf-8. dpi is resolution + * @(#) in dots per inch. alignment is 0, 1 and 2 for right, centre and left + * @(#) alignment. width is the wrap width in pixels. + * @(#) + * @(#) Returns 0 on success and -1 on error. + * + * Written on: 20/5/04 + * 29/7/04 + * - !HAVE_PANGOFT2 was broken, thanks Kenneth + * 15/11/04 + * - gah, still broken, thanks Stefan + * 5/4/06 + * - return an error for im_text( "" ) rather than trying to make an + * empty image + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef HAVE_PANGOFT2 +#include +#include +#endif /*HAVE_PANGOFT2*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_PANGOFT2 + +static PangoLayout * +text_layout_new( PangoContext *context, + const char *text, const char *font, int width, int alignment, int dpi ) +{ + PangoLayout *layout; + PangoFontDescription *font_description; + + layout = pango_layout_new( context ); + pango_layout_set_markup( layout, text, -1 ); + + font_description = pango_font_description_from_string( font ); + pango_layout_set_font_description( layout, font_description ); + pango_font_description_free( font_description ); + + if( width > 0 ) + pango_layout_set_width( layout, width * PANGO_SCALE ); + + if( alignment < 0 || alignment > 2 ) + alignment = PANGO_ALIGN_RIGHT; + pango_layout_set_alignment( layout, (PangoAlignment) alignment ); + + return( layout ); +} + +static int +text_ft_to_vips( FT_Bitmap *bitmap, IMAGE *out ) +{ + int y; + + if( im_outcheck( out ) ) + return( -1 ); + im_initdesc( out, bitmap->width, bitmap->rows, 1, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + for( y = 0; y < bitmap->rows; y++ ) + if( im_writeline( y, out, + (PEL *) bitmap->buffer + y * bitmap->pitch ) ) + return( -1 ); + + return( 0 ); +} + +static int +text_layout_render_to_image( PangoLayout *layout, IMAGE *out ) +{ + PangoRectangle logical_rect; + FT_Bitmap bitmap; + int left; + int top; + int width; + int height; + + pango_layout_get_extents( layout, NULL, &logical_rect ); + +#ifdef DEBUG + printf( "logical left = %d, top = %d, width = %d, height = %d\n", + PANGO_PIXELS( logical_rect.x ), + PANGO_PIXELS( logical_rect.y ), + PANGO_PIXELS( logical_rect.width ), + PANGO_PIXELS( logical_rect.height ) ); +#endif /*DEBUG*/ + + left = PANGO_PIXELS( logical_rect.x ); + top = PANGO_PIXELS( logical_rect.y ); + width = PANGO_PIXELS( logical_rect.width ); + height = PANGO_PIXELS( logical_rect.height ); + + /* Can happen for "", for example. + */ + if( width == 0 || height == 0 ) { + im_error( "im_text", _( "no text to render" ) ); + return( -1 ); + } + + bitmap.width = width; + bitmap.pitch = (bitmap.width + 3) & ~3; + bitmap.rows = height; + if( !(bitmap.buffer = im_malloc( NULL, bitmap.pitch * bitmap.rows )) ) + return( -1 ); + bitmap.num_grays = 256; + bitmap.pixel_mode = ft_pixel_mode_grays; + memset( bitmap.buffer, 0x00, bitmap.pitch * bitmap.rows ); + + if( pango_layout_get_width( layout ) != -1 ) + pango_ft2_render_layout( &bitmap, layout, -left, -top ); + else + pango_ft2_render_layout( &bitmap, layout, 0, 0 ); + if( text_ft_to_vips( &bitmap, out ) ) { + im_free( bitmap.buffer ); + return( -1 ); + } + + im_free( bitmap.buffer ); + + return( 0 ); +} + +static int +text_render_to_image( PangoContext *context, IMAGE *out, + const char *text, const char *font, int width, int alignment, int dpi ) +{ + PangoLayout *layout; + + if( !(layout = text_layout_new( context, text, font, + width, alignment, dpi )) ) + return( -1 ); + + if( text_layout_render_to_image( layout, out ) ) { + g_object_unref( layout ); + return( -1 ); + } + + g_object_unref( layout ); + + return( 0 ); +} + +int +im_text( IMAGE *out, const char *text, const char *font, + int width, int alignment, int dpi ) +{ + static PangoFontMap *fontmap = NULL; + PangoContext *context; + + if( !pango_parse_markup( text, -1, 0, NULL, NULL, NULL, NULL ) ) { + im_error( "im_text", _( "invalid markup in text" ) ); + return( -1 ); + } + + /* Just have one of these, ever. It doesn't close properly when we + * _unref(), so keep it around for reuse. + */ + if( !fontmap ) + fontmap = pango_ft2_font_map_new(); + + pango_ft2_font_map_set_resolution( PANGO_FT2_FONT_MAP( fontmap ), + dpi, dpi ); + context = pango_ft2_font_map_create_context( + PANGO_FT2_FONT_MAP( fontmap ) ); + + if( text_render_to_image( context, out, text, font, + width, alignment, dpi ) ) { + g_object_unref( context ); + return( -1 ); + } + + g_object_unref( context ); + + return( 0 ); +} + +#else /*!HAVE_PANGOFT2*/ + +int +im_text( IMAGE *out, const char *text, const char *font, + int width, int alignment, int dpi ) +{ + im_error( "im_text", _( "pangoft2 support disabled" ) ); + + return( -1 ); +} + +#endif /*HAVE_PANGOFT2*/ diff --git a/libsrc/conversion/im_thresh.c b/libsrc/conversion/im_thresh.c new file mode 100644 index 00000000..3662fece --- /dev/null +++ b/libsrc/conversion/im_thresh.c @@ -0,0 +1,134 @@ +/* @(#) Thresholds an image. Works for any non-complex type. + * @(#) Output is a binary image with 0 and 255 only + * @(#) Input is either memory mapped or in a buffer. + * @(#) + * @(#) int im_thresh(imin, imout, threshold) + * @(#) IMAGE *imin, *imout; + * @(#) double threshold; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1991, N. Dessipris, J Cupitt + * + * Author: N. Dessipris, J. Cupitt + * Written on: 15/03/1991 + * Modified on : + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Useful: Call a macro with the name, type pairs for all VIPS functions. */ +#define BRIGHT 255 +#define DARK 0 +#define im_for_all_types(A) \ + case IM_BANDFMT_UCHAR: A(unsigned char); break; \ + case IM_BANDFMT_CHAR: A(signed char); break; \ + case IM_BANDFMT_USHORT: A(unsigned short); break; \ + case IM_BANDFMT_SHORT: A(signed short); break; \ + case IM_BANDFMT_UINT: A(unsigned int); break; \ + case IM_BANDFMT_INT: A(signed int); break; \ + case IM_BANDFMT_FLOAT: A(float); break; \ + case IM_BANDFMT_DOUBLE: A(double); break; + +/* Replacement for im_thresh */ +int +im_thresh( in, out, threshold ) +IMAGE *in, *out; +double threshold; +{ + int x, y; + PEL *bu; /* Buffer we write to */ + int s, epl; /* Size and els per line */ + +/* Check our args. */ + if( im_iocheck( in, out ) ) + { im_errormsg("im_thresh: im_iocheck failed"); return( -1 ); } + if( in->Coding != IM_CODING_NONE ) + { im_errormsg("im_thresh: input should be uncoded");return(-1);} + +/* Set up the output header. */ + if( im_cp_desc( out, in ) ) + { im_errormsg("im_thresh: im_cp_desc failed"); return( -1 ); } + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + if( im_setupout( out ) ) + { + im_errormsg("im_thresh: im_setupout failed"); + return( -1 ); + } + +/* Make buffer for building o/p in. */ + epl = in->Xsize * in->Bands; + s = epl * sizeof( PEL ); + if( (bu = (PEL *) im_malloc( out, (unsigned)s )) == NULL ) + return( -1 ); + +/* Define what we do for each band element type. */ +#define im_thresh_loop(TYPE)\ + { TYPE *a = (TYPE *) in->data;\ + \ + for( y = 0; y < in->Ysize; y++ ) {\ + PEL *b = bu;\ + \ + for( x = 0; x < epl; x++ ) {\ + double f = (double) *a++;\ + if ( f >= threshold)\ + *b++ = (PEL)BRIGHT;\ + else\ + *b++ = (PEL)DARK;\ + }\ + \ + if( im_writeline( y, out, bu ) ) \ + return( -1 );\ + }\ + } + +/* Do the above for all image types. */ + switch( in->BandFmt ) { + im_for_all_types( im_thresh_loop ); + + default: + im_errormsg("im_thresh: Unknown input format"); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/conversion/im_tiff2vips.c b/libsrc/conversion/im_tiff2vips.c new file mode 100644 index 00000000..0a62d019 --- /dev/null +++ b/libsrc/conversion/im_tiff2vips.c @@ -0,0 +1,1490 @@ +/* TIFF parts: Copyright (c) 1988, 1990 by Sam Leffler. + * All rights reserved. + * + * This file is provided for unrestricted use provided that this + * legend is included on all tape media and as a part of the + * software program in whole or part. Users may copy, modify or + * distribute this file at will. + * ----------------------------- + * Modifications for VIPS: Kirk Martinez 1994 + * 22/11/94 JC + * - more general + * - memory leaks fixed + * 20/3/95 JC + * - TIFF error handler added + * - read errors detected correctly + * + * Modified to handle LAB in tiff format. + * It convert LAB-tiff format to IM_TYPE_LABQ in vips format. + * Copyright July-1995 Ahmed Abbood. + * + * + * 19/9/95 JC + * - now calls TIFFClose ... stupid + * 25/1/96 JC + * - typo on MINISBLACK ... + * 7/4/97 JC + * - completely redone for TIFF 6 + * - now full baseline TIFF 6 reader, and does CIELAB as well + * 11/4/97 JC + * - added partial read for tiled images + * 23/4/97 JC + * - extra subsample parameter + * - im_istiffpyramid() added + * 5/12/97 JC + * - if loading YCbCr, convert to IM_CODING_LABQ + * 1/5/98 JC + * - now reads 16-bit greyscale and RGB + * 26/10/98 JC + * - now used "rb" mode on systems that need binary open + * 12/11/98 JC + * - no sub-sampling if sub == 1 + * 26/2/99 JC + * - ooops, else missing for subsample stuff above + * 2/10/99 JC + * - tiled 16-bit greyscale read was broken + * - added mutex for TIFF*() calls + * 11/5/00 JC + * - removed TIFFmalloc/TIFFfree usage + * 23/4/01 JC + * - HAVE_TIFF turns on TIFF goodness + * 24/5/01 JC + * - im_tiff2vips_header() added + * 11/7/01 JC + * - subsample now in input filename + * - ... and it's a page number (from 0) instead + * 21/8/02 JC + * - now reads CMYK + * - hmm, dpi -> ppm conversion was wrong! + * 10/9/02 JC + * - oops, handle TIFF errors better + * 2/12/02 JC + * - reads 8-bit RGBA + * 12/12/02 JC + * - reads 16-bit LAB + * 13/2/03 JC + * - pixels/cm res read was wrong + * 17/11/03 Andrey Kiselev + * - read 32-bit float greyscale and rgb + * 5/4/04 + * - better handling of edge tiles (thanks Ruven) + * 16/4/04 + * - cleanup + * - added broken tile read mode + * 18/5/04 Andrey Kiselev + * - better no resolution diagnostic + * 26/5/04 + * - reads 16 bit RGBA + * 28/7/04 + * - arrg, 16bit RGB was broken, thanks haida + * 26/11/04 + * - add a TIFF warning handler, stops occasional libMagick exceptions + * 9/3/05 + * - load 32-bit float LAB + * 8/4/05 + * - onebit read no longer reads one byte too many on multiple of 8 wide + * images + * 22/6/05 + * - 16 bit LAB read was broken + * 9/9/05 + * - read any ICCPROFILE tag + * 8/5/06 + * - set RGB16 and GREY16 Type + * 21/5/06 + * - use external im_tile_cache() operation for great code shrinkage + * - less RAM usage too, esp. with >1 CPU + * - should be slightly faster + * - removed 'broken' read option + * 18/7/07 Andrey Kiselev + * - remove "b" option on TIFFOpen() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_TIFF + +#include + +int +im_tiff2vips( const char *tiffile, IMAGE *im ) +{ + im_error( "im_tiff2vips", _( "TIFF support disabled" ) ); + return( -1 ); +} + +int +im_istiffpyramid( const char *name ) +{ + return( 0 ); +} + +int +im_tiff2vips_header( const char *tiffile, IMAGE *im ) +{ + im_error( "im_tiff2vips", _( "TIFF support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_TIFF*/ + +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Scanline-type process function. + */ +typedef void (*scanline_process_fn)( PEL *q, PEL *p, int n, void *user ); + +/* Stuff we track during a read. + */ +typedef struct { + /* Parameters. + */ + char *filename; + IMAGE *out; + + /* From filename. + */ + int page; + + /* The TIFF we read. + */ + TIFF *tiff; + + /* Process for this image type. + */ + scanline_process_fn sfn; + void *client; + + /* Geometry. + */ + int twidth, theight; /* Tile size */ + + /* Only need one of these, since we mutex around TIFF*(). + */ + GMutex *tlock; /* Lock for TIFF*() calls */ +} ReadTiff; + +/* Reading a YCbCr image ... parameters we use for conversion. + */ +typedef struct { + /* Input and output. + */ + TIFF *tif; /* From here */ + IMAGE *im; /* To here */ + + /* RGB <-> YCbCr conversion. + */ + float LumaRed, LumaGreen, LumaBlue; + + /* RGB -> LAB conversion. + */ + void *table; +} YCbCrParams; + +/* Handle TIFF errors here. + */ +static void +thandler_error( char *module, char *fmt, va_list ap ) +{ + im_verror( module, fmt, ap ); +} + +static void +thandler_warning( char *module, char *fmt, va_list ap ) +{ + char buf[256]; + + im_vsnprintf( buf, 256, fmt, ap ); + im_warn( module, "%s", buf ); +} + +/* Test for field exists. + */ +static int +tfexists( TIFF *tif, ttag_t tag ) +{ + uint32 a, b; + + if( TIFFGetField( tif, tag, &a, &b ) ) + return( 1 ); + else + return( 0 ); +} + +/* Test a uint16 field. Field must be defined and equal to the value. + */ +static int +tfequals( TIFF *tif, ttag_t tag, uint16 val ) +{ + uint16 fld; + + if( !TIFFGetFieldDefaulted( tif, tag, &fld ) ) { + im_error( "im_tiff2vips", + _( "required field %d missing" ), tag ); + return( 0 ); + } + if( fld != val ) { + im_error( "im_tiff2vips", _( "required field %d=%d, not %d" ), + tag, fld, val ); + return( 0 ); + } + + /* All ok. + */ + return( 1 ); +} + +/* Get a uint32 field. + */ +static int +tfget32( TIFF *tif, ttag_t tag, int *out ) +{ + uint32 fld; + + if( !TIFFGetFieldDefaulted( tif, tag, &fld ) ) { + im_error( "im_tiff2vips", + _( "required field %d missing" ), tag ); + return( 0 ); + } + + /* All ok. + */ + *out = fld; + return( 1 ); +} + +/* Get a uint16 field. + */ +static int +tfget16( TIFF *tif, ttag_t tag, int *out ) +{ + uint16 fld; + + if( !TIFFGetFieldDefaulted( tif, tag, &fld ) ) { + im_error( "im_tiff2vips", + _( "required field %d missing" ), tag ); + return( 0 ); + } + + /* All ok. + */ + *out = fld; + return( 1 ); +} + +/* Per-scanline process function for IM_CODING_LABQ. + */ +static void +labpack_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + + for( x = 0; x < n; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + q[3] = 0; + + q += 4; + p += 3; + } +} + +/* Read an 8-bit LAB image. + */ +static int +parse_labpack( ReadTiff *rtiff, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 3 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) ) + return( -1 ); + + out->Bands = 4; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_LABQ; + out->Type = IM_TYPE_LAB; + + rtiff->sfn = labpack_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for IM_CODING_LABQ. + */ +static void +labs_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + unsigned short *p1 = (unsigned short *) p; + short *q1 = (short *) q; + + for( x = 0; x < n; x++ ) { + q1[0] = p1[0] >> 1; + q1[1] = p1[1]; + q1[2] = p1[2]; + + q1 += 3; + p1 += 3; + } +} + +/* Read a 16-bit LAB image. + */ +static int +parse_labs( ReadTiff *rtiff, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 3 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 16 ) ) + return( -1 ); + + out->Bands = 3; + out->Bbits = 16; + out->BandFmt = IM_BANDFMT_SHORT; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_LABS; + + rtiff->sfn = labs_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for IM_CODING_LABQ. + */ +static void +ycbcr_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + + for( x = 0; x < n; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + q[3] = 0; + + q += 4; + p += 3; + } +} + +/* Read a YCbCr image. + */ +static int +parse_ycbcr( ReadTiff *rtiff, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 3 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) ) + return( -1 ); + + out->Bands = 4; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_LABQ; + out->Type = IM_TYPE_LAB; + + rtiff->sfn = ycbcr_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for 1 bit images. + */ +static void +onebit_line( PEL *q, PEL *p, int n, void *flg ) +{ + /* Extract PHOTOMETRIC_INTERPRETATION. + */ + int pm = *((int *) flg); + int x, i, z; + PEL bits; + + int black = (pm == PHOTOMETRIC_MINISBLACK) ? 0 : 255; + int white = black ^ -1; + + /* (sigh) how many times have I written this? + */ + for( x = 0, i = 0; i < (n >> 3); i++ ) { + bits = (PEL) p[i]; + + for( z = 0; z < 8; z++, x++ ) { + q[x] = (bits & 128) ? white : black; + bits <<= 1; + } + } + + /* Do last byte in line. + */ + if( n & 7 ) { + bits = p[i]; + for( z = 0; z < (n & 7); z++ ) { + q[x + z] = (bits & 128) ? white : black; + bits <<= 1; + } + } +} + +/* Read a 1-bit TIFF image. Pass in pixel values to use for black and white. + */ +static int +parse_onebit( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + int *ppm; + + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 1 ) ) + return( -1 ); + + out->Bands = 1; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_B_W; + + /* Note pm for later. + */ + if( !(ppm = IM_ARRAY( out, 1, int )) ) + return( -1 ); + *ppm = pm; + + rtiff->sfn = onebit_line; + rtiff->client = ppm; + + return( 0 ); +} + +/* Per-scanline process function for 8-bit greyscale images. + */ +static void +greyscale8_line( PEL *q, PEL *p, int n, void *flg ) +{ + /* Extract swap mask. + */ + PEL mask = *((PEL *) flg); + int x; + + /* Read bytes, swapping sense if necessary. + */ + for( x = 0; x < n; x++ ) + q[x] = p[x] ^ mask; +} + +/* Read a 8-bit grey-scale TIFF image. + */ +static int +parse_greyscale8( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + PEL *mask; + + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) ) + return( -1 ); + + /* Eor each pel with this later. + */ + if( !(mask = IM_ARRAY( out, 1, PEL )) ) + return( -1 ); + *mask = (pm == PHOTOMETRIC_MINISBLACK) ? 0 : 255; + + out->Bands = 1; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_B_W; + + rtiff->sfn = greyscale8_line; + rtiff->client = mask; + + return( 0 ); +} + +/* Per-scanline process function for 16-bit greyscale images. + */ +static void +greyscale16_line( PEL *q, PEL *p, int n, void *flg ) +{ + /* Extract swap mask. + */ + unsigned short mask = *((unsigned short *) flg); + unsigned short *p1 = (unsigned short *) p; + unsigned short *q1 = (unsigned short *) q; + int x; + + /* Read bytes, swapping sense if necessary. + */ + for( x = 0; x < n; x++ ) + q1[x] = p1[x] ^ mask; +} + +/* Read a 16-bit grey-scale TIFF image. + */ +static int +parse_greyscale16( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + unsigned short *mask; + + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 16 ) ) + return( -1 ); + + /* Eor each pel with this later. + */ + if( !(mask = IM_ARRAY( out, 1, unsigned short )) ) + return( -1 ); + mask[0] = (pm == PHOTOMETRIC_MINISBLACK) ? 0 : 65535; + + out->Bands = 1; + out->Bbits = 16; + out->BandFmt = IM_BANDFMT_USHORT; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_GREY16; + + rtiff->sfn = greyscale16_line; + rtiff->client = mask; + + return( 0 ); +} + +/* Per-scanline process function for 32-bit floating point greyscale images. + */ +static void +greyscale32f_line( PEL *q, PEL *p, int n ) +{ + float *p1 = (float *) p; + float *q1 = (float *) q; + int x; + + for( x = 0; x < n; x++ ) + q1[x] = p1[x]; +} + +/* Read a 32-bit floating point greyscale TIFF image. What do we do about + * MINISWHITE/MINISBLACK (pm)? Not sure ... just ignore it. + */ +static int +parse_greyscale32f( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 32 ) ) + return( -1 ); + + out->Bands = 1; + out->Bbits = 32; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_B_W; + + rtiff->sfn = (scanline_process_fn) greyscale32f_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for palette images. + */ +static void +palette_line( PEL *q, PEL *p, int n, void *flg ) +{ + /* Extract maps. + */ + PEL *red = ((PEL **) flg)[0]; + PEL *green = ((PEL **) flg)[1]; + PEL *blue = ((PEL **) flg)[2]; + int x; + + /* Read bytes, generating colour. + */ + for( x = 0; x < n; x++ ) { + int i = *p++; + + q[0] = red[i]; + q[1] = green[i]; + q[2] = blue[i]; + + q += 3; + } +} + +/* Read a palette-ised TIFF image. Again, we only allow 8-bits for now. + */ +static int +parse_palette( ReadTiff *rtiff, IMAGE *out ) +{ + uint16 *tred, *tgreen, *tblue; + PEL *red, *green, *blue; + PEL **maps; + int i; + + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 1 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) ) + return( -1 ); + + /* Allocate mem for VIPS colour maps. + */ + if( !(red = IM_ARRAY( out, 256, PEL )) || + !(green = IM_ARRAY( out, 256, PEL )) || + !(blue = IM_ARRAY( out, 256, PEL )) || + !(maps = IM_ARRAY( out, 3, PEL * )) ) + return( -1 ); + + /* Get maps, convert to 8-bit data. + */ + if( !TIFFGetField( rtiff->tiff, + TIFFTAG_COLORMAP, &tred, &tgreen, &tblue ) ) { + im_error( "im_tiff2vips", _( "bad colormap" ) ); + return( -1 ); + } + for( i = 0; i < 256; i++ ) { + red[i] = tred[i] >> 8; + green[i] = tgreen[i] >> 8; + blue[i] = tblue[i] >> 8; + } + maps[0] = red; + maps[1] = green; + maps[2] = blue; + + out->Bands = 3; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_sRGB; + + rtiff->sfn = palette_line; + rtiff->client = maps; + + return( 0 ); +} + +/* Per-scanline process function for 8-bit RGB/RGBA. + */ +static void +rgb8_line( PEL *q, PEL *p, int n, IMAGE *im ) +{ + int x, b; + + for( x = 0; x < n; x++ ) { + for( b = 0; b < im->Bands; b++ ) + q[b] = p[b]; + + q += im->Bands; + p += im->Bands; + } +} + +/* Read an 8-bit RGB/RGBA image. + */ +static int +parse_rgb8( ReadTiff *rtiff, IMAGE *out ) +{ + int bands; + + /* Check other TIFF fields to make sure we can read this. Can have 4 + * bands for RGBA. + */ + if( !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) || + !tfget16( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, &bands ) ) + return( -1 ); + if( bands != 3 && bands != 4 ) { + im_error( "im_tiff2vips", _( "3 or 4 bands RGB TIFF only" ) ); + return( -1 ); + } + + out->Bands = bands; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_sRGB; + + rtiff->sfn = (scanline_process_fn) rgb8_line; + rtiff->client = out; + + return( 0 ); +} + +/* Per-scanline process function for RGB/RGBA 16. + */ +static void +rgb16_line( PEL *q, PEL *p, int n, IMAGE *im ) +{ + int x, b; + unsigned short *p1 = (unsigned short *) p; + unsigned short *q1 = (unsigned short *) q; + + for( x = 0; x < n; x++ ) { + for( b = 0; b < im->Bands; b++ ) + q1[b] = p1[b]; + + q1 += im->Bands; + p1 += im->Bands; + } +} + +/* Read a 16-bit RGB/RGBA image. + */ +static int +parse_rgb16( ReadTiff *rtiff, IMAGE *out ) +{ + int bands; + + /* Check other TIFF fields to make sure we can read this. Can have 4 + * bands for RGBA. + */ + if( !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 16 ) || + !tfget16( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, &bands ) ) + return( -1 ); + if( bands != 3 && bands != 4 ) { + im_error( "im_tiff2vips", _( "3 or 4 bands RGB TIFF only" ) ); + return( -1 ); + } + + out->Bands = bands; + out->Bbits = 16; + out->BandFmt = IM_BANDFMT_USHORT; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_RGB16; + + rtiff->sfn = (scanline_process_fn) rgb16_line; + rtiff->client = out; + + return( 0 ); +} + +/* Per-scanline process function for 32f. + */ +static void +r32f_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + float *p1 = (float *) p; + float *q1 = (float *) q; + + for( x = 0; x < n; x++ ) { + q1[0] = p1[0]; + q1[1] = p1[1]; + q1[2] = p1[2]; + + q1 += 3; + p1 += 3; + } +} + +/* Read a 32-bit float image. RGB or LAB, with or without alpha. + */ +static int +parse_32f( ReadTiff *rtiff, int pm, IMAGE *out ) +{ + int bands; + + if( !tfget16( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, &bands ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 32 ) ) + return( -1 ); + + /* Can be 4 for images with an alpha channel. + */ + assert( bands == 3 || bands == 4 ); + + out->Bands = bands; + out->Bbits = 32; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Coding = IM_CODING_NONE; + + switch( pm ) { + case PHOTOMETRIC_CIELAB: + out->Type = IM_TYPE_LAB; + break; + + case PHOTOMETRIC_RGB: + out->Type = IM_TYPE_sRGB; + break; + + default: + assert( 0 ); + } + + rtiff->sfn = r32f_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Per-scanline process function for CMYK8. + */ +static void +cmyk8_line( PEL *q, PEL *p, int n, void *dummy ) +{ + int x; + + for( x = 0; x < n; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + q[3] = p[3]; + + q += 4; + p += 4; + } +} + +/* Read a CMYK image. + */ +static int +parse_cmyk( ReadTiff *rtiff, IMAGE *out ) +{ + if( !tfequals( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, 4 ) || + !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) || + !tfequals( rtiff->tiff, TIFFTAG_INKSET, INKSET_CMYK ) ) + return( -1 ); + + out->Bands = 4; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_CMYK; + + rtiff->sfn = cmyk8_line; + rtiff->client = NULL; + + return( 0 ); +} + +/* Read resolution from a TIFF image. + */ +static int +parse_resolution( TIFF *tiff, IMAGE *out ) +{ + float x, y; + int ru; + + if( TIFFGetFieldDefaulted( tiff, TIFFTAG_XRESOLUTION, &x ) && + TIFFGetFieldDefaulted( tiff, TIFFTAG_YRESOLUTION, &y ) && + tfget16( tiff, TIFFTAG_RESOLUTIONUNIT, &ru ) ) { + switch( ru ) { + case RESUNIT_NONE: + break; + + case RESUNIT_INCH: + /* In pixels-per-inch ... convert to mm. + */ + x /= 10.0 * 2.54; + y /= 10.0 * 2.54; + break; + + case RESUNIT_CENTIMETER: + /* In pixels-per-centimetre ... convert to mm. + */ + x /= 10.0; + y /= 10.0; + break; + + default: + im_error( "im_tiff2vips", + _( "unknown resolution unit" ) ); + return( -1 ); + } + } + else { + im_warning( "im_tiff2vips: no resolution information for " + "TIFF image \"%s\" -- defaulting to 1 pixel per mm", + TIFFFileName( tiff ) ); + x = 1.0; + y = 1.0; + } + + out->Xres = x; + out->Yres = y; + + return( 0 ); +} + +/* Look at PhotometricInterpretation and BitsPerPixel, and try to figure out + * which of the image classes this is. + */ +static int +parse_header( ReadTiff *rtiff, IMAGE *out ) +{ + int pm, bps, format; + uint32 data_length; + void *data; + + /* Ban separate planes, too annoying. + */ + if( tfexists( rtiff->tiff, TIFFTAG_PLANARCONFIG ) && + !tfequals( rtiff->tiff, + TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ) ) + return( -1 ); + + /* Always need dimensions. + */ + if( !tfget32( rtiff->tiff, TIFFTAG_IMAGEWIDTH, &out->Xsize ) || + !tfget32( rtiff->tiff, TIFFTAG_IMAGELENGTH, &out->Ysize ) || + parse_resolution( rtiff->tiff, out ) ) + return( -1 ); + + /* Try to find out which type of TIFF image it is. + */ + if( !tfget16( rtiff->tiff, TIFFTAG_PHOTOMETRIC, &pm ) || + !tfget16( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, &bps ) ) + return( -1 ); + + switch( pm ) { + case PHOTOMETRIC_CIELAB: + switch( bps ) { + case 8: + if( parse_labpack( rtiff, out ) ) + return( -1 ); + break; + + case 16: + if( parse_labs( rtiff, out ) ) + return( -1 ); + break; + + case 32: + if( !tfget16( rtiff->tiff, + TIFFTAG_SAMPLEFORMAT, &format ) ) + return( -1 ); + + if( format == SAMPLEFORMAT_IEEEFP ) { + if( parse_32f( rtiff, pm, out ) ) + return( -1 ); + } + else { + im_error( "im_tiff2vips", + _( "unsupported sample " + "format %d for lab image" ), + format ); + return( -1 ); + } + + break; + + default: + im_error( "im_tiff2vips", + _( "unsupported depth %d for LAB image" ), + bps ); + return( -1 ); + } + + break; + + case PHOTOMETRIC_YCBCR: + /* Easy decision! + */ + if( parse_ycbcr( rtiff, out ) ) + return( -1 ); + + break; + + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + switch( bps ) { + case 1: + if( parse_onebit( rtiff, pm, out ) ) + return( -1 ); + + break; + + case 8: + if( parse_greyscale8( rtiff, pm, out ) ) + return( -1 ); + + break; + + case 16: + if( parse_greyscale16( rtiff, pm, out ) ) + return( -1 ); + + break; + + case 32: + if( !tfget16( rtiff->tiff, + TIFFTAG_SAMPLEFORMAT, &format ) ) + return( -1 ); + + if( format == SAMPLEFORMAT_IEEEFP ) { + if( parse_greyscale32f( rtiff, pm, out ) ) + return( -1 ); + } + else { + im_error( "im_tiff2vips", + _( "unsupported sample format " + "%d for greyscale image" ), + format ); + return( -1 ); + } + + break; + + default: + im_error( "im_tiff2vips", _( "unsupported depth %d " + "for greyscale image" ), bps ); + return( -1 ); + } + + break; + + case PHOTOMETRIC_PALETTE: + /* Full colour pallette. + */ + if( parse_palette( rtiff, out ) ) + return( -1 ); + + break; + + case PHOTOMETRIC_RGB: + /* Plain RGB. + */ + switch( bps ) { + case 8: + if( parse_rgb8( rtiff, out ) ) + return( -1 ); + break; + + case 16: + if( parse_rgb16( rtiff, out ) ) + return( -1 ); + break; + + case 32: + if( !tfget16( rtiff->tiff, + TIFFTAG_SAMPLEFORMAT, &format ) ) + return( -1 ); + + if( format == SAMPLEFORMAT_IEEEFP ) { + if( parse_32f( rtiff, pm, out ) ) + return( -1 ); + } + else { + im_error( "im_tiff2vips", + _( "unsupported sample " + "format %d for rgb image" ), + format ); + return( -1 ); + } + + break; + + default: + im_error( "im_tiff2vips", _( "unsupported depth %d " + "for RGB image" ), bps ); + return( -1 ); + } + + break; + + case PHOTOMETRIC_SEPARATED: + if( parse_cmyk( rtiff, out ) ) + return( -1 ); + + break; + + default: + im_error( "im_tiff2vips", _( "unknown photometric " + "interpretation %d" ), pm ); + return( -1 ); + } + + /* Read any ICC profile. + */ + if( TIFFGetField( rtiff->tiff, + TIFFTAG_ICCPROFILE, &data_length, &data ) ) { + void *data_copy; + + if( !(data_copy = im_malloc( NULL, data_length )) ) + return( -1 ); + memcpy( data_copy, data, data_length ); + if( im_meta_set_blob( out, IM_META_ICC_NAME, + (im_callback_fn) im_free, data_copy, data_length ) ) { + im_free( data_copy ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Allocate a tile buffer. Have one of these for each thread so we can unpack + * to vips in parallel. + */ +static void * +seq_start( IMAGE *out, ReadTiff *rtiff ) +{ + tdata_t *buf; + + if( !(buf = im_malloc( NULL, TIFFTileSize( rtiff->tiff ) )) ) + return( NULL ); + + return( (void *) buf ); +} + +/* Loop over the output region, painting in tiles from the file. + */ +static int +fill_region( REGION *out, tdata_t *buf, ReadTiff *rtiff ) +{ + Rect *r = &out->valid; + + /* Find top left of tiles we need. + */ + int xs = (r->left / rtiff->twidth) * rtiff->twidth; + int ys = (r->top / rtiff->theight) * rtiff->theight; + + /* Sizeof a line of bytes in the TIFF tile. + */ + int tls = TIFFTileSize( rtiff->tiff ) / rtiff->theight; + + /* Sizeof a pel in the TIFF file. This won't work for formats which + * are <1 byte per pel, like onebit :-( Fortunately, it's only used + * to calculate addresses within a tile, and because we are wrapped in + * im_tile_cache(), we will never have to calculate positions within a + * tile. + */ + int tps = tls / rtiff->twidth; + + int x, y, z; + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += rtiff->theight ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += rtiff->twidth ) { + Rect tile; + Rect hit; + + /* Read that tile. + */ + g_mutex_lock( rtiff->tlock ); + if( TIFFReadTile( rtiff->tiff, buf, + x, y, 0, 0 ) < 0 ) { + g_mutex_unlock( rtiff->tlock ); + return( -1 ); + } + g_mutex_unlock( rtiff->tlock ); + + /* The tile we read. + */ + tile.left = x; + tile.top = y; + tile.width = rtiff->twidth; + tile.height = rtiff->twidth; + + /* The section that hits the region we are building. + */ + im_rect_intersectrect( &tile, r, &hit ); + + /* Unpack to VIPS format. We can do this in parallel. + * Just unpack the section of the tile we need. + */ + for( z = 0; z < hit.height; z++ ) { + PEL *p = (PEL *) buf + + (hit.left - tile.left) * tps + + (hit.top - tile.top + z) * tls; + PEL *q = (PEL *) IM_REGION_ADDR( out, + hit.left, hit.top + z ); + + rtiff->sfn( q, p, hit.width, rtiff->client ); + } + } + + return( 0 ); +} + +/* Tile-type TIFF reader core - pass in a per-tile transform. Generate into + * the im and do it all partially. + */ +static int +read_tilewise( ReadTiff *rtiff, IMAGE *out ) +{ + IMAGE *raw; + + /* Tile cache: keep enough for two complete rows of tiles. + * This lets us do (smallish) area ops, like im_conv(), while + * still only hitting each TIFF tile once. + */ + if( !(raw = im_open_local( out, "cache", "p" )) ) + return( -1 ); + + /* Get tiling geometry. + */ + if( !tfget32( rtiff->tiff, TIFFTAG_TILEWIDTH, &rtiff->twidth ) || + !tfget32( rtiff->tiff, TIFFTAG_TILELENGTH, &rtiff->theight ) ) + return( -1 ); + + /* Make sure we can write PIO-style. + */ + if( im_poutcheck( raw ) ) + return( -1 ); + + /* Parse the TIFF header and set up raw. + */ + if( parse_header( rtiff, raw ) ) + return( -1 ); + + /* Process and save as VIPS. + */ + if( im_demand_hint( raw, IM_SMALLTILE, NULL ) || + im_generate( raw, + seq_start, fill_region, im_free, rtiff, NULL ) ) + return( -1 ); + + /* Copy to out, adding a cache. Enough tiles for two complete rows. + */ + if( im_tile_cache( raw, out, + rtiff->twidth, rtiff->theight, + 2 * (1 + raw->Xsize / rtiff->twidth) ) ) + return( -1 ); + + return( 0 ); +} + +/* Scanline-type TIFF reader core - pass in a per-scanline transform. + */ +static int +read_scanlinewise( ReadTiff *rtiff, IMAGE *out ) +{ + PEL *vbuf; + tdata_t tbuf; + int y; + + if( parse_header( rtiff, out ) ) + return( -1 ); + + /* Make sure we can write WIO-style. + */ + if( im_outcheck( out ) || im_setupout( out ) ) + return( -1 ); + + /* Make VIPS output buffer. + */ + if( !(vbuf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Make TIFF line input buffer. + */ + if( !(tbuf = im_malloc( out, TIFFScanlineSize( rtiff->tiff ) )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + /* Read TIFF scanline. + */ + if( TIFFReadScanline( rtiff->tiff, tbuf, y, 0 ) < 0 ) { + im_error( "im_tiff2vips", _( "read error" ) ); + return( -1 ); + } + + /* Process and save as VIPS. + */ + rtiff->sfn( vbuf, tbuf, out->Xsize, rtiff->client ); + if( im_writeline( y, out, vbuf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Free a ReadTiff. + */ +static int +readtiff_destroy( ReadTiff *rtiff ) +{ + IM_FREEF( TIFFClose, rtiff->tiff ); + IM_FREEF( g_mutex_free, rtiff->tlock ); + + return( 0 ); +} + +/* Make a ReadTiff. + */ +static ReadTiff * +readtiff_new( const char *filename, IMAGE *out ) +{ + ReadTiff *rtiff; + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char *p, *q; + + if( !(rtiff = IM_NEW( out, ReadTiff )) ) + return( NULL ); + rtiff->filename = NULL; + rtiff->out = out; + im_filename_split( filename, name, mode ); + rtiff->filename = im_strdup( out, name ); + rtiff->page = 0; + rtiff->tiff = NULL; + rtiff->sfn = NULL; + rtiff->client = NULL; + rtiff->twidth = 0; + rtiff->theight = 0; + rtiff->tlock = g_mutex_new(); + + if( im_add_close_callback( out, + (im_callback_fn) readtiff_destroy, rtiff, NULL ) ) { + readtiff_destroy( rtiff ); + return( NULL ); + } + + /* Parse out params. + */ + p = &mode[0]; + if( (q = im_getnextoption( &p )) ) { + rtiff->page = atoi( q ); + + if( rtiff->page < 0 || rtiff->page > 1000 ) { + im_error( "im_tiff2vips", _( "bad page number %d" ), + rtiff->page ); + return( NULL ); + } + } + + return( rtiff ); +} + +/* Pull out the nth directory from a TIFF file. + */ +static TIFF * +get_directory( const char *filename, int page ) +{ + TIFF *tif; + int i; + + /* No need to use "b" and it means something different anyway. + */ + if( !(tif = TIFFOpen( filename, "r" )) ) { + im_error( "im_tiff2vips", + _( "unable to open \"%s\" for input" ), + filename ); + return( NULL ); + } + + for( i = 0; i < page; i++ ) + if( !TIFFReadDirectory( tif ) ) { + /* Run out of directories. + */ + TIFFClose( tif ); + return( NULL ); + } + + return( tif ); +} + +int +im_istiffpyramid( const char *name ) +{ + TIFF *tif; + + TIFFSetErrorHandler( (TIFFErrorHandler) thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) thandler_warning ); + + if( (tif = get_directory( name, 2 )) ) { + /* We can see page 2 ... assume it is. + */ + TIFFClose( tif ); + return( 1 ); + } + + return( 0 ); +} + +int +im_tiff2vips( const char *filename, IMAGE *out ) +{ + ReadTiff *rtiff; + +#ifdef DEBUG + printf( "im_tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() ); +#endif /*DEBUG*/ + + TIFFSetErrorHandler( (TIFFErrorHandler) thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) thandler_warning ); + + if( !(rtiff = readtiff_new( filename, out )) ) + return( -1 ); + + if( !(rtiff->tiff = get_directory( rtiff->filename, rtiff->page )) ) { + im_error( "im_tiff2vips", _( "TIFF file does not " + "contain page %d" ), rtiff->page ); + return( -1 ); + } + + if( TIFFIsTiled( rtiff->tiff ) ) { + if( read_tilewise( rtiff, out ) ) + return( -1 ); + } + else { + if( read_scanlinewise( rtiff, out ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Just parse the header. + */ +int +im_tiff2vips_header( const char *filename, IMAGE *out ) +{ + ReadTiff *rtiff; + + TIFFSetErrorHandler( (TIFFErrorHandler) thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) thandler_warning ); + + if( !(rtiff = readtiff_new( filename, out )) ) + return( -1 ); + + if( !(rtiff->tiff = get_directory( rtiff->filename, rtiff->page )) ) { + im_error( "im_tiff2vips", + _( "TIFF file does not contain page %d" ), + rtiff->page ); + return( -1 ); + } + + if( parse_header( rtiff, out ) ) + return( -1 ); + + return( 0 ); +} + +#endif /*HAVE_TIFF*/ diff --git a/libsrc/conversion/im_tile_cache.c b/libsrc/conversion/im_tile_cache.c new file mode 100644 index 00000000..f7f30b9d --- /dev/null +++ b/libsrc/conversion/im_tile_cache.c @@ -0,0 +1,390 @@ +/* Tile tile cache from tiff2vips ... broken out so it can be shared with + * openexr read. + * + * This isn't the same as im_cache(): we don't sub-divide, and we + * single-thread our callee. + * + * 23/8/06 + * - take ownership of reused tiles in case the cache is being shared + * 13/2/07 + * - relase ownership after fillng with pixels in case we read across + * threads + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Lower and upper bounds for tile cache size. Choose an exact number based on + * tile size. + */ +#define IM_MAX_TILE_CACHE (250) +#define IM_MIN_TILE_CACHE (5) + +/* A tile in our cache. + */ +typedef struct { + struct _Read *read; + + REGION *region; /* REGION with private mem for data */ + int time; /* Time of last use for flush */ + int x; /* xy pos in VIPS image cods */ + int y; +} Tile; + +/* Stuff we track during a read. + */ +typedef struct _Read { + /* Parameters. + */ + IMAGE *in; + IMAGE *out; + int tile_width; + int tile_height; + int max_tiles; + + /* Cache. + */ + int time; /* Update ticks for LRU here */ + int ntiles; /* Current cache size */ + GMutex *lock; /* Lock everything here */ + GSList *cache; /* List of tiles */ +} Read; + +static void +tile_destroy( Tile *tile ) +{ + Read *read = tile->read; + + read->cache = g_slist_remove( read->cache, tile ); + read->ntiles -= 1; + assert( read->ntiles >= 0 ); + tile->read = NULL; + + IM_FREEF( im_region_free, tile->region ); + + im_free( tile ); +} + +static void +read_destroy( Read *read ) +{ + IM_FREEF( g_mutex_free, read->lock ); + + while( read->cache ) { + Tile *tile = (Tile *) read->cache->data; + + tile_destroy( tile ); + } + + im_free( read ); +} + +static Read * +read_new( IMAGE *in, IMAGE *out, + int tile_width, int tile_height, int max_tiles ) +{ + Read *read; + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + read->in = in; + read->out = out; + read->tile_width = tile_width; + read->tile_height = tile_height; + read->max_tiles = max_tiles; + read->time = 0; + read->ntiles = 0; + read->lock = g_mutex_new(); + read->cache = NULL; + + if( im_add_close_callback( out, + (im_callback_fn) read_destroy, read, NULL ) ) { + read_destroy( read ); + return( NULL ); + } + + return( read ); +} + +static Tile * +tile_new( Read *read ) +{ + Tile *tile; + + if( !(tile = IM_NEW( NULL, Tile )) ) + return( NULL ); + + tile->read = read; + tile->region = NULL; + tile->time = read->time; + tile->x = -1; + tile->y = -1; + read->cache = g_slist_prepend( read->cache, tile ); + assert( read->ntiles >= 0 ); + read->ntiles += 1; + + if( !(tile->region = im_region_create( read->in )) ) { + tile_destroy( tile ); + return( NULL ); + } + + return( tile ); +} + +/* Do we have a tile in the cache? + */ +static Tile * +tile_search( Read *read, int x, int y ) +{ + GSList *p; + + for( p = read->cache; p; p = p->next ) { + Tile *tile = (Tile *) p->data; + + if( tile->x == x && tile->y == y ) + return( tile ); + } + + return( NULL ); +} + +static void +tile_touch( Tile *tile ) +{ + assert( tile->read->ntiles >= 0 ); + + tile->time = tile->read->time++; +} + +/* Fill a tile with pixels. + */ +static int +tile_fill( Tile *tile, int x, int y ) +{ + Rect area; + + tile->x = x; + tile->y = y; + +#ifdef DEBUG + printf( "im_tile_cache: filling tile %d x %d\n", tile->x, tile->y ); +#endif /*DEBUG*/ + + area.left = x; + area.top = y; + area.width = tile->read->tile_width; + area.height = tile->read->tile_height; + if( im_prepare( tile->region, &area ) ) + return( -1 ); + + /* Make sure these pixels aren't part of this thread's buffer cache + * ... they may be read out by another thread. + */ + im__region_no_ownership( tile->region ); + + tile_touch( tile ); + + return( 0 ); +} + +/* Find existing tile, make a new tile, or if we have a full set of tiles, + * reuse LRU. + */ +static Tile * +tile_find( Read *read, int x, int y ) +{ + Tile *tile; + int oldest; + GSList *p; + + /* In cache already? + */ + if( (tile = tile_search( read, x, y )) ) { + tile_touch( tile ); + + return( tile ); + } + + /* Cache not full? + */ + if( read->max_tiles == -1 || + read->ntiles < read->max_tiles ) { + if( !(tile = tile_new( read )) || + tile_fill( tile, x, y ) ) + return( NULL ); + + return( tile ); + } + + /* Reuse an old one. + */ + oldest = read->time; + tile = NULL; + for( p = read->cache; p; p = p->next ) { + Tile *t = (Tile *) p->data; + + if( t->time < oldest ) { + oldest = t->time; + tile = t; + } + } + + assert( tile ); + + /* The tile may have been created by another thread if we are sharing + * the tile cache between several readers. Take ownership of the tile + * to stop assert() failures in im_prepare(). This is safe, since we + * are in a mutex. + */ + im__region_take_ownership( tile->region ); + +#ifdef DEBUG + printf( "im_tile_cache: reusing tile %d x %d\n", tile->x, tile->y ); +#endif /*DEBUG*/ + + if( tile_fill( tile, x, y ) ) + return( NULL ); + + return( tile ); +} + +/* Copy rect from from to to. + */ +static void +copy_region( REGION *from, REGION *to, Rect *area ) +{ + int y; + + /* Area should be inside both from and to. + */ + assert( im_rect_includesrect( &from->valid, area ) ); + assert( im_rect_includesrect( &to->valid, area ) ); + + /* Loop down common area, copying. + */ + for( y = area->top; y < IM_RECT_BOTTOM( area ); y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( from, area->left, y ); + PEL *q = (PEL *) IM_REGION_ADDR( to, area->left, y ); + + memcpy( q, p, IM_IMAGE_SIZEOF_PEL( from->im ) * area->width ); + } +} + +/* Loop over the output region, filling with data from cache. + */ +static int +fill_region( REGION *out, void *seq, Read *read ) +{ + const int tw = read->tile_width; + const int th = read->tile_height; + Rect *r = &out->valid; + + /* Find top left of tiles we need. + */ + int xs = (r->left / tw) * tw; + int ys = (r->top / th) * th; + + int x, y; + + g_mutex_lock( read->lock ); + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += th ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += tw ) { + Tile *tile; + Rect tarea; + Rect hit; + + if( !(tile = tile_find( read, x, y )) ) { + g_mutex_unlock( read->lock ); + return( -1 ); + } + + /* The area of the tile. + */ + tarea.left = x; + tarea.top = y; + tarea.width = tw; + tarea.height = th; + + /* The part of the tile that we need. + */ + im_rect_intersectrect( &tarea, r, &hit ); + + copy_region( tile->region, out, &hit ); + } + + g_mutex_unlock( read->lock ); + + return( 0 ); +} + +int +im_tile_cache( IMAGE *in, IMAGE *out, + int tile_width, int tile_height, int max_tiles ) +{ + Read *read; + + if( tile_width <= 0 || tile_height <= 0 || max_tiles < -1 ) { + im_error( "im_tile_cache", _( "bad parameters" ) ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + if( !(read = read_new( in, out, + tile_width, tile_height, max_tiles )) ) + return( -1 ); + if( im_generate( out, + NULL, fill_region, NULL, read, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_vips2csv.c b/libsrc/conversion/im_vips2csv.c new file mode 100644 index 00000000..6d19f9dd --- /dev/null +++ b/libsrc/conversion/im_vips2csv.c @@ -0,0 +1,146 @@ +/* Write a csv file. + * + * 9/6/06 + * - hacked from im_debugim + * 23/10/06 + * - allow separator to be specified (default "\t", ) + * 17/11/06 + * - oops, was broken + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define PRINT_INT( TYPE ) fprintf( fp, "%d", *((TYPE*)p) ); +#define PRINT_FLOAT( TYPE ) fprintf( fp, "%g", *((TYPE*)p) ); +#define PRINT_COMPLEX( TYPE ) fprintf( fp, "(%g, %g)", \ + ((TYPE*)p)[0], ((TYPE*)p)[1] ); + +static int +vips2csv( IMAGE *in, FILE *fp, const char *sep ) +{ + int w = IM_IMAGE_N_ELEMENTS( in ); + int es = IM_IMAGE_SIZEOF_ELEMENT( in ); + + int x, y; + PEL *p; + + p = (PEL *) in->data; + for( y = 0; y < in->Ysize; y++ ) { + for( x = 0; x < w; x++ ) { + if( x > 0 ) + fprintf( fp, "%s", sep ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + PRINT_INT( unsigned char ); break; + case IM_BANDFMT_CHAR: + PRINT_INT( char ); break; + case IM_BANDFMT_USHORT: + PRINT_INT( unsigned short ); break; + case IM_BANDFMT_SHORT: + PRINT_INT( short ); break; + case IM_BANDFMT_UINT: + PRINT_INT( unsigned int ); break; + case IM_BANDFMT_INT: + PRINT_INT( int ); break; + case IM_BANDFMT_FLOAT: + PRINT_FLOAT( float ); break; + case IM_BANDFMT_DOUBLE: + PRINT_FLOAT( double ); break; + case IM_BANDFMT_COMPLEX: + PRINT_COMPLEX( float ); break; + case IM_BANDFMT_DPCOMPLEX: + PRINT_COMPLEX( double ); break; + + default: + assert( 0 ); + } + + p += es; + } + + fprintf( fp, "\n" ); + } + + return( 0 ); +} + +int +im_vips2csv( IMAGE *in, const char *filename ) +{ + char *separator = "\t"; + + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + FILE *fp; + char *p, *q, *r; + + /* Parse mode string. + */ + im_filename_split( filename, name, mode ); + p = &mode[0]; + while( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "sep", q ) && (r = im_getsuboption( q )) ) + separator = r; + } + + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_vips2csv", _( "input must be uncoded" ) ); + return( -1 ); + } + + if( !(fp = fopen( name, "w" )) ) { + im_error( "im_cvips2csv", _( "unable to open \"%s\"" ), + name ); + return( -1 ); + } + + if( vips2csv( in, fp, separator ) ) { + fclose( fp ); + return( -1 ); + } + + fclose( fp ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_vips2jpeg.c b/libsrc/conversion/im_vips2jpeg.c new file mode 100644 index 00000000..69f83d2c --- /dev/null +++ b/libsrc/conversion/im_vips2jpeg.c @@ -0,0 +1,867 @@ +/* Convert 1 or 3-band 8-bit VIPS images to/from JPEG. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 12/11/04 + * - better demand size choice for eval + * 30/6/05 JC + * - update im_error()/im_warn() + * - now loads and saves exif data + * 30/7/05 + * - now loads ICC profiles + * - now saves ICC profiles from the VIPS header + * 24/8/05 + * - jpeg load sets vips xres/yres from exif, if possible + * - jpeg save sets exif xres/yres from vips, if possible + * 29/8/05 + * - cut from old vips_jpeg.c + * 20/4/06 + * - auto convert to sRGB/mono for save + * 13/10/06 + * - add +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_JPEG + +#include + +int +im_vips2jpeg( IMAGE *in, const char *filename ) +{ + im_error( "im_vips2jpeg", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +int +im_vips2bufjpeg( IMAGE *in, IMAGE *out, int qfac, char **obuf, int *olen ) +{ + im_error( "im_vips2bufjpeg", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +int +im_vips2mimejpeg( IMAGE *in, int qfac ) +{ + im_error( "im_vips2mimejpeg", _( "JPEG support disabled" ) ); + + return( -1 ); +} + +#else /*HAVE_JPEG*/ + +#include +#include +#include +#include +#include + +#ifdef HAVE_EXIF +#ifdef UNTAGGED_EXIF +#include +#include +#include +#include +#else /*!UNTAGGED_EXIF*/ +#include +#include +#include +#include +#endif /*UNTAGGED_EXIF*/ +#endif /*HAVE_EXIF*/ + +#include +#include +#include + +/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we + * also define. Make sure it's turned off. + */ +#ifdef HAVE_STDLIB_H +#undef HAVE_STDLIB_H +#endif /*HAVE_STDLIB_H*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define a new error handler for when we bomb out. + */ +typedef struct { + /* Public fields. + */ + struct jpeg_error_mgr pub; + + /* Private stuff for us. + */ + jmp_buf jmp; /* longjmp() here to get back to VIPS */ + FILE *fp; /* fclose() if non-NULL */ +} ErrorManager; + +/* New output message method - send to VIPS. + */ +METHODDEF(void) +new_output_message( j_common_ptr cinfo ) +{ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message)( cinfo, buffer ); + im_error( "vips_jpeg", _( "%s" ), buffer ); + +#ifdef DEBUG + printf( "vips_jpeg.c: new_output_message: \"%s\"\n", buffer ); +#endif /*DEBUG*/ +} + +/* New error_exit handler. + */ +METHODDEF(void) +new_error_exit( j_common_ptr cinfo ) +{ + ErrorManager *eman = (ErrorManager *) cinfo->err; + +#ifdef DEBUG + printf( "vips_jpeg.c: new_error_exit\n" ); +#endif /*DEBUG*/ + + /* Close the fp if necessary. + */ + if( eman->fp ) { + (void) fclose( eman->fp ); + eman->fp = NULL; + } + + /* Send the error message to VIPS. This method is overridden above. + */ + (*cinfo->err->output_message)( cinfo ); + + /* Jump back. + */ + longjmp( eman->jmp, 1 ); +} + +/* What we track during a JPEG write. + */ +typedef struct { + IMAGE *in; + struct jpeg_compress_struct cinfo; + ErrorManager eman; + REGION *reg; + im_threadgroup_t *tg; + JSAMPROW *row_pointer; + char *profile_bytes; + unsigned int profile_length; +} Write; + +static void +write_destroy( Write *write ) +{ + jpeg_destroy_compress( &write->cinfo ); + IM_FREEF( im_threadgroup_free, write->tg ); + IM_FREEF( im_region_free, write->reg ); + IM_FREEF( im_close, write->in ); + IM_FREEF( fclose, write->eman.fp ); + IM_FREE( write->row_pointer ); + IM_FREE( write->profile_bytes ); + im_free( write ); +} + +static Write * +write_new( IMAGE *in ) +{ + Write *write; + + if( !(write = IM_NEW( NULL, Write )) ) + return( NULL ); + memset( write, 0, sizeof( Write ) ); + + if( !(write->in = im__convert_saveable( in, FALSE )) ) { + im_error( "im_vips2jpeg", + _( "unable to convert to RGB for save" ) ); + write_destroy( write ); + return( NULL ); + } + + write->reg = im_region_create( write->in ); + write->tg = im_threadgroup_create( write->in ); + + write->row_pointer = IM_ARRAY( NULL, write->tg->nlines, JSAMPROW ); + + write->cinfo.err = jpeg_std_error( &write->eman.pub ); + write->eman.pub.error_exit = new_error_exit; + write->eman.pub.output_message = new_output_message; + write->eman.fp = NULL; + write->profile_bytes = NULL; + write->profile_length = 0; + + if( !write->reg || !write->tg || !write->row_pointer ) { + write_destroy( write ); + return( NULL ); + } + + return( write ); +} + +#ifdef HAVE_EXIF +static void +write_rational( ExifEntry *entry, ExifByteOrder bo, void *data ) +{ + ExifRational *v = (ExifRational *) data; + + exif_set_rational( entry->data, bo, *v ); +} + +static void +write_short( ExifEntry *entry, ExifByteOrder bo, void *data ) +{ + ExifShort *v = (ExifShort *) data; + + exif_set_short( entry->data, bo, *v ); +} + +typedef void (*write_fn)( ExifEntry *, ExifByteOrder, void * ); + +static int +write_tag( ExifData *ed, ExifTag tag, ExifFormat f, write_fn fn, void *data ) +{ + ExifByteOrder bo; + int found; + int i; + + bo = exif_data_get_byte_order( ed ); + + /* Need to set the tag in all sections which have it :-( + */ + found = 0; + for( i = 0; i < EXIF_IFD_COUNT; i++ ) { + ExifEntry *entry; + + if( (entry = exif_content_get_entry( ed->ifd[i], tag )) && + entry->format == f && + entry->components == 1 ) { + fn( entry, bo, data ); + found = 1; + } + } + + if( !found ) { + /* There was no tag we could update ... make one in ifd[0]. + */ + ExifEntry *entry; + + entry = exif_entry_new(); + exif_content_add_entry( ed->ifd[0], entry ); + exif_entry_initialize( entry, tag ); + fn( entry, bo, data ); + } + + return( 0 ); +} + +static int +set_exif_resolution( ExifData *ed, IMAGE *im ) +{ + double xres, yres; + ExifRational xres_rational, yres_rational; + ExifShort unit; + + /* Always save as inches - more progs support it for read. + */ + xres = im->Xres * 25.4; + yres = im->Yres * 25.4; + unit = 2; + + /* Wow, how dumb, fix this. + */ + xres_rational.numerator = xres * 100000; + xres_rational.denominator = 100000; + yres_rational.numerator = yres * 100000; + yres_rational.denominator = 100000; + + if( write_tag( ed, EXIF_TAG_X_RESOLUTION, EXIF_FORMAT_RATIONAL, + write_rational, &xres_rational ) || + write_tag( ed, EXIF_TAG_Y_RESOLUTION, EXIF_FORMAT_RATIONAL, + write_rational, &yres_rational ) || + write_tag( ed, EXIF_TAG_RESOLUTION_UNIT, EXIF_FORMAT_SHORT, + write_short, &unit ) ) { + im_error( "im_jpeg2vips", + _( "error setting JPEG resolution" ) ); + return( -1 ); + } + + return( 0 ); +} +#endif /*HAVE_EXIF*/ + +static int +write_exif( Write *write ) +{ + unsigned char *data; + size_t data_length; + unsigned int idl; +#ifdef HAVE_EXIF + ExifData *ed; + + /* Either parse from the embedded EXIF, or if there's none, make + * some fresh EXIF we can write the resolution to. + */ + if( im_header_get_type( write->in, IM_META_EXIF_NAME ) ) { + if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, + (void *) &data, &data_length ) ) + return( -1 ); + + if( !(ed = exif_data_new_from_data( data, data_length )) ) + return( -1 ); + } + else + ed = exif_data_new(); + + /* Set EXIF resolution from VIPS. + */ + if( set_exif_resolution( ed, write->in ) ) { + exif_data_free( ed ); + return( -1 ); + } + + /* Reserialise and write. exif_data_save_data() returns an int for some + * reason. + */ + exif_data_save_data( ed, &data, &idl ); + if( !idl ) { + im_error( "im_jpeg2vips", _( "error saving EXIF" ) ); + exif_data_free( ed ); + return( -1 ); + } + data_length = idl; + +#ifdef DEBUG + printf( "im_vips2jpeg: attaching %d bytes of EXIF\n", data_length ); +#endif /*DEBUG*/ + + exif_data_free( ed ); + jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length ); + free( data ); +#else /*!HAVE_EXIF*/ + /* No libexif ... just copy the embedded EXIF over. + */ + if( im_header_get_type( write->in, IM_META_EXIF_NAME ) ) { + if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, + (void *) &data, &data_length ) ) + return( -1 ); + +#ifdef DEBUG + printf( "im_vips2jpeg: attaching %d bytes of EXIF\n", + data_length ); +#endif /*DEBUG*/ + + jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, + data, data_length ); + } +#endif /*!HAVE_EXIF*/ + + return( 0 ); +} + +/* ICC writer from lcms, slight tweaks. + */ + +#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ +#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ +#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ +#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +static void +write_profile_data (j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len) +{ + unsigned int num_markers; /* total number of markers we'll write */ + int cur_marker = 1; /* per spec, counting starts at 1 */ + unsigned int length; /* number of bytes to write in this marker */ + + /* rounding up will fail for length == 0 */ + assert( icc_data_len > 0 ); + + /* Calculate the number of markers we'll need, rounding up of course */ + num_markers = (icc_data_len + MAX_DATA_BYTES_IN_MARKER - 1) / + MAX_DATA_BYTES_IN_MARKER; + + while (icc_data_len > 0) { + /* length of profile to put in this marker */ + length = icc_data_len; + if (length > MAX_DATA_BYTES_IN_MARKER) + length = MAX_DATA_BYTES_IN_MARKER; + icc_data_len -= length; + + /* Write the JPEG marker header (APP2 code and marker length) */ + jpeg_write_m_header(cinfo, ICC_MARKER, + (unsigned int) (length + ICC_OVERHEAD_LEN)); + + /* Write the marker identifying string "ICC_PROFILE" (null-terminated). + * We code it in this less-than-transparent way so that the code works + * even if the local character set is not ASCII. + */ + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x5F); + jpeg_write_m_byte(cinfo, 0x50); + jpeg_write_m_byte(cinfo, 0x52); + jpeg_write_m_byte(cinfo, 0x4F); + jpeg_write_m_byte(cinfo, 0x46); + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x4C); + jpeg_write_m_byte(cinfo, 0x45); + jpeg_write_m_byte(cinfo, 0x0); + + /* Add the sequencing info */ + jpeg_write_m_byte(cinfo, cur_marker); + jpeg_write_m_byte(cinfo, (int) num_markers); + + /* Add the profile data */ + while (length--) { + jpeg_write_m_byte(cinfo, *icc_data_ptr); + icc_data_ptr++; + } + cur_marker++; + } +} + +/* Write an ICC Profile from a file into the JPEG stream. + */ +static int +write_profile_file( Write *write, const char *profile ) +{ + if( !(write->profile_bytes = + im__file_read_name( profile, &write->profile_length )) ) + return( -1 ); + write_profile_data( &write->cinfo, + (JOCTET *) write->profile_bytes, write->profile_length ); + +#ifdef DEBUG + printf( "im_vips2jpeg: attached profile \"%s\"\n", profile ); +#endif /*DEBUG*/ + + return( 0 ); +} + +static int +write_profile_meta( Write *write ) +{ + void *data; + size_t data_length; + + if( im_meta_get_blob( write->in, IM_META_ICC_NAME, + &data, &data_length ) ) + return( -1 ); + + write_profile_data( &write->cinfo, data, data_length ); + +#ifdef DEBUG + printf( "im_vips2jpeg: attached %d byte profile from VIPS header\n", + data_length ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Write a VIPS image to a JPEG compress struct. + */ +static int +write_vips( Write *write, int qfac, const char *profile ) +{ + IMAGE *in = write->in; + + Rect area; + int y, i; + + /* Should have been converted for save. + */ + assert( in->BandFmt == IM_BANDFMT_UCHAR ); + assert( in->Coding == IM_CODING_NONE ); + assert( in->Bands == 1 || in->Bands == 3 ); + + /* Check input image. + */ + if( im_pincheck( in ) ) + return( -1 ); + if( qfac < 0 || qfac > 100 ) { + im_error( "im_vips2jpeg", _( "qfac should be in 0-100" ) ); + return( -1 ); + } + + /* Set compression parameters. + */ + write->cinfo.image_width = in->Xsize; + write->cinfo.image_height = in->Ysize; + if( in->Bands == 3 ) { + write->cinfo.input_components = 3; + write->cinfo.in_color_space = JCS_RGB; + } + else if( in->Bands == 1 ) { + write->cinfo.input_components = 1; + write->cinfo.in_color_space = JCS_GRAYSCALE; + } + + /* Rest to default. + */ + jpeg_set_defaults( &write->cinfo ); + jpeg_set_quality( &write->cinfo, qfac, TRUE ); + + /* Build compress tables. + */ + jpeg_start_compress( &write->cinfo, TRUE ); + + /* Write any APP markers we need. + */ + if( write_exif( write ) ) + return( -1 ); + + /* A profile supplied as an argument overrides an embedded profile. + */ + if( profile && + write_profile_file( write, profile ) ) + return( -1 ); + if( !profile && + im_header_get_type( in, IM_META_ICC_NAME ) && + write_profile_meta( write ) ) + return( -1 ); + + /* Write data. + */ + for( y = 0; y < in->Ysize; y += write->tg->nlines ) { + area.left = 0; + area.top = y; + area.width = in->Xsize; + area.height = IM_MIN( write->tg->nlines, in->Ysize - y ); + + if( im_prepare_thread( write->tg, write->reg, &area ) ) + return( -1 ); + + for( i = 0; i < area.height; i++ ) + write->row_pointer[i] = (JSAMPROW) + IM_REGION_ADDR( write->reg, 0, y + i ); + + jpeg_write_scanlines( &write->cinfo, + write->row_pointer, area.height ); + } + + jpeg_finish_compress( &write->cinfo ); + + return( 0 ); +} + +/* Write a VIPS image to a file as JPEG. + */ +int +im_vips2jpeg( IMAGE *in, const char *filename ) +{ + Write *write; + int qfac = 75; + char *profile = NULL; + + char *p, *q; + + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char buf[FILENAME_MAX]; + + /* Parse mode from filename. + */ + im_filename_split( filename, name, mode ); + strcpy( buf, mode ); + p = &buf[0]; + if( (q = im_getnextoption( &p )) ) { + if( strcmp( q, "" ) != 0 ) + qfac = atoi( mode ); + } + if( (q = im_getnextoption( &p )) ) { + if( strcmp( q, "" ) != 0 ) + profile = q; + } + if( (q = im_getnextoption( &p )) ) { + im_error( "im_vips2jpeg", + _( "unknown extra options \"%s\"" ), q ); + return( -1 ); + } + + if( !(write = write_new( in )) ) + return( -1 ); + + if( setjmp( write->eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + write_destroy( write ); + + return( -1 ); + } + + /* Can't do this in write_new(), has to be after we've made the + * setjmp(). + */ + jpeg_create_compress( &write->cinfo ); + + /* Make output. + */ +#ifdef BINARY_OPEN + if( !(write->eman.fp = fopen( name, "wb" )) ) { +#else /*BINARY_OPEN*/ + if( !(write->eman.fp = fopen( name, "w" )) ) { +#endif /*BINARY_OPEN*/ + write_destroy( write ); + im_error( "im_vips2jpeg", + _( "unable to open \"%s\"" ), name ); + + return( -1 ); + } + jpeg_stdio_dest( &write->cinfo, write->eman.fp ); + + /* Convert! + */ + if( write_vips( write, qfac, profile ) ) { + write_destroy( write ); + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} + +/* Just like the above, but we write to a memory buffer. + * + * A memory buffer for the compressed image. + */ +typedef struct { + /* Public jpeg fields. + */ + struct jpeg_destination_mgr pub; + + /* Private stuff during write. + */ + JOCTET *data; /* Allocated area */ + int used; /* Number of bytes written so far */ + int size; /* Max size */ + + /* Copy the compressed area here. + */ + IMAGE *out; /* Allocate relative to this */ + char **obuf; /* Allocated buffer, and size */ + int *olen; +} OutputBuffer; + +/* Init dest method. + */ +METHODDEF(void) +init_destination( j_compress_ptr cinfo ) +{ + OutputBuffer *buf = (OutputBuffer *) cinfo->dest; + int mx = cinfo->image_width * cinfo->image_height * + cinfo->input_components * sizeof( JOCTET ); + + /* Allocate relative to the image we are writing .. freed when we junk + * this output. + */ + buf->data = (JOCTET *) (*cinfo->mem->alloc_large) + ( (j_common_ptr) cinfo, JPOOL_IMAGE, mx ); + buf->used = 0; + buf->size = mx; + + /* Set buf pointers for library. + */ + buf->pub.next_output_byte = buf->data; + buf->pub.free_in_buffer = mx; +} + +/* Buffer full method ... should never get this. + */ +METHODDEF(boolean) +empty_output_buffer( j_compress_ptr cinfo ) +{ + /* Not really a file write error, but why not. Should never happen. + */ + ERREXIT( cinfo, JERR_FILE_WRITE ); + + return( 0 ); +} + +/* Cleanup. Write entire buffer as a MIME type. + */ +METHODDEF(void) +term_destination( j_compress_ptr cinfo ) +{ + OutputBuffer *buf = (OutputBuffer *) cinfo->dest; + int len = buf->size - buf->pub.free_in_buffer; + void *obuf; + + /* Allocate and copy to the VIPS output area. + */ + if( !(obuf = im_malloc( buf->out, len )) ) + ERREXIT( cinfo, JERR_FILE_WRITE ); + memcpy( obuf, buf->data, len ); + *(buf->obuf) = obuf; + *(buf->olen) = len; +} + +/* Set dest to one of our objects. + */ +static void +buf_dest( j_compress_ptr cinfo, IMAGE *out, char **obuf, int *olen ) +{ + OutputBuffer *buf; + + /* The destination object is made permanent so that multiple JPEG + * images can be written to the same file without re-executing + * jpeg_stdio_dest. This makes it dangerous to use this manager and + * a different destination manager serially with the same JPEG object, + * because their private object sizes may be different. + * + * Caveat programmer. + */ + if( !cinfo->dest ) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) + ( (j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof( OutputBuffer ) ); + } + + buf = (OutputBuffer *) cinfo->dest; + buf->pub.init_destination = init_destination; + buf->pub.empty_output_buffer = empty_output_buffer; + buf->pub.term_destination = term_destination; + + /* Save output parameters. + */ + buf->out = out; + buf->obuf = obuf; + buf->olen = olen; +} + +/* As above, but save to a buffer. The buffer is allocated relative to out. + * On success, buf is set to the output buffer and len to the size of the + * compressed image. + */ +int +im_vips2bufjpeg( IMAGE *in, IMAGE *out, int qfac, char **obuf, int *olen ) +{ + Write *write; + + if( !(write = write_new( in )) ) + return( -1 ); + + /* Clear output parameters. + */ + *obuf = NULL; + *olen = 0; + + /* Make jpeg compression object. + */ + if( setjmp( write->eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + write_destroy( write ); + + return( -1 ); + } + jpeg_create_compress( &write->cinfo ); + + /* Attach output. + */ + buf_dest( &write->cinfo, out, obuf, olen ); + + /* Convert! + */ + if( write_vips( write, qfac, NULL ) ) { + write_destroy( write ); + + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} + +/* As above, but save as a mime jpeg on stdout. + */ +int +im_vips2mimejpeg( IMAGE *in, int qfac ) +{ + IMAGE *base; + int len; + char *buf; + + if( !(base = im_open( "im_vips2mimejpeg:1", "p" )) ) + return( -1 ); + if( im_vips2bufjpeg( in, base, qfac, &buf, &len ) ) { + im_close( base ); + return( -1 ); + } + + /* Write as a MIME type. + */ + printf( "Content-length: %d\r\n", len ); + printf( "Content-type: image/jpeg\r\n" ); + printf( "\r\n" ); + fwrite( buf, sizeof( char ), len, stdout ); + fflush( stdout ); + + im_close( base ); + + if( ferror( stdout ) ) { + im_error( "im_vips2mimejpeg", _( "error writing output" ) ); + return( -1 ); + } + + return( 0 ); +} + +#endif /*HAVE_JPEG*/ diff --git a/libsrc/conversion/im_vips2mask.c b/libsrc/conversion/im_vips2mask.c new file mode 100644 index 00000000..49c33b0d --- /dev/null +++ b/libsrc/conversion/im_vips2mask.c @@ -0,0 +1,126 @@ +/* @(#) Function to write an IMAGE to a DOUBLEMASK. One band IM_BANDFMT_DOUBLE + * @(#) images, or n-band by 1 pixel double images. + * @(#) + * @(#) DOUBLEMASK * + * @(#) im_vips2mask( IMAGE *in, char *out ) + * @(#) + * @(#) The function returns NULL on error and a new DOUBLEMASK on success + * Author: J.Cupitt + * Written on: 6/6/94 + * Modified on: + * + * 16/10/06 + * - allow 1xn-band images too + * 23/2/07 + * - oop, broken for nx1 m-band images + * - now casts to double for you + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +DOUBLEMASK * +im_vips2mask( IMAGE *in, const char *outname ) +{ + int width, height; + DOUBLEMASK *out; + + /* double* only: cast if necessary. + */ + if( in->BandFmt != IM_BANDFMT_DOUBLE ) { + IMAGE *t; + + if( !(t = im_open( "im_vips2mask", "p" )) ) + return( NULL ); + if( im_clip2d( in, t ) || + !(out = im_vips2mask( t, outname )) ) { + im_close( t ); + return( NULL ); + } + im_close( t ); + + return( out ); + } + + /* Check the image. + */ + if( im_incheck( in ) ) + return( NULL ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_vips2mask", _( "uncoded images only" ) ); + return( NULL ); + } + if( in->Bands == 1 ) { + width = in->Xsize; + height = in->Ysize; + } + else if( in->Xsize == 1 ) { + width = in->Bands; + height = in->Ysize; + } + else if( in->Ysize == 1 ) { + width = in->Xsize; + height = in->Bands; + } + else { + im_error( "im_vips2mask", + _( "one band, nx1, or 1xn images only" ) ); + return( NULL ); + } + + if( !(out = im_create_dmask( outname, width, height )) ) + return( NULL ); + if( in->Bands > 1 && in->Ysize == 1 ) { + double *data = (double *) in->data; + int x, y; + + /* Need to transpose: the image is RGBRGBRGB, we need RRRGGGBBB. + */ + for( y = 0; y < height; y++ ) + for( x = 0; x < width; x++ ) + out->coeff[x + y * width] = + data[x * height + y]; + } + else + memcpy( out->coeff, in->data, + width * height * sizeof( double ) ); + + return( out ); +} + diff --git a/libsrc/conversion/im_vips2png.c b/libsrc/conversion/im_vips2png.c new file mode 100644 index 00000000..20ee37f4 --- /dev/null +++ b/libsrc/conversion/im_vips2png.c @@ -0,0 +1,311 @@ +/* Convert 1 to 4-band 8 or 16-bit VIPS images to/from PNG. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 22/2/05 + * - read non-interlaced PNG with a line buffer (thanks Michel Brabants) + * 11/1/06 + * - read RGBA palette-ized images more robustly (thanks Tom) + * 20/4/06 + * - auto convert to sRGB/mono (with optional alpha) for save + * 1/5/06 + * - from vips_png.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_PNG + +#include + +int +im_vips2png( IMAGE *in, const char *filename ) +{ + im_error( "im_vips2png", _( "PNG support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_PNG*/ + +#include +#include +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#if PNG_LIBPNG_VER < 10003 +#error "PNG library too old." +#endif + +static void +user_error_function( png_structp png_ptr, png_const_charp error_msg ) +{ + im_error( "im_vips2png", _( "PNG error: \"%s\"" ), error_msg ); +} + +static void +user_warning_function( png_structp png_ptr, png_const_charp warning_msg ) +{ + im_error( "im_vips2png", _( "PNG warning: \"%s\"" ), warning_msg ); +} + +/* What we track during a PNG write. + */ +typedef struct { + IMAGE *in; + REGION *reg; + im_threadgroup_t *tg; + + FILE *fp; + png_structp pPng; + png_infop pInfo; + png_bytep *row_pointer; +} Write; + +static void +write_destroy( Write *write ) +{ + IM_FREEF( im_region_free, write->reg ); + IM_FREEF( im_threadgroup_free, write->tg ); + IM_FREEF( im_close, write->in ); + IM_FREEF( fclose, write->fp ); + if( write->pPng ) + png_destroy_write_struct( &write->pPng, &write->pInfo ); + IM_FREE( write->row_pointer ); + + im_free( write ); +} + +static Write * +write_new( IMAGE *in ) +{ + Write *write; + + if( !(write = IM_NEW( NULL, Write )) ) + return( NULL ); + memset( write, 0, sizeof( Write ) ); + + if( !(write->in = im__convert_saveable( in, TRUE )) ) { + im_error( "im_vips2png", + _( "unable to convert to RGB for save" ) ); + write_destroy( write ); + return( NULL ); + } + + write->reg = im_region_create( write->in ); + write->tg = im_threadgroup_create( write->in ); + write->row_pointer = IM_ARRAY( NULL, write->tg->nlines, png_bytep ); + write->fp = NULL; + write->pPng = NULL; + write->pInfo = NULL; + + if( !write->reg || !write->tg || !write->row_pointer ) { + write_destroy( write ); + return( NULL ); + } + + if( !(write->pPng = png_create_write_struct( + PNG_LIBPNG_VER_STRING, NULL, + user_error_function, user_warning_function )) ) { + write_destroy( write ); + return( NULL ); + } + + /* Catch PNG errors from png_create_info_struct(). + */ + if( setjmp( write->pPng->jmpbuf ) ) { + write_destroy( write ); + return( NULL ); + } + + if( !(write->pInfo = png_create_info_struct( write->pPng )) ) { + write_destroy( write ); + return( NULL ); + } + + return( write ); +} + +/* Write a VIPS image to PNG. + */ +static int +write_vips( Write *write, int compress, int interlace ) +{ + IMAGE *in = write->in; + + Rect area; + int j, y, i, nb_passes; + + assert( in->BandFmt == IM_BANDFMT_UCHAR ); + assert( in->Coding == IM_CODING_NONE ); + assert( in->Bands > 0 && in->Bands < 5 ); + + /* Catch PNG errors. + */ + if( setjmp( write->pPng->jmpbuf ) ) + return( -1 ); + + /* Check input image. + */ + if( im_pincheck( in ) ) + return( -1 ); + if( compress < 0 || compress > 9 ) { + im_error( "im_vips2png", _( "compress should be in [0,9]" ) ); + return( -1 ); + } + + /* Set compression parameters. + */ + png_set_compression_level( write->pPng, compress ); + + write->pInfo->width = in->Xsize; + write->pInfo->height = in->Ysize; + write->pInfo->bit_depth = (in->BandFmt == IM_BANDFMT_UCHAR ? 8 : 16); + write->pInfo->gamma = (float) 1.0; + + switch( in->Bands ) { + case 1: write->pInfo->color_type = PNG_COLOR_TYPE_GRAY; break; + case 2: write->pInfo->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; + case 3: write->pInfo->color_type = PNG_COLOR_TYPE_RGB; break; + case 4: write->pInfo->color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; + + default: + assert( 0 ); + } + + png_write_info( write->pPng, write->pInfo ); + + /* If we're an intel byte order CPU and this is a 16bit image, we need + * to swap bytes. + */ + if( write->pInfo->bit_depth > 8 && !im_amiMSBfirst() ) + png_set_swap( write->pPng ); + + if( interlace ) + nb_passes = png_set_interlace_handling( write->pPng ); + else + nb_passes = 1; + + /* Write data. + */ + for( i = 0; i < nb_passes; i++ ) + for( y = 0; y < in->Ysize; y += write->tg->nlines ) { + area.left = 0; + area.top = y; + area.width = in->Xsize; + area.height = IM_MIN( write->tg->nlines, + in->Ysize - y ); + + if( im_prepare_thread( write->tg, write->reg, &area ) ) + return( -1 ); + + for( j = 0; j < area.height; j++ ) + write->row_pointer[j] = (png_bytep) + IM_REGION_ADDR( write->reg, 0, y + j ); + png_write_rows( write->pPng, + write->row_pointer, area.height ); + } + + png_write_end( write->pPng, write->pInfo ); + + return( 0 ); +} + +/* Write a VIPS image to a file as PNG. + */ +int +im_vips2png( IMAGE *in, const char *filename ) +{ + Write *write; + int compress; + int interlace; + + char *p, *q; + + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char buf[FILENAME_MAX]; + + if( !(write = write_new( in )) ) + return( -1 ); + + /* Extract write mode from filename and parse. + */ + im_filename_split( filename, name, mode ); + strcpy( buf, mode ); + p = &buf[0]; + compress = 6; + interlace = 0; + if( (q = im_getnextoption( &p )) ) + compress = atoi( q ); + if( (q = im_getnextoption( &p )) ) + interlace = atoi( q ); + + /* Make output. + */ +#ifdef BINARY_OPEN + if( !(write->fp = fopen( name, "wb" )) ) { +#else /*BINARY_OPEN*/ + if( !(write->fp = fopen( name, "w" )) ) { +#endif /*BINARY_OPEN*/ + write_destroy( write ); + im_error( "im_vips2png", _( "unable to open \"%s\"" ), name ); + + return( -1 ); + } + png_init_io( write->pPng, write->fp ); + + /* Convert it! + */ + if( write_vips( write, compress, interlace ) ) { + write_destroy( write ); + im_error( "im_vips2png", _( "unable to write \"%s\"" ), name ); + + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} + +#endif /*HAVE_PNG*/ diff --git a/libsrc/conversion/im_vips2ppm.c b/libsrc/conversion/im_vips2ppm.c new file mode 100644 index 00000000..da378b16 --- /dev/null +++ b/libsrc/conversion/im_vips2ppm.c @@ -0,0 +1,293 @@ +/* Write a ppm file. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 9/9/05 + * - tiny cleanups + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What we track during a PPM write. + */ +typedef struct { + IMAGE *in; + REGION *reg; + im_threadgroup_t *tg; + FILE *fp; + char *name; +} Write; + +static void +write_destroy( Write *write ) +{ + IM_FREEF( im_threadgroup_free, write->tg ); + IM_FREEF( im_region_free, write->reg ); + IM_FREEF( fclose, write->fp ); + IM_FREE( write->name ); + + im_free( write ); +} + +static Write * +write_new( IMAGE *in, const char *name ) +{ + Write *write; + + if( !(write = IM_NEW( NULL, Write )) ) + return( NULL ); + + write->in = in; + write->reg = im_region_create( write->in ); + write->tg = im_threadgroup_create( write->in ); + write->name = im_strdup( NULL, name ); + +#ifdef BINARY_OPEN + if( !(write->fp = fopen( name, "wb" )) ) { +#else /*BINARY_OPEN*/ + if( !(write->fp = fopen( name, "w" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im_vips2ppm", + _( "unable to open \"%s\" for output" ), name ); + } + + if( !write->reg || !write->tg || !write->name || !write->fp ) { + write_destroy( write ); + return( NULL ); + } + + return( write ); +} + +typedef int (*write_fn)( IMAGE *in, FILE *fp, PEL *p ); + +static int +write_ppm_line_ascii( IMAGE *in, FILE *fp, PEL *p ) +{ + const int sk = IM_IMAGE_SIZEOF_PEL( in ); + const int nb = IM_MIN( 3, in->Bands ); + int x, k; + + /* If IM_CODING_LABQ, write 3 bands. + */ + + for( x = 0; x < in->Xsize; x++ ) { + for( k = 0; k < nb; k++ ) { + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + fprintf( fp, "%d ", p[k] ); + break; + + case IM_BANDFMT_USHORT: + fprintf( fp, "%d ", ((unsigned short *) p)[k] ); + break; + + case IM_BANDFMT_UINT: + fprintf( fp, "%d ", ((unsigned int *) p)[k] ); + break; + + default: + assert( 0 ); + } + } + + fprintf( fp, " " ); + + p += sk; + } + + if( !fprintf( fp, "\n" ) ) { + im_error( "im_vips2ppm", _( "write error ... disc full?" ) ); + return( -1 ); + } + + return( 0 ); +} + +static int +write_ppm_line_binary( IMAGE *in, FILE *fp, PEL *p ) +{ + const int sk = IM_IMAGE_SIZEOF_PEL( in ); + const int nb = IM_MIN( 3, in->Bands ); + int x; + + for( x = 0; x < in->Xsize; x++ ) { + if( !fwrite( p, 1, nb, fp ) ) { + im_error( "im_vips2ppm", + _( "write error ... disc full?" ) ); + return( -1 ); + } + + p += sk; + } + + return( 0 ); +} + +static int +write_ppm( Write *write, int ascii ) +{ + IMAGE *in = write->in; + write_fn fn = ascii ? write_ppm_line_ascii : write_ppm_line_binary; + + int max_value; + char *magic; + time_t timebuf; + int y, i; + Rect area; + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + max_value = UCHAR_MAX; + break; + + case IM_BANDFMT_USHORT: + max_value = USHRT_MAX; + break; + + case IM_BANDFMT_UINT: + max_value = UINT_MAX; + break; + + default: + assert( 0 ); + } + + if( in->Bands == 1 && ascii ) + magic = "P2"; + else if( in->Bands == 1 && !ascii ) + magic = "P5"; + else if( (in->Bands == 3 || in->Bands == 4) && ascii ) + magic = "P3"; + else if( (in->Bands == 3 || in->Bands == 4) && !ascii ) + magic = "P6"; + else + assert( 0 ); + + fprintf( write->fp, "%s\n", magic ); + time( &timebuf ); + fprintf( write->fp, "#im_vips2ppm - %s\n", ctime( &timebuf ) ); + fprintf( write->fp, "%d %d\n", in->Xsize, in->Ysize ); + fprintf( write->fp, "%d\n", max_value ); + + for( y = 0; y < in->Ysize; y += write->tg->nlines ) { + area.left = 0; + area.top = y; + area.width = in->Xsize; + area.height = IM_MIN( write->tg->nlines, in->Ysize - y ); + + if( im_prepare_thread( write->tg, write->reg, &area ) ) + return( -1 ); + + for( i = 0; i < area.height; i++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( write->reg, 0, y + i ); + + if( fn( write->in, write->fp, p ) ) + return( -1 ); + } + } + + return( 0 ); +} + +int +im_vips2ppm( IMAGE *in, const char *filename ) +{ + Write *write; + int ascii; + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + + /* Default to binary output ... much smaller. + */ + ascii = 0; + + /* Extract write mode from filename. + */ + im_filename_split( filename, name, mode ); + if( strcmp( mode, "" ) != 0 ) { + if( im_isprefix( "binary", mode ) ) + ascii = 0; + else if( im_isprefix( "ascii", mode ) ) + ascii = 1; + else { + im_error( "im_vips2ppm", + _( "bad mode string, " + "should be \"binary\" or \"ascii\"" ) ); + return( -1 ); + } + } + + if( in->Bbits > 8 && !ascii ) { + im_error( "im_vips2ppm", + _( "can't write binary >8 bit images" ) ); + return( -1 ); + } + if( !im_isuint( in ) ) { + im_error( "im_vips2ppm", _( "unsigned int formats only" ) ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_vips2ppm", + _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( in->Coding == IM_CODING_NONE && in->Bands != 1 && in->Bands != 3 ) { + im_error( "im_vips2ppm", _( "1 or 3 band images only" ) ); + return( -1 ); + } + + if( im_pincheck( in ) || !(write = write_new( in, name )) ) + return( -1 ); + + if( write_ppm( write, ascii ) ) { + write_destroy( write ); + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} diff --git a/libsrc/conversion/im_vips2tiff.c b/libsrc/conversion/im_vips2tiff.c new file mode 100644 index 00000000..d2111af5 --- /dev/null +++ b/libsrc/conversion/im_vips2tiff.c @@ -0,0 +1,1605 @@ +/* TIFF PARTS: + * Copyright (c) 1988, 1990 by Sam Leffler. + * All rights reserved. + * + * This file is provided for unrestricted use provided that this + * legend is included on all tape media and as a part of the + * software program in whole or part. Users may copy, modify or + * distribute this file at will. + + MODIFICATION FOR VIPS Copyright 1991, K.Martinez + * software may be distributed FREE, with these copyright notices + * no responsibility/warantee is implied or given + * + * + * Modified and added im_LabQ2LabC() function. It can write IM_TYPE_LABQ image + * in vips format to LAB in tiff format. + * Copyright 1994 Ahmed Abbood. + * + * 19/9/95 JC + * - calls TIFFClose() more reliably + * - tidied up + * 12/4/97 JC + * - thrown away and rewritten for TIFF 6 lib + * 22/4/97 JC + * - writes a pyramid! + * - to separate TIFF files tho' + * 23/4/97 JC + * - does 2nd gather pass to put pyramid into a single TIFF file + * - ... and shrinks IM_CODING_LABQ too + * 26/10/98 JC + * - binary open for stupid systems + * 7/6/99 JC + * - 16bit TIFF write too + * 9/7/99 JC + * - ZIP tiff added + * 11/5/00 JC + * - removed TIFFmalloc/TIFFfree + * 5/8/00 JC + * - mode string now part of filename + * 23/4/01 JC + * - HAVE_TIFF turns on TIFFness + * 19/3/02 ruven + * - pyramid stops at tile size, not 64x64 + * 29/4/02 JC + * - write any number of bands (but still with photometric RGB, so not + * very useful) + * 10/9/02 JC + * - oops, handle TIFF errors better + * - now writes CMYK correctly + * 13/2/03 JC + * - tries not to write mad resolutions + * 7/5/03 JC + * - only write CMYK if Type == CMYK + * - writes EXTRASAMPLES ALPHA for bands == 2 or 4 (if we're writing RGB) + * 17/11/03 JC + * - write float too + * 28/11/03 JC + * - read via a "p" so we work from mmap window images + * - uses threadgroups for speedup + * 9/3/04 JC + * - 1 bit write mode added + * 5/4/04 + * - better handling of edge tiles (thanks Ruven) + * 18/5/04 Andrey Kiselev + * - added res_inch/res_cm option + * 20/5/04 JC + * - allow single res number too + * 19/7/04 + * - write several scanlines at once, good speed up for some cases + * 22/9/04 + * - got rid of wrapper image so nip gets progress feedback + * - fixed tiny read-beyond-buffer issue for edge tiles + * 7/10/04 + * - added ICC profile embedding + * 13/12/04 + * - can now pyramid any non-complex type (thanks Ruven) + * 27/1/05 + * - added ccittfax4 as a compression option + * 9/3/05 + * - set PHOTOMETRIC_CIELAB for vips TYPE_LAB images ... so we can write + * float LAB as well as float RGB + * - also LABS images + * 22/6/05 + * - 16 bit LAB write was broken + * 9/9/05 + * - write any icc profile from meta + * 3/3/06 + * - raise tile buffer limit (thanks Ruven) + * 11/11/06 + * - set ORIENTATION_TOPLEFT (thanks Josef) + * 18/7/07 Andrey Kiselev + * - remove "b" option on TIFFOpen() + * - support TIFFTAG_PREDICTOR types for lzw and deflate compression + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on IM_REGION_ADDR() range checks, don't delete intermediates. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifndef HAVE_TIFF + +#include + +int +im_vips2tiff( IMAGE *im, const char *filename ) +{ + im_error( "im_vips2tiff", _( "TIFF support disabled" ) ); + + return( -1 ); +} + +#else /*HAVE_TIFF*/ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Max no of tiles we buffer in a layer. Enough to buffer a line of 64x64 + * tiles on a 100k pixel across image. + */ +#define IM_MAX_LAYER_BUFFER (1000) + +/* Bits we OR together for quadrants in a tile. + */ +typedef enum pyramid_bits { + PYR_TL = 1, /* Top-left etc. */ + PYR_TR = 2, + PYR_BL = 4, + PYR_BR = 8, + PYR_ALL = 15, + PYR_NONE = 0 +} PyramidBits; + +/* A tile in our pyramid. + */ +typedef struct pyramid_tile { + REGION *tile; + PyramidBits bits; +} PyramidTile; + +/* A layer in the pyramid. + */ +typedef struct pyramid_layer { + /* Parameters. + */ + struct tiff_write *tw; /* Main TIFF write struct */ + int width, height; /* Layer size */ + int sub; /* Subsample factor for this layer */ + + char *lname; /* Name of this TIFF file */ + TIFF *tif; /* TIFF file we write this layer to */ + PEL *tbuf; /* TIFF output buffer */ + PyramidTile tiles[IM_MAX_LAYER_BUFFER]; + + struct pyramid_layer *below; /* Tiles go to here */ + struct pyramid_layer *above; /* Tiles come from here */ +} PyramidLayer; + +/* A TIFF image in the process of being written. + */ +typedef struct tiff_write { + IMAGE *im; /* Original input image */ + char *name; /* Final name we write to */ + char *mode; /* Mode string */ + + /* Read from im with these. + */ + REGION *reg; + im_threadgroup_t *tg; + + char *bname; /* Name for base layer */ + TIFF *tif; /* Image we write to */ + + PyramidLayer *layer; /* Top of pyramid, if in use */ + PEL *tbuf; /* TIFF output buffer */ + int tls; /* Tile line size */ + + int compression; /* Compression type */ + int jpqual; /* JPEG q-factor */ + int predictor; /* Predictor value */ + int tile; /* Tile or not */ + int tilew, tileh; /* Tile size */ + int pyramid; /* Write pyramid */ + int onebit; /* Write as 1-bit TIFF */ + int resunit; /* Resolution unit (inches or cm) */ + float xres; /* Resolution in X */ + float yres; /* Resolution in Y */ + int embed; /* Embed ICC profile */ + char *icc_profile; /* Profile to embed */ +} TiffWrite; + +/* Handle TIFF errors here. + */ +static void +vhandle( char *module, char *fmt, va_list ap ) +{ + im_error( "im_vips2tiff", _( "TIFF error in \"%s\": " ), module ); + im_verror( "im_vips2tiff", fmt, ap ); +} + +/* Open TIFF for output. + */ +static TIFF * +tiff_openout( const char *name ) +{ + TIFF *tif; + + if( !(tif = TIFFOpen( name, "w" )) ) { + im_error( "im_vips2tiff", + _( "unable to open \"%s\" for output" ), name ); + return( NULL ); + } + + return( tif ); +} + +/* Open TIFF for input. + */ +static TIFF * +tiff_openin( const char *name ) +{ + TIFF *tif; + + if( !(tif = TIFFOpen( name, "r" )) ) { + im_error( "im_vips2tiff", + _( "unable to open \"%s\" for input" ), name ); + return( NULL ); + } + + return( tif ); +} + +/* Convert VIPS LabQ to TIFF LAB. Just take the first three bands. + */ +static void +LabQ2LabC( PEL *q, PEL *p, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + /* Get most significant 8 bits of lab. + */ + q[0] = p[0]; + q[1] = p[1]; + q[2] = p[2]; + + p += 4; + q += 3; + } +} + +/* Pack 8 bit VIPS to 1 bit TIFF. + */ +static void +eightbit2onebit( PEL *q, PEL *p, int n ) +{ + int x; + PEL bits; + + bits = 0; + for( x = 0; x < n; x++ ) { + bits <<= 1; + if( p[x] ) + bits |= 1; + + if( (x & 0x7) == 0x7 ) { + *q++ = bits; + bits = 0; + } + } + + /* Any left-over bits? Need to be left-aligned. + */ + if( (x & 0x7) != 0 ) + *q++ = bits << (8 - (x & 0x7)); +} + +/* Convert VIPS LABS to TIFF 16 bit LAB. + */ +static void +LabS2Lab16( PEL *q, PEL *p, int n ) +{ + int x; + short *p1 = (short *) p; + unsigned short *q1 = (unsigned short *) q; + + for( x = 0; x < n; x++ ) { + /* TIFF uses unsigned 16 bit ... move zero, scale up L. + */ + q1[0] = (int) p1[0] << 1; + q1[1] = p1[1]; + q1[2] = p1[2]; + + p1 += 3; + q1 += 3; + } +} + +/* Pack a VIPS region into a TIFF tile buffer. + */ +static void +pack2tiff( TiffWrite *tw, REGION *in, PEL *q, Rect *area ) +{ + int y; + + for( y = area->top; y < IM_RECT_BOTTOM( area ); y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( in, area->left, y ); + + if( in->im->Coding == IM_CODING_LABQ ) + LabQ2LabC( q, p, area->width ); + else if( tw->onebit ) + eightbit2onebit( q, p, area->width ); + else if( in->im->BandFmt == IM_BANDFMT_SHORT && + in->im->Type == IM_TYPE_LABS ) + LabS2Lab16( q, p, area->width ); + else + memcpy( q, p, + area->width * IM_IMAGE_SIZEOF_PEL( in->im ) ); + + q += tw->tls; + } +} + +/* Embed an ICC profile. + */ +static int +embed_profile( TIFF *tif, const char *profile ) +{ + char *buffer; + unsigned int length; + + if( !(buffer = im__file_read_name( profile, &length )) ) + return( -1 ); + TIFFSetField( tif, TIFFTAG_ICCPROFILE, length, buffer ); + im_free( buffer ); + +#ifdef DEBUG + printf( "im_vips2tiff: attached profile \"%s\"\n", profile ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Embed an ICC profile from IMAGE metadata. + */ +static int +embed_profile_meta( TIFF *tif, IMAGE *im ) +{ + void *data; + size_t data_length; + + if( im_meta_get_blob( im, IM_META_ICC_NAME, &data, &data_length ) ) + return( -1 ); + TIFFSetField( tif, TIFFTAG_ICCPROFILE, data_length, data ); + +#ifdef DEBUG + printf( "im_vips2tiff: attached profile from meta\n" ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Write a TIFF header. width and height are the size of the IMAGE we are + * writing (may have been shrunk!). + */ +static int +write_tiff_header( TiffWrite *tw, TIFF *tif, int width, int height ) +{ + uint16 v[1]; + + /* Output base header fields. + */ + TIFFSetField( tif, TIFFTAG_IMAGEWIDTH, width ); + TIFFSetField( tif, TIFFTAG_IMAGELENGTH, height ); + TIFFSetField( tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); + TIFFSetField( tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT ); + TIFFSetField( tif, TIFFTAG_COMPRESSION, tw->compression ); + + /* Don't write mad resolutions (eg. zero), it confuses some programs. + */ + TIFFSetField( tif, TIFFTAG_RESOLUTIONUNIT, tw->resunit ); + TIFFSetField( tif, TIFFTAG_XRESOLUTION, + IM_CLIP( 0.01, tw->xres, 10000 ) ); + TIFFSetField( tif, TIFFTAG_YRESOLUTION, + IM_CLIP( 0.01, tw->yres, 10000 ) ); + + if( tw->compression == COMPRESSION_JPEG ) { + TIFFSetField( tif, TIFFTAG_JPEGQUALITY, tw->jpqual ); + TIFFSetField( tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB ); + } + + if( tw->predictor != -1 ) + TIFFSetField( tif, TIFFTAG_PREDICTOR, tw->predictor ); + + /* Attach ICC profile. + */ + if( tw->embed && embed_profile( tif, tw->icc_profile ) ) + return( -1 ); + if( !tw->embed && im_header_get_type( tw->im, IM_META_ICC_NAME ) && + embed_profile_meta( tif, tw->im ) ) + return( -1 ); + + /* And colour fields. + */ + if( tw->im->Coding == IM_CODING_LABQ ) { + TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, 3 ); + TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, 8 ); + TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB ); + } + else if( tw->onebit ) { + TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, 1 ); + TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, 1 ); + TIFFSetField( tif, + TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + } + else { + int photometric; + + TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, tw->im->Bands ); + TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, tw->im->Bbits ); + + switch( tw->im->Bands ) { + case 1: + case 2: + photometric = PHOTOMETRIC_MINISBLACK; + if( tw->im->Bands == 2 ) { + v[0] = EXTRASAMPLE_ASSOCALPHA; + TIFFSetField( tif, TIFFTAG_EXTRASAMPLES, 1, v ); + } + break; + + case 3: + case 4: + if( tw->im->Type == IM_TYPE_LAB || + tw->im->Type == IM_TYPE_LABS ) + photometric = PHOTOMETRIC_CIELAB; + else if( tw->im->Type == IM_TYPE_CMYK ) { + photometric = PHOTOMETRIC_SEPARATED; + TIFFSetField( tif, + TIFFTAG_INKSET, INKSET_CMYK ); + } + else + photometric = PHOTOMETRIC_RGB; + + if( tw->im->Type != IM_TYPE_CMYK && + tw->im->Bands == 4 ) { + v[0] = EXTRASAMPLE_ASSOCALPHA; + TIFFSetField( tif, TIFFTAG_EXTRASAMPLES, 1, v ); + } + break; + + default: + assert( 0 ); + } + + TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, photometric ); + } + + /* Layout. + */ + if( tw->tile ) { + TIFFSetField( tif, TIFFTAG_TILEWIDTH, tw->tilew ); + TIFFSetField( tif, TIFFTAG_TILELENGTH, tw->tileh ); + } + else + TIFFSetField( tif, TIFFTAG_ROWSPERSTRIP, 16 ); + + /* Sample format ... for float, we write IEEE. + */ + if( tw->im->BandFmt == IM_BANDFMT_FLOAT ) + TIFFSetField( tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP ); + + return( 0 ); +} + +/* Free a pyramid layer. + */ +static void +free_layer( PyramidLayer *layer ) +{ + int i; + + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) + if( layer->tiles[i].tile ) { + im_region_free( layer->tiles[i].tile ); + layer->tiles[i].tile = NULL; + } + + /* And close the TIFF file we are writing to. + */ + if( layer->tbuf ) { + im_free( layer->tbuf ); + layer->tbuf = NULL; + } + if( layer->tif ) { + TIFFClose( layer->tif ); + layer->tif = NULL; + } +} + +/* Free an entire pyramid. + */ +static void +free_pyramid( PyramidLayer *layer ) +{ + if( layer->below ) + free_pyramid( layer->below ); + + free_layer( layer ); +} + +/* Make a name for a new TIFF layer. Base it on sub factor. + */ +static char * +new_tiff_name( TiffWrite *tw, char *name, int sub ) +{ + char buf[FILENAME_MAX]; + char buf2[FILENAME_MAX]; + + /* Remove existing .tif/.tiff suffix, if any. + */ + strcpy( buf, name ); + if( im_ispostfix( buf, ".tif" ) ) + buf[strlen( buf ) - 4] = '\0'; + if( im_ispostfix( buf, ".tiff" ) ) + buf[strlen( buf ) - 5] = '\0'; + + im_snprintf( buf2, FILENAME_MAX, "%s.%d.tif", buf, sub ); + + return( im_strdup( tw->im, buf2 ) ); +} + +/* Build a pyramid. w & h are size of layer above this layer. Write new layer + * struct into *zap, return 0/-1 for success/fail. + */ +static int +build_pyramid( TiffWrite *tw, PyramidLayer *above, + PyramidLayer **zap, int w, int h ) +{ + PyramidLayer *layer = IM_NEW( tw->im, PyramidLayer ); + int i; + + if( !layer ) + return( -1 ); + layer->tw = tw; + layer->width = w / 2; + layer->height = h / 2; + + if( !above ) + /* Top of pyramid. + */ + layer->sub = 2; + else + layer->sub = above->sub * 2; + + layer->lname = NULL; + layer->tif = NULL; + layer->tbuf = NULL; + + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) { + layer->tiles[i].tile = NULL; + layer->tiles[i].bits = PYR_NONE; + } + + layer->below = NULL; + layer->above = above; + + /* Save layer, to make sure it gets freed properly. + */ + *zap = layer; + + if( layer->width > tw->tilew || layer->height > tw->tileh ) + if( build_pyramid( tw, layer, + &layer->below, layer->width, layer->height ) ) + return( -1 ); + + if( !(layer->lname = new_tiff_name( tw, tw->name, layer->sub )) ) + return( -1 ); + + /* Make output image. + */ + if( !(layer->tif = tiff_openout( layer->lname )) ) + return( -1 ); + + /* Write the TIFF header for this layer. + */ + if( write_tiff_header( tw, layer->tif, layer->width, layer->height ) ) + return( -1 ); + + if( !(layer->tbuf = im_malloc( NULL, TIFFTileSize( layer->tif ) )) ) + return( -1 ); + + return( 0 ); +} + +/* Pick a new tile to write to in this layer. Either reuse a tile we have + * previously filled, or make a new one. + */ +static int +find_new_tile( PyramidLayer *layer ) +{ + int i; + + /* Exisiting buffer we have finished with? + */ + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) + if( layer->tiles[i].bits == PYR_ALL ) + return( i ); + + /* Have to make a new one. + */ + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) + if( !layer->tiles[i].tile ) { + if( !(layer->tiles[i].tile = + im_region_create( layer->tw->im )) ) + return( -1 ); + return( i ); + } + + /* Out of space! + */ + im_error( "im_vips2tiff", _( "layer buffer exhausted -- " + "try making TIFF output tiles smaller" ) ); + + return( -1 ); +} + +/* Find a tile in the layer buffer - if it's not there, make a new one. + */ +static int +find_tile( PyramidLayer *layer, Rect *pos ) +{ + int i; + Rect quad; + Rect image; + Rect inter; + + /* Do we have a REGION for this position? + */ + for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) { + REGION *reg = layer->tiles[i].tile; + + if( reg && reg->valid.left == pos->left && + reg->valid.top == pos->top ) + return( i ); + } + + /* Make a new one. + */ + if( (i = find_new_tile( layer )) < 0 ) + return( -1 ); + if( im_region_buffer( layer->tiles[i].tile, pos ) ) + return( -1 ); + layer->tiles[i].bits = PYR_NONE; + + /* Do any quadrants of this tile fall entirely outside the image? + * If they do, set their bits now. + */ + quad.width = layer->tw->tilew / 2; + quad.height = layer->tw->tileh / 2; + image.left = 0; + image.top = 0; + image.width = layer->width; + image.height = layer->height; + + quad.left = pos->left; + quad.top = pos->top; + im_rect_intersectrect( &quad, &image, &inter ); + if( im_rect_isempty( &inter ) ) + layer->tiles[i].bits |= PYR_TL; + + quad.left = pos->left + quad.width; + quad.top = pos->top; + im_rect_intersectrect( &quad, &image, &inter ); + if( im_rect_isempty( &inter ) ) + layer->tiles[i].bits |= PYR_TR; + + quad.left = pos->left; + quad.top = pos->top + quad.height; + im_rect_intersectrect( &quad, &image, &inter ); + if( im_rect_isempty( &inter ) ) + layer->tiles[i].bits |= PYR_BL; + + quad.left = pos->left + quad.width; + quad.top = pos->top + quad.height; + im_rect_intersectrect( &quad, &image, &inter ); + if( im_rect_isempty( &inter ) ) + layer->tiles[i].bits |= PYR_BR; + + return( i ); +} + +/* Shrink a region by a factor of two, writing the result to a specified + * offset in another region. IM_CODING_LABQ only. + */ +static void +shrink_region_labpack( REGION *from, Rect *area, + REGION *to, int xoff, int yoff ) +{ + int ls = IM_REGION_LSKIP( from ); + Rect *t = &to->valid; + + int x, y; + Rect out; + + /* Calculate output size and position. + */ + out.left = t->left + xoff; + out.top = t->top + yoff; + out.width = area->width / 2; + out.height = area->height / 2; + + /* Shrink ... ignore the extension byte for speed. + */ + for( y = 0; y < out.height; y++ ) { + PEL *p = (PEL *) + IM_REGION_ADDR( from, area->left, area->top + y * 2 ); + PEL *q = (PEL *) + IM_REGION_ADDR( to, out.left, out.top + y ); + + for( x = 0; x < out.width; x++ ) { + signed char *sp = (signed char *) p; + unsigned char *up = (unsigned char *) p; + + int l = up[0] + up[4] + + up[ls] + up[ls + 4]; + int a = sp[1] + sp[5] + + sp[ls + 1] + sp[ls + 5]; + int b = sp[2] + sp[6] + + sp[ls + 2] + sp[ls + 6]; + + q[0] = l >> 2; + q[1] = a >> 2; + q[2] = b >> 2; + q[3] = 0; + + q += 4; + p += 8; + } + } +} + +#define SHRINK_TYPE_INT( TYPE ) \ + for( x = 0; x < out.width; x++ ) { \ + TYPE *tp = (TYPE *) p; \ + TYPE *tp1 = (TYPE *) (p + ls); \ + TYPE *tq = (TYPE *) q; \ + \ + for( z = 0; z < nb; z++ ) { \ + int tot = tp[z] + tp[z + nb] + \ + tp1[z] + tp1[z + nb]; \ + \ + tq[z] = tot >> 2; \ + } \ + \ + /* Move on two pels in input. \ + */ \ + p += ps << 1; \ + q += ps; \ + } + +#define SHRINK_TYPE_FLOAT( TYPE ) \ + for( x = 0; x < out.width; x++ ) { \ + TYPE *tp = (TYPE *) p; \ + TYPE *tp1 = (TYPE *) (p + ls); \ + TYPE *tq = (TYPE *) q; \ + \ + for( z = 0; z < nb; z++ ) { \ + double tot = (double) tp[z] + tp[z + nb] + \ + tp1[z] + tp1[z + nb]; \ + \ + tq[z] = tot / 4; \ + } \ + \ + /* Move on two pels in input. \ + */ \ + p += ps << 1; \ + q += ps; \ + } + +/* Shrink a region by a factor of two, writing the result to a specified + * offset in another region. n-band, non-complex. + */ +static void +shrink_region( REGION *from, Rect *area, + REGION *to, int xoff, int yoff ) +{ + int ls = IM_REGION_LSKIP( from ); + int ps = IM_IMAGE_SIZEOF_PEL( from->im ); + int nb = from->im->Bands; + Rect *t = &to->valid; + + int x, y, z; + Rect out; + + /* Calculate output size and position. + */ + out.left = t->left + xoff; + out.top = t->top + yoff; + out.width = area->width / 2; + out.height = area->height / 2; + + for( y = 0; y < out.height; y++ ) { + PEL *p = (PEL *) + IM_REGION_ADDR( from, area->left, area->top + y * 2 ); + PEL *q = (PEL *) + IM_REGION_ADDR( to, out.left, out.top + y ); + + /* Process this line of pels. + */ + switch( from->im->BandFmt ) { + case IM_BANDFMT_UCHAR: + SHRINK_TYPE_INT( unsigned char ); break; + case IM_BANDFMT_CHAR: + SHRINK_TYPE_INT( signed char ); break; + case IM_BANDFMT_USHORT: + SHRINK_TYPE_INT( unsigned short ); break; + case IM_BANDFMT_SHORT: + SHRINK_TYPE_INT( signed short ); break; + case IM_BANDFMT_UINT: + SHRINK_TYPE_INT( unsigned int ); break; + case IM_BANDFMT_INT: + SHRINK_TYPE_INT( signed int ); break; + case IM_BANDFMT_FLOAT: + SHRINK_TYPE_FLOAT( float ); break; + case IM_BANDFMT_DOUBLE: + SHRINK_TYPE_FLOAT( double ); break; + + default: + assert( 0 ); + } + } +} + +/* Write a tile from a layer. + */ +static int +save_tile( TiffWrite *tw, TIFF *tif, PEL *tbuf, REGION *reg, Rect *area ) +{ + /* Have to repack pixels. + */ + pack2tiff( tw, reg, tbuf, area ); + +#ifdef DEBUG + printf( "Writing %dx%d pixels at position %dx%d to image %s\n", + tw->tilew, tw->tileh, area->left, area->top, + TIFFFileName( tif ) ); +#endif /*DEBUG*/ + + /* Write to TIFF! easy. + */ + if( TIFFWriteTile( tif, tbuf, area->left, area->top, 0, 0 ) < 0 ) { + im_error( "im_vips2tiff", _( "TIFF write tile failed" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* A new tile has arrived! Shrink into this layer, if we fill a region, write + * it and recurse. + */ +static int +new_tile( PyramidLayer *layer, REGION *tile, Rect *area ) +{ + TiffWrite *tw = layer->tw; + int xoff, yoff; + + int t, ri, bo; + Rect out, new; + PyramidBits bit; + + /* Calculate pos and size of new pixels we make inside this layer. + */ + new.left = area->left / 2; + new.top = area->top / 2; + new.width = area->width / 2; + new.height = area->height / 2; + + /* Has size fallen to zero? Can happen if this is a one-pixel-wide + * strip. + */ + if( im_rect_isempty( &new ) ) + return( 0 ); + + /* Offset into this tile ... ie. which quadrant we are writing. + */ + xoff = new.left % layer->tw->tilew; + yoff = new.top % layer->tw->tileh; + + /* Calculate pos for tile we shrink into in this layer. + */ + out.left = new.left - xoff; + out.top = new.top - yoff; + + /* Clip against edge of image. + */ + ri = IM_MIN( layer->width, out.left + layer->tw->tilew ); + bo = IM_MIN( layer->height, out.top + layer->tw->tileh ); + out.width = ri - out.left; + out.height = bo - out.top; + + if( (t = find_tile( layer, &out )) < 0 ) + return( -1 ); + + /* Shrink into place. + */ + if( tw->im->Coding == IM_CODING_NONE ) + shrink_region( tile, area, + layer->tiles[t].tile, xoff, yoff ); + else + shrink_region_labpack( tile, area, + layer->tiles[t].tile, xoff, yoff ); + + /* Set that bit. + */ + if( xoff ) + if( yoff ) + bit = PYR_BR; + else + bit = PYR_TR; + else + if( yoff ) + bit = PYR_BL; + else + bit = PYR_TL; + if( layer->tiles[t].bits & bit ) { + im_error( "im_vips2tiff", _( "internal error #9876345" ) ); + return( -1 ); + } + layer->tiles[t].bits |= bit; + + if( layer->tiles[t].bits == PYR_ALL ) { + /* Save this complete tile. + */ + if( save_tile( tw, layer->tif, layer->tbuf, + layer->tiles[t].tile, &layer->tiles[t].tile->valid ) ) + return( -1 ); + + /* And recurse down the pyramid! + */ + if( layer->below && + new_tile( layer->below, + layer->tiles[t].tile, + &layer->tiles[t].tile->valid ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Write as tiles. + */ +static int +write_tif_tile( TiffWrite *tw ) +{ + IMAGE *im = tw->im; + Rect area; + int x, y; + + if( !(tw->tbuf = im_malloc( NULL, TIFFTileSize( tw->tif ) )) ) + return( -1 ); + + /* Write pyramid too? Only bother if bigger than tile size. + */ + if( tw->pyramid && + (im->Xsize > tw->tilew || im->Ysize > tw->tileh) && + build_pyramid( tw, NULL, &tw->layer, im->Xsize, im->Ysize ) ) + return( -1 ); + + for( y = 0; y < im->Ysize; y += tw->tileh ) + for( x = 0; x < im->Xsize; x += tw->tilew ) { + /* Set up rect we write. + */ + area.left = x; + area.top = y; + area.width = IM_MIN( tw->tilew, im->Xsize - x ); + area.height = IM_MIN( tw->tileh, im->Ysize - y ); + + if( im_prepare_thread( tw->tg, tw->reg, &area ) ) + return( -1 ); + + /* Write to TIFF. + */ + if( save_tile( tw, tw->tif, tw->tbuf, tw->reg, &area ) ) + return( -1 ); + + /* Is there a pyramid? Write to that too. + */ + if( tw->layer && new_tile( tw->layer, tw->reg, &area ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Write as scan-lines. + */ +static int +write_tif_strip( TiffWrite *tw ) +{ + IMAGE *im = tw->im; + Rect area; + int y, y2; + + if( !(tw->tbuf = im_malloc( NULL, TIFFScanlineSize( tw->tif ) )) ) + return( -1 ); + + /* Set up rect we ask for. + */ + area.left = 0; + area.width = im->Xsize; + + for( y = 0; y < im->Ysize; y += tw->tg->nlines ) { + area.top = y; + area.height = IM_MIN( tw->tg->nlines, im->Ysize - y ); + if( im_prepare_thread( tw->tg, tw->reg, &area ) ) + return( -1 ); + + for( y2 = 0; y2 < area.height; y2++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( tw->reg, 0, y + y2 ); + + /* Any repacking necessary. + */ + if( im->Coding == IM_CODING_LABQ ) { + LabQ2LabC( tw->tbuf, p, im->Xsize ); + p = tw->tbuf; + } + else if( im->BandFmt == IM_BANDFMT_SHORT && + im->Type == IM_TYPE_LABS ) { + LabS2Lab16( tw->tbuf, p, im->Xsize ); + p = tw->tbuf; + } + else if( tw->onebit ) { + eightbit2onebit( tw->tbuf, p, im->Xsize ); + p = tw->tbuf; + } + + /* Write to TIFF! easy. + */ + if( TIFFWriteScanline( tw->tif, p, y + y2, 0 ) < 0 ) { + im_error( "im_vips2tiff", + _( "TIFF write failed" ) ); + return( -1 ); + } + } + } + + return( 0 ); +} + +/* Delete any temp files we wrote. + */ +static void +delete_files( TiffWrite *tw ) +{ + PyramidLayer *layer = tw->layer; + + if( tw->bname ) { + unlink( tw->bname ); + tw->bname = NULL; + } + + for( layer = tw->layer; layer; layer = layer->below ) + if( layer->lname ) { + unlink( layer->lname ); + layer->lname = NULL; + } +} + +/* Free a TiffWrite. + */ +static void +free_tiff_write( TiffWrite *tw ) +{ +#ifndef DEBUG + delete_files( tw ); +#endif /*DEBUG*/ + + if( tw->tg ) { + im_threadgroup_free( tw->tg ); + tw->tg = NULL; + } + if( tw->reg ) { + im_region_free( tw->reg ); + tw->reg = NULL; + } + if( tw->tif ) { + TIFFClose( tw->tif ); + tw->tif = NULL; + } + if( tw->tbuf ) { + im_free( tw->tbuf ); + tw->tbuf = NULL; + } + if( tw->layer ) + free_pyramid( tw->layer ); + if( tw->icc_profile ) { + im_free( tw->icc_profile ); + tw->icc_profile = NULL; + } +} + +/* Round N down to P boundary. + */ +#define ROUND_DOWN(N,P) ((N) - ((N) % P)) + +/* Round N up to P boundary. + */ +#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) )) + +/* Make and init a TiffWrite. + */ +static TiffWrite * +make_tiff_write( IMAGE *im, const char *filename ) +{ + TiffWrite *tw; + char *p, *q, *r; + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char buf[FILENAME_MAX]; + + if( !(tw = IM_NEW( im, TiffWrite )) ) + return( NULL ); + tw->im = im; + im_filename_split( filename, name, mode ); + tw->name = im_strdup( im, name ); + tw->mode = im_strdup( im, mode ); + tw->tg = NULL; + tw->reg = NULL; + tw->bname = NULL; + tw->tif = NULL; + tw->layer = NULL; + tw->tbuf = NULL; + tw->compression = COMPRESSION_NONE; + tw->jpqual = 75; + tw->predictor = -1; + tw->tile = 0; + tw->tilew = 128; + tw->tileh = 128; + tw->pyramid = 0; + tw->onebit = 0; + tw->embed = 0; + tw->icc_profile = NULL; + + /* Output resolution settings default to VIPS-alike. + */ + tw->resunit = RESUNIT_CENTIMETER; + tw->xres = im->Xres * 10; + tw->yres = im->Yres * 10; + + /* Parse mode string. + */ + strcpy( buf, mode ); + p = &buf[0]; + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "none", q ) ) + tw->compression = COMPRESSION_NONE; + else if( im_isprefix( "packbits", q ) ) + tw->compression = COMPRESSION_PACKBITS; + else if( im_isprefix( "ccittfax4", q ) ) + tw->compression = COMPRESSION_CCITTFAX4; + else if( im_isprefix( "lzw", q ) ) { + tw->compression = COMPRESSION_LZW; + + if( (r = im_getsuboption( q )) ) + if( sscanf( r, "%d", &tw->predictor ) != 1 ) { + im_error( "im_vips2tiff", + _( "bad predictor " + "parameter" ) ); + return( NULL ); + } + } + else if( im_isprefix( "deflate", q ) ) { + tw->compression = COMPRESSION_ADOBE_DEFLATE; + + if( (r = im_getsuboption( q )) ) + if( sscanf( r, "%d", &tw->predictor ) != 1 ) { + im_error( "im_vips2tiff", + _( "bad predictor " + "parameter" ) ); + return( NULL ); + } + } + else if( im_isprefix( "jpeg", q ) ) { + tw->compression = COMPRESSION_JPEG; + + if( (r = im_getsuboption( q )) ) + if( sscanf( r, "%d", &tw->jpqual ) != 1 ) { + im_error( "im_vips2tiff", + _( "bad JPEG quality " + "parameter" ) ); + return( NULL ); + } + } + else { + im_error( "im_vips2tiff", _( "unknown compression mode " + "\"%s\"\nshould be one of \"none\", " + "\"packbits\", \"ccittfax4\", \"lzw\", " + "\"deflate\" or \"jpeg\"" ), q ); + return( NULL ); + } + } + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "tile", q ) ) { + tw->tile = 1; + + if( (r = im_getsuboption( q )) ) { + if( sscanf( r, "%dx%d", + &tw->tilew, &tw->tileh ) != 2 ) { + im_error( "im_vips2tiff", _( "bad tile " + "sizes" ) ); + return( NULL ); + } + + if( tw->tilew < 10 || tw->tileh < 10 || + tw->tilew > 1000 || tw->tileh > 1000 ) { + im_error( "im_vips2tiff", _( "bad tile " + "size %dx%d" ), + tw->tilew, tw->tileh ); + return( NULL ); + } + + if( (tw->tilew & 0xf) != 0 || + (tw->tileh & 0xf) != 0 ) { + im_error( "im_vips2tiff", _( "tile " + "size not a multiple of 16" ) ); + return( NULL ); + } + } + } + else if( im_isprefix( "strip", q ) ) + tw->tile = 0; + else { + im_error( "im_vips2tiff", _( "unknown layout mode " + "\"%s\"\nshould be one of \"tile\" or " + "\"strip\"" ), q ); + return( NULL ); + } + } + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "pyramid", q ) ) + tw->pyramid = 1; + else if( im_isprefix( "flat", q ) ) + tw->pyramid = 0; + else { + im_error( "im_vips2tiff", _( "unknown multi-res mode " + "\"%s\"\nshould be one of \"flat\" or " + "\"pyramid\"" ), q ); + return( NULL ); + } + } + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "onebit", q ) ) + tw->onebit = 1; + else if( im_isprefix( "manybit", q ) ) + tw->onebit = 0; + else { + im_error( "im_vips2tiff", _( "unknown format " + "\"%s\"\nshould be one of \"onebit\" or " + "\"manybit\"" ), q ); + return( NULL ); + } + } + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "res_cm", q ) ) + tw->resunit = RESUNIT_CENTIMETER; + else if( im_isprefix( "res_inch", q ) ) + tw->resunit = RESUNIT_INCH; + else { + im_error( "im_vips2tiff", _( "unknown resolution unit " + "\"%s\"\nshould be one of \"res_cm\" or " + "\"res_inch\"" ), q ); + return( NULL ); + } + + if( (r = im_getsuboption( q )) ) { + if( sscanf( r, "%fx%f", &tw->xres, &tw->yres ) != 2 ) { + if( sscanf( r, "%f", &tw->xres ) != 1 ) { + im_error( "im_vips2tiff", _( "bad " + "resolution values" ) ); + return( NULL ); + } + + tw->yres = tw->xres; + } + } + } + if( (q = im_getnextoption( &p )) && strcmp( q, "" ) != 0 ) { + tw->embed = 1; + tw->icc_profile = im_strdup( NULL, q ); + } + if( (q = im_getnextoption( &p )) ) { + im_error( "im_vips2tiff", + _( "unknown extra options \"%s\"" ), q ); + return( NULL ); + } + if( !tw->tile && tw->pyramid ) { + im_warn( "im_vips2tiff", _( "can't have strip pyramid -- " + "enabling tiling" ) ); + tw->tile = 1; + } + + /* We can only pyramid LABQ and non-complex images. + */ + if( tw->pyramid ) { + if( im->Coding == IM_CODING_NONE && im_iscomplex( im ) ) { + im_error( "im_vips2tiff", + _( "can only pyramid LABQ and " + "non-complex images" ) ); + return( NULL ); + } + } + + /* Only 1-bit-ize 8 bit mono images. + */ + if( tw->onebit ) { + if( im->Coding != IM_CODING_NONE || + im->BandFmt != IM_BANDFMT_UCHAR || + im->Bands != 1 ) + tw->onebit = 0; + } + + if( tw->onebit && tw->compression == COMPRESSION_JPEG ) { + im_warn( "im_vips2tiff", _( "can't have 1-bit JPEG -- " + "disabling JPEG" ) ); + tw->compression = COMPRESSION_NONE; + } + + /* Make region and threadgroup. + */ + if( !(tw->reg = im_region_create( tw->im )) || + !(tw->tg = im_threadgroup_create( tw->im )) ) { + free_tiff_write( tw ); + return( NULL ); + } + + /* Sizeof a line of bytes in the TIFF tile. + */ + if( im->Coding == IM_CODING_LABQ ) + tw->tls = tw->tilew * 3; + else if( tw->onebit ) + tw->tls = ROUND_UP( tw->tilew, 8 ) / 8; + else + tw->tls = IM_IMAGE_SIZEOF_PEL( im ) * tw->tilew; + + return( tw ); +} + +/* Copy fields. + */ +#define CopyField( tag, v ) \ + if( TIFFGetField( in, tag, &v ) ) TIFFSetField( out, tag, v ) + +/* Copy a TIFF file ... we know we wrote it, so just copy the tags we know + * we might have set. + */ +static int +tiff_copy( TIFF *out, TIFF *in ) +{ + uint32 i32, i32a; + uint16 i16; + int i; + float f; + tdata_t buf; + ttile_t tile; + ttile_t n; + + /* All the fields we might have set. + */ + CopyField( TIFFTAG_IMAGEWIDTH, i32 ); + CopyField( TIFFTAG_IMAGELENGTH, i32 ); + CopyField( TIFFTAG_PLANARCONFIG, i16 ); + CopyField( TIFFTAG_ORIENTATION, i16 ); + CopyField( TIFFTAG_COMPRESSION, i16 ); + CopyField( TIFFTAG_XRESOLUTION, f ); + CopyField( TIFFTAG_YRESOLUTION, f ); + CopyField( TIFFTAG_RESOLUTIONUNIT, i16 ); + CopyField( TIFFTAG_JPEGQUALITY, i ); + CopyField( TIFFTAG_PREDICTOR, i16 ); + CopyField( TIFFTAG_SAMPLESPERPIXEL, i16 ); + CopyField( TIFFTAG_BITSPERSAMPLE, i16 ); + CopyField( TIFFTAG_PHOTOMETRIC, i16 ); + CopyField( TIFFTAG_TILEWIDTH, i32 ); + CopyField( TIFFTAG_TILELENGTH, i32 ); + CopyField( TIFFTAG_ROWSPERSTRIP, i32 ); + + if( TIFFGetField( in, TIFFTAG_ICCPROFILE, &i32, &i32a ) ) + TIFFSetField( out, TIFFTAG_ICCPROFILE, i32, i32a ); + + buf = im_malloc( NULL, TIFFTileSize( in ) ); + n = TIFFNumberOfTiles( in ); + for( tile = 0; tile < n; tile++ ) { + tsize_t len; + + len = TIFFReadEncodedTile( in, tile, buf, (tsize_t) -1 ); + if( len < 0 || + TIFFWriteEncodedTile( out, tile, buf, len ) < 0 ) { + im_free( buf ); + return( -1 ); + } + } + im_free( buf ); + + return( 0 ); +} + +/* Append a file to a TIFF file. + */ +static int +tiff_append( TIFF *out, const char *name ) +{ + TIFF *in; + + if( !(in = tiff_openin( name )) ) + return( -1 ); + + if( tiff_copy( out, in ) ) { + TIFFClose( in ); + return( -1 ); + } + TIFFClose( in ); + + if( !TIFFWriteDirectory( out ) ) + return( -1 ); + + return( 0 ); +} + +/* Gather all of the files we wrote into single output file. + */ +static int +gather_pyramid( TiffWrite *tw ) +{ + PyramidLayer *layer; + TIFF *out; + +#ifdef DEBUG + printf( "Starting pyramid gather ...\n" ); +#endif /*DEBUG*/ + + if( !(out = tiff_openout( tw->name )) ) + return( -1 ); + + if( tiff_append( out, tw->bname ) ) { + TIFFClose( out ); + return( -1 ); + } + + for( layer = tw->layer; layer; layer = layer->below ) + if( tiff_append( out, layer->lname ) ) { + TIFFClose( out ); + return( -1 ); + } + + TIFFClose( out ); + +#ifdef DEBUG + printf( "Pyramid built\n" ); +#endif /*DEBUG*/ + + return( 0 ); +} + +int +im_vips2tiff( IMAGE *im, const char *filename ) +{ + TiffWrite *tw; + int res; + +#ifdef DEBUG + printf( "im_tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() ); +#endif /*DEBUG*/ + + /* Override the default TIFF error handler. + */ + TIFFSetErrorHandler( (TIFFErrorHandler) vhandle ); + + /* Check input image. + */ + if( im_pincheck( im ) ) + return( -1 ); + if( im->Coding != IM_CODING_LABQ && im->Coding != IM_CODING_NONE ) { + im_error( "im_vips2tiff", _( "unknown coding type" ) ); + return( -1 ); + } + if( im->BandFmt != IM_BANDFMT_UCHAR && + !(im->BandFmt == IM_BANDFMT_SHORT && + im->Type == IM_TYPE_LABS) && + im->BandFmt != IM_BANDFMT_USHORT && + im->BandFmt != IM_BANDFMT_FLOAT ) { + im_error( "im_vips2tiff", _( "unsigned 8-bit int, 16-bit int, " + "and 32-bit float only" ) ); + return( -1 ); + } + if( im->Coding == IM_CODING_NONE ) { + if( im->Bands < 1 || im->Bands > 4 ) { + im_error( "im_vips2tiff", _( "1 to 4 bands only" ) ); + return( -1 ); + } + } + + /* Make output image. If this is a pyramid, write the base image to + * fred.1.tif rather than fred.tif. + */ + if( !(tw = make_tiff_write( im, filename )) ) + return( -1 ); + if( tw->pyramid ) { + if( !(tw->bname = new_tiff_name( tw, tw->name, 1 )) || + !(tw->tif = tiff_openout( tw->bname )) ) { + free_tiff_write( tw ); + return( -1 ); + } + } + else { + /* No pyramid ... write straight to name. + */ + if( !(tw->tif = tiff_openout( tw->name )) ) { + free_tiff_write( tw ); + return( -1 ); + } + } + + /* Write the TIFF header for the full-res file. + */ + if( write_tiff_header( tw, tw->tif, im->Xsize, im->Ysize ) ) { + free_tiff_write( tw ); + return( -1 ); + } + + if( tw->tile ) + res = write_tif_tile( tw ); + else + res = write_tif_strip( tw ); + if( res ) { + free_tiff_write( tw ); + return( -1 ); + } + + /* Free pyramid resources ... this will TIFFClose() the intermediates, + * ready for us to read from them again. + */ + if( tw->layer ) + free_pyramid( tw->layer ); + if( tw->tif ) { + TIFFClose( tw->tif ); + tw->tif = NULL; + } + + /* Gather layers together into final pyramid file. + */ + if( tw->pyramid && gather_pyramid( tw ) ) { + free_tiff_write( tw ); + return( -1 ); + } + + free_tiff_write( tw ); + + return( 0 ); +} + +#endif /*HAVE_TIFF*/ diff --git a/libsrc/conversion/im_zoom.c b/libsrc/conversion/im_zoom.c new file mode 100644 index 00000000..e6c0dacc --- /dev/null +++ b/libsrc/conversion/im_zoom.c @@ -0,0 +1,371 @@ +/* @(#) Zoom an image by pixel replication. Any non-coded type, also works for + * @(#) IM_CODING_LABQ. + * @(#) + * @(#) int im_zoom( in, out, factor ) + * @(#) IMAGE *in, *out; + * @(#) int factor; + * @(#) + * @(#) Returns: -1 on error, else 0 + * Author: N. Martinez 1991 + * 6/6/94 JC + * - rewritten to ANSI-C + * - now works for any type, including IM_CODING_LABQ + * 7/10/94 JC + * - new IM_ARRAY() macro + * 26/1/96 JC + * - separate x and y zoom factors + * 21/8/96 JC + * - partial, yuk! this is so complicated ... + * 30/8/96 JC + * - sets demand_hint + * 10/2/00 JC + * - check for integer overflow in zoom facs ... was happening with ip's + * zoom on large images + * 3/8/02 JC + * - fall back to im_copy() for x & y factors == 1 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + * TODO: + * Test for pixel size and use memcpy() on individual pixels once they reach + * sizes of the order of tens of bytes. char-wise copy is quiker than + * memcpy() for smaller pixels. + * + * Also, I haven't tested it but int-wise copying may be faster still, as + * long as alignment permits it. + * + * tcv. 2006-09-01 + */ + +/* Turn off assert(). +#define NDEBUG 1 + */ + +/* Turn on IM_REGION_ADDR() range checks. +#define DEBUG 1 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Round N down to P boundary. + */ +#define ROUND_DOWN(N,P) ((N) - ((N) % P)) + +/* Round N up to P boundary. + */ +#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) )) + +/* Our main parameter struct. + */ +typedef struct { + int xfac; /* Scale factors */ + int yfac; +} ZoomInfo; + +/* Paint the part of the region containing only whole pels. + */ +static void +paint_whole( REGION *or, REGION *ir, ZoomInfo *zm, + const int left, const int right, const int top, const int bottom ) +{ + const int ps = IM_IMAGE_SIZEOF_PEL( ir->im ); + const int ls = IM_REGION_LSKIP( or ); + const int rs = ps * (right - left); + + /* Transform to ir coordinates. + */ + const int ileft = left / zm->xfac; + const int iright = right / zm->xfac; + const int itop = top / zm->yfac; + const int ibottom = bottom / zm->yfac; + + int x, y, z, i; + + /* We know this! + */ + assert( right > left && bottom > top && + right % zm->xfac == 0 && + left % zm->xfac == 0 && + top % zm->yfac == 0 && + bottom % zm->yfac == 0 ); + + /* Loop over input, as we know we are all whole. + */ + for( y = itop; y < ibottom; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, ileft, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, left, y * zm->yfac ); + PEL *r; + + /* Expand the first line of pels. + */ + r = q; + for( x = ileft; x < iright; x++ ) { + /* Copy each pel xfac times. + */ + for( z = 0; z < zm->xfac; z++ ) { + for( i = 0; i < ps; i++ ) + r[i] = p[i]; + + r += ps; + } + + p += ps; + } + + /* Copy the expanded line yfac-1 times. + */ + r = q + ls; + for( z = 1; z < zm->yfac; z++ ) { + memcpy( r, q, rs ); + r += ls; + } + } +} + +/* Paint the part of the region containing only part-pels. + */ +static void +paint_part( REGION *or, REGION *ir, const ZoomInfo *zm, + const int left, const int right, const int top, const int bottom ) +{ + const int ps = IM_IMAGE_SIZEOF_PEL( ir->im ); + const int ls = IM_REGION_LSKIP( or ); + const int rs = ps * (right - left); + + /* Start position in input. + */ + const int ix = left / zm->xfac; + const int iy = top / zm->yfac; + + /* Pels down to yfac boundary, pels down to bottom. Do the smallest of + * these for first y loop. + */ + const int ptbound = (iy + 1) * zm->yfac - top; + const int ptbot = bottom - top; + + int yt = IM_MIN( ptbound, ptbot ); + + int x, y, z, i; + + /* Only know this. + */ + assert( right - left >= 0 && bottom - top >= 0 ); + + /* Have to loop over output. + */ + for( y = top; y < bottom; ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, ix, y / zm->yfac ); + PEL *q = (PEL *) IM_REGION_ADDR( or, left, y ); + PEL *r; + + /* Output pels until we jump the input pointer. + */ + int xt = (ix + 1) * zm->xfac - left; + + /* Loop for this output line. + */ + r = q; + for( x = left; x < right; x++ ) { + /* Copy 1 pel. + */ + for( i = 0; i < ps; i++ ) + r[i] = p[i]; + r += ps; + + /* Move input if on boundary. + */ + --xt; + if( xt == 0 ) { + xt = zm->xfac; + p += ps; + } + } + + /* Repeat that output line until the bottom of this pixel + * boundary, or we hit bottom. + */ + r = q + ls; + for( z = 1; z < yt; z++ ) { + memcpy( r, q, rs ); + r += ls; + } + + /* Move y on by the number of lines we wrote. + */ + y += yt; + + /* Reset yt for next iteration. + */ + yt = zm->yfac; + } +} + +/* Zoom a REGION. + */ +static int +zoom_gen( REGION *or, REGION *ir, IMAGE *in, ZoomInfo *zm ) +{ + /* Output area we are building. + */ + const Rect *r = &or->valid; + const int ri = IM_RECT_RIGHT( r ); + const int bo = IM_RECT_BOTTOM(r); + + Rect s; + int left, right, top, bottom; + int width, height; + + /* Area of input we need. We have to round out, as we may have + * part-pixels all around the edges. + */ + left = ROUND_DOWN( r->left, zm->xfac ); + right = ROUND_UP( ri, zm->xfac ); + top = ROUND_DOWN( r->top, zm->yfac ); + bottom = ROUND_UP( bo, zm->yfac ); + width = right - left; + height = bottom - top; + s.left = left / zm->xfac; + s.top = top / zm->yfac; + s.width = width / zm->xfac; + s.height = height / zm->yfac; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Find the part of the output (if any) which uses only whole pels. + */ + left = ROUND_UP( r->left, zm->xfac ); + right = ROUND_DOWN( ri, zm->xfac ); + top = ROUND_UP( r->top, zm->yfac ); + bottom = ROUND_DOWN( bo, zm->yfac ); + width = right - left; + height = bottom - top; + + /* Stage 1: we just paint the whole pels in the centre of the region. + * As we know they are not clipped, we can do it quickly. + */ + if( width > 0 && height > 0 ) + paint_whole( or, ir, zm, left, right, top, bottom ); + + /* Just fractional pixels left. Paint in the top, left, right and + * bottom parts. + */ + if( top - r->top > 0 ) + /* Some top pixels. + */ + paint_part( or, ir, zm, + r->left, ri, r->top, IM_MIN( top, bo ) ); + if( left - r->left > 0 && height > 0 ) + /* Left pixels. + */ + paint_part( or, ir, zm, + r->left, IM_MIN( left, ri ), top, bottom ); + if( ri - right > 0 && height > 0 ) + /* Right pixels. + */ + paint_part( or, ir, zm, + IM_MAX( right, r->left ), ri, top, bottom ); + if( bo - bottom > 0 && height >= 0 ) + /* Bottom pixels. + */ + paint_part( or, ir, zm, + r->left, ri, IM_MAX( bottom, r->top ), bo ); + + return( 0 ); +} + +int +im_zoom( IMAGE *in, IMAGE *out, int xfac, int yfac ) +{ + ZoomInfo *zm; + + /* Check arguments. + */ + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_zoom", _( "unknown coding type" ) ); + return( -1 ); + } + if( xfac <= 0 || yfac <= 0 ) { + im_error( "im_zoom", _( "zoom factors should be >= 0" ) ); + return( -1 ); + } + if( (double) in->Xsize * xfac > (double) INT_MAX / 2 || + (double) in->Ysize * yfac > (double) INT_MAX / 2 ) { + /* Make sure we won't get integer overflow. + */ + im_error( "im_zoom", _( "zoom factors too large" ) ); + return( -1 ); + } + if( xfac == 1 && yfac == 1 ) + return( im_copy( in, out ) ); + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Make output. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Xsize * xfac; + out->Ysize = in->Ysize * yfac; + + /* Save parameters. + */ + if( !(zm = IM_NEW( out, ZoomInfo )) ) + return( -1 ); + zm->xfac = xfac; + zm->yfac = yfac; + + /* Set demand hints. THINSTRIP will prevent us from using + * paint_whole() too much ... so go for FATSTRIP. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, zoom_gen, im_stop_one, in, zm ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/conversion/man3/Makefile.am b/libsrc/conversion/man3/Makefile.am new file mode 100644 index 00000000..a0444356 --- /dev/null +++ b/libsrc/conversion/man3/Makefile.am @@ -0,0 +1,84 @@ +man_MANS = \ + im_analyze2vips.3 \ + im_bandjoin.3 \ + im_black.3 \ + im_c2amph.3 \ + im_c2imag.3 \ + im_c2ps.3 \ + im_c2real.3 \ + im_c2rect.3 \ + im_clip.3 \ + im_clip2c.3 \ + im_clip2cm.3 \ + im_clip2d.3 \ + im_clip2dcm.3 \ + im_clip2f.3 \ + im_clip2fmt.3 \ + im_clip2i.3 \ + im_clip2s.3 \ + im_clip2ui.3 \ + im_clip2us.3 \ + im_copy.3 \ + im_copy_set.3 \ + im_copy_set_meta.3 \ + im_copy_swap.3 \ + im_copy_morph.3 \ + im_copy_from.3 \ + im_csv2vips.3 \ + im_csv2vips_header.3 \ + im_magick2vips.3 \ + im_magick2vips_header.3 \ + im_extract.3 \ + im_extract_area.3 \ + im_extract_areabands.3 \ + im_extract_bands.3 \ + im_falsecolour.3 \ + im_fliphor.3 \ + im_flipver.3 \ + im_gbandjoin.3 \ + im_insert.3 \ + im_istiff.3 \ + im_jpeg2vips.3 \ + im_jpeg2vips_header.3 \ + im_lrjoin.3 \ + im_mask2vips.3 \ + im_png2vips.3 \ + im_png2vips_header.3 \ + im_exr2vips.3 \ + im_exr2vips_header.3 \ + im_ppm2vips.3 \ + im_ppm2vips_header.3 \ + im_print.3 \ + im_raw2vips.3 \ + im_recomb.3 \ + im_replicate.3 \ + im_grid.3 \ + im_ri2c.3 \ + im_rot180.3 \ + im_rot270.3 \ + im_rot90.3 \ + im_scale.3 \ + im_scaleps.3 \ + im_rightshift_size.3 \ + im_slice.3 \ + im_subsample.3 \ + im_system.3 \ + im_tbjoin.3 \ + im_thresh.3 \ + im_text.3 \ + im_tiff2vips.3 \ + im_tiff2vips_header.3 \ + im_tile_cache.3 \ + im_vips2bufjpeg.3 \ + im_vips2csv.3 \ + im_vips2jpeg.3 \ + im_vips2mask.3 \ + im_vips2mimejpeg.3 \ + im_vips2png.3 \ + im_vips2ppm.3 \ + im_vips2tiff.3 \ + im_zoom.3 \ + im_msb.3 \ + im_msb_band.3 + +EXTRA_DIST = ${man_MANS} diff --git a/libsrc/conversion/man3/im_analyze2vips.3 b/libsrc/conversion/man3/im_analyze2vips.3 new file mode 100644 index 00000000..b3bbf50c --- /dev/null +++ b/libsrc/conversion/man3/im_analyze2vips.3 @@ -0,0 +1,39 @@ +.TH IM_ANALYZE2VIPS 3 "4 August 2005" +.SH NAME +im_analyze2vips \- convert Analyze 7.5 images to VIPS format +.SH SYNOPSIS +#include + +int im_analyze2vips( const char *filename, IMAGE *out ) + +int im_analyze2vips_header( const char *filename, IMAGE *out ) + +.SH DESCRIPTION +.B im_analyze2vips(3) +reads the Analyze image in +.B filename, +and writes the image out +in VIPS format. It can read (almost) any Analyze format image. Images with +more than two dimensions simply appear as a very tall, thin strip. Use +.B im_grid(3) +to lay the tiles out as your prefer. + +It reads the old-style 7.5 format, where the header and the image data are +stored in separate files. You can give the name of either the header (for +example, "fred.hdr") the image data (for example, "fred.img"), or neither (eg. +"fred"). + +The fields in the Analyze header appear in the VIPS header with a "dsr-" +prefix. So the Analyze field "patient_id", which is part of data_history, may +be retrieved with + + im_header_string "dsr-data_history.patient_id" fred.v + +.B im_analyze2vips_header() +reads the just the header of the image into the VIPS image. You can't read any +pixels! + +.SH SEE ALSO +im_grid(3) +.SH COPYRIGHT +Imperial College 2005 diff --git a/libsrc/conversion/man3/im_bandjoin.3 b/libsrc/conversion/man3/im_bandjoin.3 new file mode 100644 index 00000000..f6d0d5d9 --- /dev/null +++ b/libsrc/conversion/man3/im_bandjoin.3 @@ -0,0 +1,39 @@ +.TH IM_BANDJOIN 3 "28 June 1990" +.SH NAME +im_bandjoin, im_gbandjoin \- join two or more images +.SH SYNOPSIS +.B #include + +.B int im_bandjoin(im1, im2, imout) +.br +.B IMAGE *im1, *im2, *imout; + +.B int im_gbandjoin(imarray, imout, no) +.br +.B IMAGE *imarray[], *imout; +.br +.B int no; +.SH DESCRIPTION +These function perform a band-wise join of two or more images. Input +images should be of the same type and should have the same sizes. + +.B im_bandjoin() +performs a band-wise join of two images. If the two images +have n and m bands respectively, then the output image will have n+m +bands, with the first n coming from the first image and the last m +from the second. + +.B im_gbandjoin() +performs a generalised band-wise join of no images. +Input images can have any number of bands; for instance if imarray[0] has j +bands, imarray[1] has k bands, ...., imarray[no-1] has z bands, output +has j+k+...+z bands. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_lrjoin(3), im_lrmerge(3), im_insert(3). +.SH COPYRIGHT +.br +J. Cupitt, N. Dessipris +.SH AUTHOR +J. Cupitt, N. Dessipris \- 25/04/1991 diff --git a/libsrc/conversion/man3/im_black.3 b/libsrc/conversion/man3/im_black.3 new file mode 100644 index 00000000..20a9331f --- /dev/null +++ b/libsrc/conversion/man3/im_black.3 @@ -0,0 +1,23 @@ +.TH IM_BLACK 3 "11 April 1990" +.SH NAME +im_black \- make a black image +.SH SYNOPSIS +.B #include + +.B int im_black(out, x, y, bands) +.br +.B IMAGE *out; +.br +.B int x, y, bands; +.SH DESCRIPTION +im_black() +makes a black uchar image of the specified size and number of bands. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_openin(3), im_openout(3), im_setbuf(3). +.SH COPYRIGHT +.br +National Gallery 1990 - 1993 +.SH AUTHOR +J. Cupitt \- 11/04/1990 diff --git a/libsrc/conversion/man3/im_c2amph.3 b/libsrc/conversion/man3/im_c2amph.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2amph.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_c2imag.3 b/libsrc/conversion/man3/im_c2imag.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2imag.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_c2ps.3 b/libsrc/conversion/man3/im_c2ps.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2ps.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_c2real.3 b/libsrc/conversion/man3/im_c2real.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2real.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_c2rect.3 b/libsrc/conversion/man3/im_c2rect.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_c2rect.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip.3 b/libsrc/conversion/man3/im_clip.3 new file mode 100644 index 00000000..6096b537 --- /dev/null +++ b/libsrc/conversion/man3/im_clip.3 @@ -0,0 +1,90 @@ +.TH CONVERSIONS 3 "15 April 1991" +.SH NAME +im_c2amph, im_c2imag, im_c2ps, im_c2real, im_clip2fmt, +im_ri2c \- conversion between image types +.SH SYNOPSIS +#include + +int im_c2amph(in, out) +.br +IMAGE *in, *out; + +int im_c2rect(in, out) +.br +IMAGE *in, *out; + +int im_c2imag(in, out) +.br +IMAGE *in, *out; + +int im_c2ps(in, out) +.br +IMAGE *in, *out; + +int im_c2real(in, out) +.br +IMAGE *in, *out; + +int im_clip2fmt(in, out, ofmt) +.br +IMAGE *in, *out; +.br +int ofmt; + +int im_ri2c(in1, in2, out) +.br +IMAGE *in1, *in2, *out; +.SH DESCRIPTION +Each of the above functions converts the image held by the image descriptor in +to another type image and writes the result on the image descriptor out. +Sizes and the number of channels are identical to those of input. Whenever +required by the conversion, the element values are rounded by calling floor(). + +.B im_c2amph() +converts a complex (float or double) input to (amplitude, phase), with phase +in degrees. Output has the same format as input. + +.B im_c2rect() +converts a complex (float or double) input in (amplitude, phase) form back to +rectangular coordinates. Again, phase should be expressed in degrees. + +.B im_c2ps() +convert a (float or double) complex image to amplitude (float or +double) image. Complex input (a,b) is mapped to sqrt( a*a+b*b ). + +.B im_clip2fmt() +converts the input image to 'ofmt', where ofmt is the new value for +BandFmt. For example, im_clip2fmt(in, out, FMTUSHORT) converts any image to +unsigned short. + +The number of +clipped values (if any), are printed in the standard error output. Before +clipping rounding is carried out if necessary. When converting between real +and complex types with these functions, the imaginary part of the output is +set to zero. When converting from complex to real types, the imaginary part is +ignored. Use im_c2ps(), im_c2real() etc. to get other behaviour. + +Legacy functions are available, called im_clip(), im_clip2c(), im_clip2d(), +im_clip2f(), im_clip2i(), im_clip2s(), im_clip2ui(), im_clip2us(), +im_clip2cm() and im_clip2dcm() which convert the input image to unsigned char, +signed char, double, float, int, short, unsigned int, unsigned short, +complex and double complex respectively. + +.B im_ri2c() +takes as inputs two non-complex images of equal sizes and number of +channels. The output is a complex image with the real part coming from in1 +and the imaginary part coming from in2. Output is float complex (FMTCOMPLEX) +only if none of the inputs is double. If one or both inputs are double the +result is double complex (FMTDPCOMPLEX). + +.B im_c2real() +and +.B im_c2imag() +extract the real or the imaginary part of a +complex image respectively. If input is be float complex or double complex, +then output is float or double respectively. + +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_scale(3). diff --git a/libsrc/conversion/man3/im_clip2c.3 b/libsrc/conversion/man3/im_clip2c.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2c.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2cm.3 b/libsrc/conversion/man3/im_clip2cm.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2cm.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2d.3 b/libsrc/conversion/man3/im_clip2d.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2d.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2dcm.3 b/libsrc/conversion/man3/im_clip2dcm.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2dcm.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2f.3 b/libsrc/conversion/man3/im_clip2f.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2f.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2fmt.3 b/libsrc/conversion/man3/im_clip2fmt.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2fmt.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2i.3 b/libsrc/conversion/man3/im_clip2i.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2i.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2s.3 b/libsrc/conversion/man3/im_clip2s.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2s.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2ui.3 b/libsrc/conversion/man3/im_clip2ui.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2ui.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_clip2us.3 b/libsrc/conversion/man3/im_clip2us.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_clip2us.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_copy.3 b/libsrc/conversion/man3/im_copy.3 new file mode 100644 index 00000000..4e33f68d --- /dev/null +++ b/libsrc/conversion/man3/im_copy.3 @@ -0,0 +1,79 @@ +.TH IM_COPY 3 "11 April 1990" +.SH NAME +im_copy, im_copy_set, im_copy_swap, im_copy_morph \- copy an image +.SH SYNOPSIS +.B #include + +int im_copy(in, out) +.br +IMAGE *in, *out; + +int im_copy_set( in, out, type, xres, yres ) +.br +IMAGE *in, *out; +.br +int type; +.br +float xres, yres; + +int im_copy_swap( in, out ) +.br +IMAGE *in, *out; + +int im_copy_morph( in, out, Bands, BandFmt, Coding ) +.br +IMAGE *in, *out; +.br +int Bands, BandFmt, Coding; + +typedef enum { +.br + IM_ARCH_NATIVE, +.br + IM_ARCH_BYTE_SWAPPED, +.br + IM_ARCH_LSB_FIRST, +.br + IM_ARCH_MSB_FIRST +.br +} im_arch_type; + +int im_copy_from( in, out, architecture ) +.br +IMAGE *in, *out; +.br +im_arch_type architecture; + +.SH DESCRIPTION +.B im_copy(3) +copies the image held by the image descriptor in +and writes the result to the image descriptor out. The input can be of any +size and have any type. Does LABPACK coded images too! + +.B im_copy_set(3) +behaves exactly as +.B im_copy(3), +but lets you set informational fields in the +header on the way through. + +.B im_copy_swap(3) +copies an uncoded image, swapping between SPARC and Intel byte order on the +way. + +.B im_copy_morph(3) +behaves exactly as +.B im_copy(3), +but lets you set fields which affect pixel format +on the way through. + +.B im_copy_from(3) +calls either +.B im_copy(3) +or +.B im_copy_swap(3) +as necessary to copy from the specified architecture. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_extract(3), im_open(3) diff --git a/libsrc/conversion/man3/im_copy_from.3 b/libsrc/conversion/man3/im_copy_from.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_from.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_copy_morph.3 b/libsrc/conversion/man3/im_copy_morph.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_morph.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_copy_set.3 b/libsrc/conversion/man3/im_copy_set.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_set.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_copy_set_meta.3 b/libsrc/conversion/man3/im_copy_set_meta.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_set_meta.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_copy_swap.3 b/libsrc/conversion/man3/im_copy_swap.3 new file mode 100644 index 00000000..8e0c909e --- /dev/null +++ b/libsrc/conversion/man3/im_copy_swap.3 @@ -0,0 +1 @@ +.so man3/im_copy.3 diff --git a/libsrc/conversion/man3/im_csv2vips.3 b/libsrc/conversion/man3/im_csv2vips.3 new file mode 100644 index 00000000..f80aea48 --- /dev/null +++ b/libsrc/conversion/man3/im_csv2vips.3 @@ -0,0 +1,76 @@ +.TH IM_CSV 3 "November 2000" +.SH NAME +im_csv2vips, im_vips2csv \- read and write CSV (comma separated values) files +.SH SYNOPSIS +#include + +int im_csv2vips( const char *filename, IMAGE *out ) + +int im_csv2vips_header( const char *filename, IMAGE *out ); + +int im_vips2csv( IMAGE *in, const char *filename ) + +.SH DESCRIPTION +.B im_csv2vips(3) +reads the CSV (comma separated values) data in filename, and writes the image +out in VIPS format. The output image is always 1 band (monochrome), +IM_BANDFMT_DOUBLE. + +The reader is deliberately rather fussy: it will fail if there are any short +lines, or if the file is too short. It will ignore lines that are too long. + +Read options can be embedded in the filename. The options can be given in any +order and are: + + skip:lines-to-skip + +The number of lines to skip at the start of the file. Default zero. + + whi:whitespace-characters + +The skippable whitespace characters. Default and double quotes ("). +Whitespace characters are always run together. + + sep:separator-characters + +The characters that separate fields. Default ;,. Separators are +never run together. + + line:lines-to-read + +The number of lines to read from the file. Default -1, meaning read to end of +file. + +.B im_csv2vips_header(3) +works exactly as +.B im_csv2vips(3) +but only sets the header of the output image. It is rather slow, since it has +to read the whole input file. + +.B im_vips2csv(3) +writes the VIPS image to the file as ascii text, one line of text per +scanline. Complex numbers are written as "(real,imaginary)" and will need +extra parsing I guess. + +Write options can be embedded in the filename. The options can be given in any +order and are: + + sep:separator-string + +The string to use to separate numbers in the output. The default is "\\t" (tab). + +.SH EXAMPLES + + im_csv2vips( "fred.csv:skip:58,sep:\,,line:3", out ); + +Will read three lines starting at line 59, with space, comma and tab as the +allowed separators. Note that the ',' has to be escaped with a backslash. + + im_vips2csv fred.jpg fred.csv:sep:\t + +Convert a jpeg to CSV, separating numbers with tab characters. + +.SH SEE ALSO +im_read_dmask(3), im_ppm2vips(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_csv2vips_header.3 b/libsrc/conversion/man3/im_csv2vips_header.3 new file mode 100644 index 00000000..e4558b4a --- /dev/null +++ b/libsrc/conversion/man3/im_csv2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_csv2vips.3 diff --git a/libsrc/conversion/man3/im_exr2vips.3 b/libsrc/conversion/man3/im_exr2vips.3 new file mode 100644 index 00000000..2a930078 --- /dev/null +++ b/libsrc/conversion/man3/im_exr2vips.3 @@ -0,0 +1,23 @@ +.TH IM_EXR 3 "3 Jan 2003" +.SH NAME +im_exr2vips, im_exr2vips_header \- convert OpenEXR images to VIPS format +.SH SYNOPSIS +#include + +int im_exr2vips( const char *filename, IMAGE *out ) + +int im_exr2vips_header( const char *filename, IMAGE *out ) + +.SH DESCRIPTION +.B im_exr2vips() +reads the OpenEXR image in filename, and writes the image out +in VIPS format. + +.B im_png2vips_header() +reads just the header information from the OpenEXR file. You can't read any +pixels! + +.SH SEE ALSO +im_isexr(3), im_vips2tiff(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_exr2vips_header.3 b/libsrc/conversion/man3/im_exr2vips_header.3 new file mode 100644 index 00000000..719c91d6 --- /dev/null +++ b/libsrc/conversion/man3/im_exr2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_exr2vips.3 diff --git a/libsrc/conversion/man3/im_extract.3 b/libsrc/conversion/man3/im_extract.3 new file mode 100644 index 00000000..fe920239 --- /dev/null +++ b/libsrc/conversion/man3/im_extract.3 @@ -0,0 +1,60 @@ +.TH IM_EXTRACT 3 "11 April 1990" +.SH NAME +im_extract_areabands, im_extract_bands, im_extract_area \- extract a portion of an image +.SH SYNOPSIS +.B #include + +int im_extract_areabands( IMAGE *in, IMAGE *out, +.br + int left, int top, int width, int height, int band, int nbands ) + +int im_extract_area( IMAGE *in, IMAGE *out, +.br + int left, int top, int width, int height ) + +int im_extract_bands( IMAGE *in, IMAGE *out, +.br + int chsel, int nbands ) + +.SH DESCRIPTION +.B im_extract_areabands(3) +extracts the rectangular portion of the image defined by +.B left, +.B top, +.B width, +and +.B height of image +.B in +and writes the result to image +.B out. +The area must lie entirely within in the image. Selects the set of +.B nbands +bands +starting at band number +.B band +(numbering bands from zero). + +Works for any size image, any number of bands, any type. Works for LABPACK +coded images too! But disallows band extraction in this case. + +.B im_extract_area(3) +is a convenience function which extracts an area from an +image, leaving the bands the same. + +.B im_extract_bands(3) +takes +.B nbands +out of an image, starting from band +.B band. +So, for example, nbands == 2, bands == 1 will form a two band image from an RGB +image, where the two bands are the G and the B bands. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_insert(3), im_lrjoin(3), im_lrmerge(3), im_stats(3), +im_region_region(3). +.SH COPYRIGHT +J. Cupitt, +.SH AUTHOR +J. Cupitt \- 11/04/1990 diff --git a/libsrc/conversion/man3/im_extract_area.3 b/libsrc/conversion/man3/im_extract_area.3 new file mode 100644 index 00000000..09e88df3 --- /dev/null +++ b/libsrc/conversion/man3/im_extract_area.3 @@ -0,0 +1 @@ +.so man3/im_extract.3 diff --git a/libsrc/conversion/man3/im_extract_areabands.3 b/libsrc/conversion/man3/im_extract_areabands.3 new file mode 100644 index 00000000..09e88df3 --- /dev/null +++ b/libsrc/conversion/man3/im_extract_areabands.3 @@ -0,0 +1 @@ +.so man3/im_extract.3 diff --git a/libsrc/conversion/man3/im_extract_bands.3 b/libsrc/conversion/man3/im_extract_bands.3 new file mode 100644 index 00000000..09e88df3 --- /dev/null +++ b/libsrc/conversion/man3/im_extract_bands.3 @@ -0,0 +1 @@ +.so man3/im_extract.3 diff --git a/libsrc/conversion/man3/im_falsecolour.3 b/libsrc/conversion/man3/im_falsecolour.3 new file mode 100644 index 00000000..1549752e --- /dev/null +++ b/libsrc/conversion/man3/im_falsecolour.3 @@ -0,0 +1,23 @@ +.TH IM_FALSECOLOUR 3 "30 October 1992" +.SH NAME +im_falsecolour \- colour a mono image +.SH SYNOPSIS +.B #include + +.B int im_falsecolour(in, out) +.br +.B IMAGE *in, *out; + +.SH DESCRIPTION +Turns a one-band unsigned char image into a three band unsigned char image. +The colour scale is stolen from a PET scan and has red for hot, green for +normal and blue for cold. +.SH RETURN VALUE +0 on success and -1 on error. +.SH SEE ALSO +im_disp2XYZ(3), etc. +.SH COPYRIGHT +.br +National Gallery +.SH AUTHOR +J. Cupitt diff --git a/libsrc/conversion/man3/im_fliphor.3 b/libsrc/conversion/man3/im_fliphor.3 new file mode 100644 index 00000000..bf72dac4 --- /dev/null +++ b/libsrc/conversion/man3/im_fliphor.3 @@ -0,0 +1 @@ +.so man3/im_rot180.3 diff --git a/libsrc/conversion/man3/im_flipver.3 b/libsrc/conversion/man3/im_flipver.3 new file mode 100644 index 00000000..bf72dac4 --- /dev/null +++ b/libsrc/conversion/man3/im_flipver.3 @@ -0,0 +1 @@ +.so man3/im_rot180.3 diff --git a/libsrc/conversion/man3/im_gbandjoin.3 b/libsrc/conversion/man3/im_gbandjoin.3 new file mode 100644 index 00000000..15713fe8 --- /dev/null +++ b/libsrc/conversion/man3/im_gbandjoin.3 @@ -0,0 +1 @@ +.so man3/im_bandjoin.3 diff --git a/libsrc/conversion/man3/im_grid.3 b/libsrc/conversion/man3/im_grid.3 new file mode 100644 index 00000000..f3597c00 --- /dev/null +++ b/libsrc/conversion/man3/im_grid.3 @@ -0,0 +1,42 @@ +.TH IM_GRID 3 "4 August 2005" +.SH NAME +im_grid \- split a vertical image into a grid of smaller images +.SH SYNOPSIS +.B #include + +int +.br +im_grid( IMAGE *in, IMAGE *out, +.br + int tile_height, int across, int down ) +.SH DESCRIPTION +.B im_grid(3) +slices image +.B in +into a set of tiles, each the same width as +.B in +and of height +.B tile_height +and rearranges the tiles into a grid with +.B across +tiles across and +.B down +tiles down. + +It is useful for loading volumetric images (for example, CT or PET scans) +where more than 2 dimensions need to be displayed. + +The current implementation is optimised for the case where image +.B in +is thin and tall and +.B across +and +.B down +are large. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_extract_area(3), im_zoom(3) +.SH COPYRIGHT +Imperial College 2005 diff --git a/libsrc/conversion/man3/im_insert.3 b/libsrc/conversion/man3/im_insert.3 new file mode 100644 index 00000000..7b4e69b6 --- /dev/null +++ b/libsrc/conversion/man3/im_insert.3 @@ -0,0 +1,29 @@ +.TH IM_INSERT 3 "11 April 1990" +.SH NAME +im_insert \- insert one image into another +.SH SYNOPSIS +#include + +int im_insert(in, ins, out, x, y) +.br +IMAGE *in, *insub, *out; +.br +int x, y; +.SH DESCRIPTION +im_insert() +inserts one image into another. ins is inserted into image in at +position x, y relative to the top left hand corner of in. out is made large +enough to hold all of both in and ins. Any areas of out not coming from +either in or ins are set to black (binary 0). If ins overlaps in, +ins +will appear on top of in. Both images must have the same number of +bands and the same BandFmt. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_extract(3), im_lrjoin(3), im_lrmerge(3) +.SH COPYRIGHT +.br +J. Cupitt, +.SH AUTHOR +J. Cupitt \- 11/04/1990 diff --git a/libsrc/conversion/man3/im_istiff.3 b/libsrc/conversion/man3/im_istiff.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/conversion/man3/im_istiff.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/conversion/man3/im_jpeg2vips.3 b/libsrc/conversion/man3/im_jpeg2vips.3 new file mode 100644 index 00000000..8d5d3adb --- /dev/null +++ b/libsrc/conversion/man3/im_jpeg2vips.3 @@ -0,0 +1,70 @@ +.TH IM_JPEG2VIPS 3 "6 June 1994" +.SH NAME +im_jpeg2vips, im_vips2jpeg, im_vips2bufjpeg, im_vips2mimejpeg \- convert JPEG images to and from VIPS format +.SH SYNOPSIS +#include + +int im_jpeg2vips( char *filename, IMAGE *out ) + +int im_jpeg2vips_header( char *filename, IMAGE *out ) + +int im_vips2jpeg( IMAGE *in, char *filename ) + +int im_vips2bufjpeg( IMAGE *in, IMAGE *base, char **obuf, int *olen ) + +int im_vips2mimejpeg( IMAGE *in ) + +.SH DESCRIPTION +.B im_jpeg2vips() +reads the named jpeg file and writes it to the specified +IMAGE. The entire image is read before returning. It will handle 1 and 3 band +8-bit images only. + +Any embedded ICC profiles are ignored: you always just get the RGB from the +file. Instead, the embedded profile will be attached to the image as metadata. +Any EXIF data is also attached as VIPS metadata. + +.B im_jpeg2vips_header() +reads just the header of the JPEG file (including any EXIF data), and sets +the fields in the VIPS image. +You can't read any pixels! This is only useful for printing header +information. + +.B im_vips2jpeg() +writes the IMAGE to filename in JPEG format. It uses the +default settings of the IJG library. + +A compression factor may be encoded in the filename: for example, +"fred.jpg" will write with the default compression factor (75), +"fred.jpg:25" will write with factor 25. + +An ICC profile may also be specified. For example, +"fred.jpg:25,/home/john/srgb.icc" will embed the profile stored in the file +"/home/john/srgb.icc" into the JPEG image. This does not affect the pixels +which are written; just the way they are tagged. + +If no profile is specified in the save string and the VIPS header contains an +ICC profile named IM_META_ICC_NAME ("icc-profile-data"), the +profile from the header will be attached. + +The image is automatically converted to RGB or Monochrome before saving. Any +metadata attached to the image is saved as EXIF, if possible. + +.B im_vips2bufjpeg() +returns the compressed image in a memory buffer. The buffer +is allocated for you, local to IMAGE descriptor +.B base. +The size of the +allocated buffer is returned in the +.B olen +parameter. You are responsible for +freeing the buffer. The buffer is only allocated if the function returns +successfully. + +.B im_vips2mimejpeg() +writes the image to stdout as a MIME image/jpeg type. It +outputs Content-Length and Content-Type fields making the result suitable for +sending to a web browser. + +.SH SEE ALSO +im_isjpeg(3). diff --git a/libsrc/conversion/man3/im_jpeg2vips_header.3 b/libsrc/conversion/man3/im_jpeg2vips_header.3 new file mode 100644 index 00000000..d7fbd38d --- /dev/null +++ b/libsrc/conversion/man3/im_jpeg2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_jpeg2vips.3 diff --git a/libsrc/conversion/man3/im_lrjoin.3 b/libsrc/conversion/man3/im_lrjoin.3 new file mode 100644 index 00000000..83de90c3 --- /dev/null +++ b/libsrc/conversion/man3/im_lrjoin.3 @@ -0,0 +1,38 @@ +.TH JOIN 3 "25 April 1991" +.SH NAME +im_lrjoin, im_tbjoin \- join two images into one +.SH SYNOPSIS +#include + +int im_lrjoin( IMAGE *im1, IMAGE *im2, IMAGE *imout ) + +int im_tbjoin( IMAGE *im1, IMAGE *im2, IMAGE *imout ) +.SH DESCRIPTION +These functions join two image left-right or top-bottom. Both input images +should have the same no of bands and the same BandFmt. Output has the same no +of bands and BandFmt as input. Only the history of the first image is kept by +the output image. + +im_lrjoin() joins two images held by image descriptors im1 and im2 and writes +the resultant byte image on the image descriptor imout. The number of the +extracted channels are identical for all images. The Xsize of imout is the +sum of the Xsizes of im1 and im2; the Ysize of imout is the min of the Ysizes +of im1 and im2. When joining im1 is on the left side of imout and im2 is on +the right side of imout. + +im_tbjoin() joins two images held by image descriptors im1 and im2 and writes +the resultant byte image on the image descriptor imout. The number of the +extracted channels are identical for all images. The Xsize of imout is the +min of the Xsizes of im1 and im2; the Ysize of imout is the sum of the Ysizes +of im1 and im2. When joining im1 is on the top side of imout and im2 is on +the bottom side of imout. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_extract(3), im_lrmerge(3), im_insert(3) +.SH COPYRIGHT +.br +K. Martinez, N. Dessipris, +.SH AUTHOR +K. Martinez and N. Dessipris \- 25/04/1991 diff --git a/libsrc/conversion/man3/im_magick2vips.3 b/libsrc/conversion/man3/im_magick2vips.3 new file mode 100644 index 00000000..f16166ea --- /dev/null +++ b/libsrc/conversion/man3/im_magick2vips.3 @@ -0,0 +1,25 @@ +.TH IM_MAGICK 3 "January 2003" +.SH NAME +im_magick2vips, im_magick2vips_header \- read images with the libMagick +library +.SH SYNOPSIS +#include + +int im_magick2vips( const char *filename, IMAGE *out ) + +int im_magick2vips_header( const char *filename, IMAGE *out ) + +.SH DESCRIPTION +.B im_magick2vips() +reads the image in filename using libMagick, and writes the image out +in VIPS format. It should be able to read any ImageMagick image, including +the float and double formats. + +.B im_magick2vips_header() +reads the just the header into the VIPS image. You can't read any +pixels! + +.SH SEE ALSO +im_ismagick(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_magick2vips_header.3 b/libsrc/conversion/man3/im_magick2vips_header.3 new file mode 100644 index 00000000..651b1cf2 --- /dev/null +++ b/libsrc/conversion/man3/im_magick2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_magick2vips.3 diff --git a/libsrc/conversion/man3/im_mask2vips.3 b/libsrc/conversion/man3/im_mask2vips.3 new file mode 100644 index 00000000..48cca147 --- /dev/null +++ b/libsrc/conversion/man3/im_mask2vips.3 @@ -0,0 +1 @@ +.so man3/im_vips2mask.3 diff --git a/libsrc/conversion/man3/im_msb.3 b/libsrc/conversion/man3/im_msb.3 new file mode 100644 index 00000000..35e45abc --- /dev/null +++ b/libsrc/conversion/man3/im_msb.3 @@ -0,0 +1,63 @@ +.TH IM_MSB 3 "06 May 2006" +.SH NAME + im_msb, im_msb_band \- Convert to uchar by discarding bits +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "int im_msb( IMAGE " "*in" ", IMAGE " "*out" " ); +.br + +.BI "int im_msb_band( IMAGE " "*in" ", IMAGE " "*out" ", int " "band" " ); +.fi +.SH DESCRIPTION +.B im_msb(3) +converts char, short, or int images (including LABQ coded ones) into unsigned +char images, very quickly, by discarding the lower order bits. Once scaled to +char, signed values are converted to unsigned by adding 128. +.PP +For a signed short (16 bit) image, +.PP +im_msb( in, out ); +.PP +is equivalent to: +.PP +im_lintra( (1.0/256.0), in, 128.0, temp ); +.br +im_clip2fmt( temp, out, IM_BANDFMT_UCHAR ); +.PP +but much faster. +.PP +For any image which uses the whole range of values for its band format, +.PP +im_msb( in, out ); +.PP +is equivalent to: +.PP +im_scale( in, out ); +.PP +but a great deal faster, and without evaluating the input twice. +.PP +.B im_msb_band(3) +is as +.BR im_msb(3) , +except that all but one of the bands are also discarded. +.PP +im_msb_band( in, out, i ); +.PP +is equivalent to: +.PP +im_msb( in, temp ); +.br +im_extract_bands( temp, out, i, 1 ); +.PP +but again, faster. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_lintra(3), im_scale(3), im_clip(3) +.SH COPYRIGHT +.br +Copyright 2006, The Nottingham Trent University. +.SH AUTHOR +Tom Vajzovic diff --git a/libsrc/conversion/man3/im_msb_band.3 b/libsrc/conversion/man3/im_msb_band.3 new file mode 100644 index 00000000..7c697b04 --- /dev/null +++ b/libsrc/conversion/man3/im_msb_band.3 @@ -0,0 +1 @@ +.so man3/im_msb.3 diff --git a/libsrc/conversion/man3/im_png2vips.3 b/libsrc/conversion/man3/im_png2vips.3 new file mode 100644 index 00000000..9b7ef388 --- /dev/null +++ b/libsrc/conversion/man3/im_png2vips.3 @@ -0,0 +1,56 @@ +.TH IM_PNG 3 "3 Jan 2003" +.SH NAME +im_png2vips, im_png2vips_header, im_png2vips \- convert PNG images to and from VIPS format +.SH SYNOPSIS +#include + +int im_png2vips( const char *filename, IMAGE *out ) + +int im_png2vips_header( const char *filename, IMAGE *out ) + +int im_vips2png( IMAGE *in, const char *filename ) + +.SH DESCRIPTION +.B im_png2vips() +reads the png image in filename, and writes the image out +in VIPS format. + +.B im_png2vips_header() +reads just the header information from the PNG file. You can't read any +pixels! + +.B im_vips2png() +reads the image in and writes a PNG file to the specified +filename. The filename may include an optional mode string, following a ':' +character, which you can use to specify compression and interlace. + +The mode string has the following syntax: + + , + +where is an integer between 0 and 9 specifying the amount of +compression (6 is the default), and is 0 for no interlace (this is +the default) and 1 for ADAM7 interlace. + +Beware that writing an interlaced image is potentially up to 7 times slower +than writing a non-interlaced image. + +The image is automatically converted to RGB or Monochrome (with an optional +alpha channel) before saving. + +.SH EXAMPLES + +The call: + + im_vips2png fred.v fred.png + +Writes a compression 6, uninterlaced PNG image. + + im_vips2png fred.v fred.tif:,1 + +Writes an interlaced image. + +.SH SEE ALSO +im_ispng(3), im_vips2tiff(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_png2vips_header.3 b/libsrc/conversion/man3/im_png2vips_header.3 new file mode 100644 index 00000000..4a4b4cd7 --- /dev/null +++ b/libsrc/conversion/man3/im_png2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_png2vips.3 diff --git a/libsrc/conversion/man3/im_ppm2vips.3 b/libsrc/conversion/man3/im_ppm2vips.3 new file mode 100644 index 00000000..8ade759e --- /dev/null +++ b/libsrc/conversion/man3/im_ppm2vips.3 @@ -0,0 +1,38 @@ +.TH IM_PPM 3 "November 2000" +.SH NAME +im_ppm2vips, im_ppm2vips_header, im_vips2ppm \- convert PPM/PGM/PBM images to and from VIPS format +.SH SYNOPSIS +#include + +int im_ppm2vips( const char *filename, IMAGE *out ) + +int im_ppm2vips_header( const char *filename, IMAGE *out ) + +int im_vips2ppm( IMAGE *in, const char *filename ) + +.SH DESCRIPTION +.B im_ppm2vips() +reads the PPM/PGM/PBM image in filename, and writes the image out +in VIPS format. It can read 1, 8, 16 and 32 bit images, colour or monochrome, +stored in binary or in ASCII. One bit images become 8 bit VIPS images, with 0 +and 255 for 0 and 1. + +.B im_ppm2vips_header() +reads the just the header of the image into the VIPS image. You can't read any +pixels! + +.B im_vips2ppm() +writes the VIPS image to the named file in PPM format. It can write 8, 16 or +32 bit integer images, colour or monochrome, stored as binary or ASCII. Images +of more than 8 bits can only be stored in ASCII. + +The storage format is indicated by a filename extension. + + im_vips2ppm( im, "fred.ppm:ascii" ) + +will write to "fred.ppm" in ascii format. The default is binary. + +.SH SEE ALSO +im_isppm(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_ppm2vips_header.3 b/libsrc/conversion/man3/im_ppm2vips_header.3 new file mode 100644 index 00000000..de0d1282 --- /dev/null +++ b/libsrc/conversion/man3/im_ppm2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_ppm2vips.3 diff --git a/libsrc/conversion/man3/im_print.3 b/libsrc/conversion/man3/im_print.3 new file mode 100644 index 00000000..e1ebc80f --- /dev/null +++ b/libsrc/conversion/man3/im_print.3 @@ -0,0 +1,21 @@ +.TH IM_PRINT 3 "January 2002" +.SH NAME +im_print \- print a string to stdout +.SH SYNOPSIS +#include + +int +.br +im_print( const char *message ) + +.SH DESCRIPTION +.B im_print() +prints the message to stdout, with a newline. Sometimes useful for debugging +language bindings. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_system(3) +.SH COPYRIGHT +2002 The National Gallery diff --git a/libsrc/conversion/man3/im_raw2vips.3 b/libsrc/conversion/man3/im_raw2vips.3 new file mode 100644 index 00000000..e50d822a --- /dev/null +++ b/libsrc/conversion/man3/im_raw2vips.3 @@ -0,0 +1,30 @@ +.TH IM_RAW2VIPS 3 "4 August 2005" +.SH NAME +im_raw2vips \- wrap a raw binary file inside an IMAGE descriptor +.SH SYNOPSIS +.B #include + +int +.br +im_raw2vips( const char *filename, IMAGE *out, +.br + int width, int height, int bpp, int offset ) + +.SH DESCRIPTION +.B im_raw2vips(3) +mmaps the file named, setting image +.B out +so that access to that image will read from the file. +The parameters specify the image width, height, bytes per pixel and offset +in bytes from the start of the file. + +Use functions like +.B im_copy_morph(3) +to set the pixel type, byte ordering and so on. + +.SH RETURN VALUE +The functions return NULL on error. +.SH SEE ALSO +im_copy_morph(3), im_copy_swap(3). +.SH COPYRIGHT +Imperial College, 2005 diff --git a/libsrc/conversion/man3/im_recomb.3 b/libsrc/conversion/man3/im_recomb.3 new file mode 100644 index 00000000..c0481d11 --- /dev/null +++ b/libsrc/conversion/man3/im_recomb.3 @@ -0,0 +1,36 @@ +.TH IM_RECOMB 3 "11 April 1990" +.SH NAME +im_recomb \- matrix recombination of image +.SH SYNOPSIS +#include + +int im_recomb(imagein, imageout, mat) +.br +IMAGE *imagein, *imageout; +.br +DOUBLEMASK *mat; + +.SH DESCRIPTION +im_recomb() recombines the elements of an m band image to form an n band image +using mat, an m by n matrix of floating point numbers. + +It calculates + + A = B x C + +where A is an n band output image, C is an m band input image and B +is an m by n matrix of floats. Can be used with a 3 by 3 matrix to perform +simple colour space transforms; 7 by 30 matrix to shrink 3rd order +development of 3 filter system to XYZ etc. + +The output type is float unless the input type is double, in which case the +output type is double. It does not work for complex image types. All +intermediates are calculated as double. Note that the width of the matrix +should be equal to the number of bands in the input image. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_read_dmask(3), im_matinv(3), im_lintra(3) +.SH COPYRIGHT +National Gallery and Birkbeck College, 1990 - 1996 diff --git a/libsrc/conversion/man3/im_replicate.3 b/libsrc/conversion/man3/im_replicate.3 new file mode 100644 index 00000000..d05a7817 --- /dev/null +++ b/libsrc/conversion/man3/im_replicate.3 @@ -0,0 +1,31 @@ +.TH IM_REPLICATE 3 "6 October 2003" +.SH NAME +im_replicate \- replicate an image horizontally and vertically +.SH SYNOPSIS +.B #include + +int +.br +im_replicate( IMAGE *in, IMAGE *out, +.br + int across, int down ) +.SH DESCRIPTION +.B im_replicate(3) +repeats image +.B in +across and down a fixed number of times to make a new larger image. + +The current implementation is optimised for the case where image +.B in +is small and +.B across +and +.B down +are large. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_extract_area(3), im_zoom(3) +.SH COPYRIGHT +National Gallery 2003 diff --git a/libsrc/conversion/man3/im_ri2c.3 b/libsrc/conversion/man3/im_ri2c.3 new file mode 100644 index 00000000..47bc803a --- /dev/null +++ b/libsrc/conversion/man3/im_ri2c.3 @@ -0,0 +1 @@ +.so man3/im_clip.3 diff --git a/libsrc/conversion/man3/im_rightshift_size.3 b/libsrc/conversion/man3/im_rightshift_size.3 new file mode 100644 index 00000000..52b6782f --- /dev/null +++ b/libsrc/conversion/man3/im_rightshift_size.3 @@ -0,0 +1,30 @@ +.TH IM_RIGHTSHIFT_SIZE 3 "12 August 2006" +.SH NAME + im_rightshift_size \- Decrease size by a power-of-two factor +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "int im_rightshift_size( IMAGE " "*in" ", IMAGE " "*out" ", int " "xshift" ", int " "yshift" ", int " "band_fmt" " ); +.fi +.SH DESCRIPTION +.B im_rightshift_size(3) +decreases the size of an integer type image by a power-of-two factor, very quickly, by summing the values of +adjacent pixels. The sum is shifted to produce output of the specified band format. +.IR xshift " and "yshift +are positive and give the base-two logarithms of the scale factors. +.PP +If the input image is a signed type, then +.I band_fmt +must be one of IM_BANDFMT_CHAR, IM_BANDFMT_SHORT or IM_BANDFMT_INT. If it is an unsigned type, then +.I band_fmt +must be one of IM_BANDFMT_UCHAR, IM_BANDFMT_USHORT or IM_BANDFMT_UINT. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_affine(3) +.SH COPYRIGHT +.br +Copyright 2006, Tom Vajzovic. +.SH AUTHOR +Tom Vajzovic diff --git a/libsrc/conversion/man3/im_rot180.3 b/libsrc/conversion/man3/im_rot180.3 new file mode 100644 index 00000000..94fba66f --- /dev/null +++ b/libsrc/conversion/man3/im_rot180.3 @@ -0,0 +1,38 @@ +.TH IM_ROTATION 3 "5 December 1991" +.SH NAME +im_rot180, im_rot270, im_rot90, im_fliphor, im_flipver \- rotate and flip +an image +.SH SYNOPSIS +.B #include + +.B int im_rot180(in, out) +.br +.B IMAGE *in, *out; + +.B int im_rot270(in, out) +.br +.B IMAGE *in, *out; + +.B int im_rot90(in, out) +.br +.B IMAGE *in, *out; + +.B int im_fliphor(in, out) +.br +.B IMAGE *in, *out; + +.B int im_flipver(in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +im_rot180(), im_rot270() and im_rot90(), rotate the input image in by 180, +270 and 90 degrees clockwise. im_fliphor() and im_flipver(), flip in +horizontally and vertically respectively. All functions work for any size +image, any type and any number of bands. All also work for LABPACK coded +images. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE\ ALSO +rot180(1) +.SH AUTHOR +N. Dessipris \- 5/12/1991 diff --git a/libsrc/conversion/man3/im_rot270.3 b/libsrc/conversion/man3/im_rot270.3 new file mode 100644 index 00000000..bf72dac4 --- /dev/null +++ b/libsrc/conversion/man3/im_rot270.3 @@ -0,0 +1 @@ +.so man3/im_rot180.3 diff --git a/libsrc/conversion/man3/im_rot90.3 b/libsrc/conversion/man3/im_rot90.3 new file mode 100644 index 00000000..bf72dac4 --- /dev/null +++ b/libsrc/conversion/man3/im_rot90.3 @@ -0,0 +1 @@ +.so man3/im_rot180.3 diff --git a/libsrc/conversion/man3/im_scale.3 b/libsrc/conversion/man3/im_scale.3 new file mode 100644 index 00000000..29915b4b --- /dev/null +++ b/libsrc/conversion/man3/im_scale.3 @@ -0,0 +1,40 @@ +.TH CONVERSIONS 3 "28 June 1990" +.SH NAME +im_scale, im_scaleps \- scale an image to unsigned char image +.SH SYNOPSIS +#include + +int im_scale(in, out) +.br +IMAGE *in, *out; + +int im_scaleps(in, out) +.br +IMAGE *in, *out; +.SH DESCRIPTION +These functions scale a non-complex image to a displayable +byte (unsigned char) image. + +im_scale() scales the image held by image descriptor to a byte (unsigned char) +image and writes the result on the image descriptor out. Sizes and the number +of the bands of out are identical to those of input. +Input image should be non complex and can have any number of channels. +The output is scaled between 0 and 255; if the image is multiband the maximum +value of all channels is set to 255 and the minimum to 0. In all cases, +rounding is performed by adding .5 to the scaled values. + +.B im_scaleps() +scales to 0 - 255 by mapping each pixel through the equation + + log10(1.0 + pow(x, 0.25)) + +and then multiplying by a factor so that the image maximum is 255. + +This transformation highlights both low and high spatial +frequencies in the power spectrum. +The images produced by this non-linear transformation are only +for display and not for further processing. +.SH RETURN VALUE +Each function returns 0 on success and -1 on error. +.SH SEE ALSO +im_any2c(3), im_c2amph(3), im_clip(3) diff --git a/libsrc/conversion/man3/im_scaleps.3 b/libsrc/conversion/man3/im_scaleps.3 new file mode 100644 index 00000000..ed46d616 --- /dev/null +++ b/libsrc/conversion/man3/im_scaleps.3 @@ -0,0 +1 @@ +.so man3/im_scale.3 diff --git a/libsrc/conversion/man3/im_slice.3 b/libsrc/conversion/man3/im_slice.3 new file mode 100644 index 00000000..2e4675e8 --- /dev/null +++ b/libsrc/conversion/man3/im_slice.3 @@ -0,0 +1 @@ +.so man3/im_thresh.3 diff --git a/libsrc/conversion/man3/im_subsample.3 b/libsrc/conversion/man3/im_subsample.3 new file mode 100644 index 00000000..c8cefe3b --- /dev/null +++ b/libsrc/conversion/man3/im_subsample.3 @@ -0,0 +1,24 @@ +.TH IM_SUBSAMPLE 3 "19 Aug 1996" +.SH NAME +im_subsample \- subsample image by integer factor +.SH SYNOPSIS +#include + +int im_subsample(in, out, xsub, ysub) +.br +IMAGE *in, *out; +.br +int xsub, ysub; + +.SH DESCRIPTION +.B im_subsample() +sub-samples image in by an x and y integer factor. It is much faster than +im_shrink(), especially for large partial images. + +It works for any image type. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_shrink(3), im_similarity(3), im_zoom(3) +.SH COPYRIGHT +1989-1996 The National Gallery and Birkbeck College diff --git a/libsrc/conversion/man3/im_system.3 b/libsrc/conversion/man3/im_system.3 new file mode 100644 index 00000000..d0adc95f --- /dev/null +++ b/libsrc/conversion/man3/im_system.3 @@ -0,0 +1,48 @@ +.TH IM_SYSTEM 3 "7 Mar 2000" +.SH NAME +im_system \- run a command on an image +.SH SYNOPSIS +#include + +int +im_system(im, cmd, out) +.br +IMAGE *im; +.br +const char *cmd; +.br +char **out; + +.SH DESCRIPTION +.B im_system() +runs a command on an image, returning the command's output as a string. This +string should be freed with +.B im_free() +when you've finished with it. + +The command is executed with the +.B system(3) +call; the first '%s' in the command being substituted for a filename. + +For example: + +im_system( im, "vips2dj %s | lpr", &result ) + +will run the command vips2dj(1) on the image, piping the result to the +printer. + +If the IMAGE is a file on disc, then the filename will be the name of the real +file. If the image is in memory, or the result of a computation, then a new +file is created in the temporary area called something like "vips_XXXXXX", and +that filename given to the command. The file is deleted when the command +finishes. + +The environment variable TMPDIR can be used to set the temporary directory. If +it is not set, it defaults to "/tmp". + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +system(3), vips2dj(1) +.SH COPYRIGHT +2000 The National Gallery and Birkbeck College diff --git a/libsrc/conversion/man3/im_tbjoin.3 b/libsrc/conversion/man3/im_tbjoin.3 new file mode 100644 index 00000000..82c54e22 --- /dev/null +++ b/libsrc/conversion/man3/im_tbjoin.3 @@ -0,0 +1 @@ +.so man3/im_lrjoin.3 diff --git a/libsrc/conversion/man3/im_text.3 b/libsrc/conversion/man3/im_text.3 new file mode 100644 index 00000000..c1b7be46 --- /dev/null +++ b/libsrc/conversion/man3/im_text.3 @@ -0,0 +1,47 @@ +.TH IM_TEXT 3 "20 May 2004" +.SH NAME +im_text \- render a utf-8 text string into an image +.SH SYNOPSIS +.B #include + +.B int im_text( out, text, font, width, alignment, dpi ) +.br +.B IMAGE *out; +.br +.B const char *text; +.br +.B const char *font; +.br +.B int width; +.br +.B int alignment; +.br +.B int dpi; + +.SH DESCRIPTION +.B im_text(3) +makes an image containing the text string rendered as a bitmap with Pango. +The result is a one band 8 bit image with 0 - 255 as black to white. The +string may contain Pango markup, for example "TheGuardian". + +Fonts are specified pango-style as "family style size", for example "sans 12" +or "times italic 14". + +The +.B width +parameter, if greater than zero, gives the line width to wrap at. + +.B alignment +can be 0, 1 or 2 for right, centre and left alignment. + +.B dpi +is the resolution to render the text at. 300 is good for print, 100 for +displays. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_plotmask(3), im_insertplace(3) +.SH COPYRIGHT +.br +National Gallery 2004 diff --git a/libsrc/conversion/man3/im_thresh.3 b/libsrc/conversion/man3/im_thresh.3 new file mode 100644 index 00000000..345fdb57 --- /dev/null +++ b/libsrc/conversion/man3/im_thresh.3 @@ -0,0 +1,46 @@ +.TH IM_THRESH 3 "26 April 1991" +.SH NAME +im_thresh, im_slice \- threshold an image +.SH SYNOPSIS +#include + +int im_thresh(in, out, threshold) +.br +IMAGE *in, *out; +.br +double threshold; + +int im_slice(in, out, threshold1, threshold2) +.br +IMAGE *in, *out; +.br +double threshold1, threshold2; +.SH DESCRIPTION +These functions have been replaced with the relational and boolean +packages - see +.B im_lessconst() +and +.B im_and() +for much better ways of doing this. + +These functions operate on any non-complex input. The output image is a +unsigned char image with the same sizes and the same number of channels as +input. + +im_slice() thresholds the image held by image descriptor in and writes the +result on the image descriptor out. Output is a byte image with values less +than threshold1) set to 0, values in [threshold1, threshold2) set to 128 and +values greater than threshold2 set to 255 (x in range [a,b) means a<=x + +int im_tiff2vips( const char *filename, IMAGE *out ) + +int im_tiff2vips_header( const char *filename, IMAGE *out ) + +int im_vips2tiff( IMAGE *in, const char *filename ) + +.SH DESCRIPTION +.B im_tiff2vips(3) +reads the tiff image in filename, and writes the image out +in VIPS format. It is a full baseline TIFF 6 reader, with extensions for +tiled images, multipage images, LAB colour space, pyramidal images and +JPEG compression. + +You can embed options in the filename. They have the form: + + filename.tif: + +.B page-number +lets you read a particular page out of a multipage TIFF file. For +example: + + "fred.tif:12" + +will read page 12 (numbering from page zero) from the TIFF image. + +Use +.B im_istifftiled(3) +and +.B im_istiffpyramid(3) +to find the TIFF image type before calling. + +.B im_tiff2vips_header(3) +reads just the header information from the TIFF file. You can't read any +pixels! + +.B im_vips2tiff(3) +reads the image in and writes a TIFF file to the specified +filename. The filename may include an optional mode string, following a ':' +character. For example, "fred.tif" would write a default TIFF file (flat, +strips, no compression). Writing to "fred.tif:deflate,tile:64x64" would write a +ZIP-coded image, split into 64 by 64 pixel tiles. + +The mode string has the following syntax: + + ,,,,, + +where is one of: + + "none" - no compression + "jpeg" - JPEG compression + "deflate" - ZIP (deflate) compression + "packbits" - TIFF packbits compression + "ccittfax4" - CCITT Group 4 fax encoding + "lzw" - Lempel-Ziv compression + +"jpeg" compression can be followed by a ":" character and a JPEG quality +level; "lzw" and "deflate" can be followed by a ":" and predictor value. The +default compression type is "none", the default JPEG quality factor is 75. + +Predictor is not set by default. There are three predictor values recognised +at the moment (2007, July): 1 is no prediction, 2 is a horizontal differencing +and 3 is a floating point predictor. Refer to the libtiff specifications for +further discussion of various predictors. In short, predictor helps to better +compress image, especially in case of digital photos or scanned images and bit +depths > 8. Try it to find whether it works for your images. + +JPEG compression is a good lossy compressor for photographs, packbits is good +for 1-bit images, and deflate is the best lossless compression TIFF can do. +LZW has patent problems and is no longer recommended. + + is one of: + + "strip" - strip layout + "tile" - tiled layout + +"tile" layout can be followed by a ":" character and the horizontal and +vertical tile size, separated by a "x" character. The default layout is +"strip", and the default tile size is 128 by 128 pixels. + + is one of: + + "flat" - single image + "pyramid" - many images arranged in a pyramid + +The default multi-res mode is "flat". + + is one of: + + "manybit" - don't bit-reduce images + "onebit" - one band 8 bit images are saved as 1 bit + +The default format is "multibit". + + is one of: + + "res_cm" - output resolution unit is pixels per centimetre + "res_inch" - output resolution unit is pixels per inch + +The default format is "res_cm". The unit can optionally be followed by a +":" character and the horizontal and vertical resolution, separated by a "x" +character. You can have a single number with no "x" and set the horizontal +and vertical resolutions together. The default unit is cm, and the default +resolution is taken from the VIPS header. + + is the filename of an ICC profile to embed in the TIFF file + +The TIFF reader and writer are based on Sam Leffler's TIFF library, and the IJG +JPEG coder. + +.SH EXAMPLES + +The call: + + im_vips2tiff fred.v fred.tif + +Writes a striped, uncompressed TIFF image. Almost anything should be able to +read this. + + im_vips2tiff fred.v fred.tif:jpeg,tile,pyramid + +Writes a tiled JPEG pyramid image. Although VIPS tries to follow the TIFF +specification carefully, you may have trouble reading this on any system other +than VIPS. + + im_vips2tiff fred.v fred.tif:jpeg:25,tile:64x64 + +Writes a highly compressed JPEG image, with a tile size of 64 by 64 pixels. + + im_vips2tiff fred.v fred.tif:,tile + +Writes an uncompressed tiled image. + + im_vips2tiff fred.v fred.tif:packbits,tile,,onebit + +Writes a tiled one bit TIFF image (provided fred.v is a one band 8 bit image) +compressed with packbits. + + im_vips2tiff fred.v fred.tif:,,,,res_inch:300 + +Writes fred.v as a tiff file, with the resolution in the tiff header set to +300 dpi. + +.SH SEE ALSO +im_istiff(3), im_istifftiled(3), im_istiffpyramid(3) +.SH COPYRIGHT +Hey, you want this? You have it! diff --git a/libsrc/conversion/man3/im_tiff2vips_header.3 b/libsrc/conversion/man3/im_tiff2vips_header.3 new file mode 100644 index 00000000..78ab0c6a --- /dev/null +++ b/libsrc/conversion/man3/im_tiff2vips_header.3 @@ -0,0 +1 @@ +.so man3/im_tiff2vips.3 diff --git a/libsrc/conversion/man3/im_tile_cache.3 b/libsrc/conversion/man3/im_tile_cache.3 new file mode 100644 index 00000000..181c253b --- /dev/null +++ b/libsrc/conversion/man3/im_tile_cache.3 @@ -0,0 +1,45 @@ +.TH IM_TILE_CACHE 3 "19 May 2006" +.SH NAME +im_tile_cache \- cache an image, tilewise +.SH SYNOPSIS +#include + +int +.br +im_tile_cache( IMAGE *in, IMAGE *out, +.br + int tile_width, int tile_height, int max_tiles ); + +.SH DESCRIPTION +.B tile_cache(3) +behaves rather like +.B im_copy(3) +between images +.B in +and +.B out, +except that it keeps a cache of computed pixels. This cache is made of up to +.B max_tiles +tiles (a value of -1 for +.B max +means any number of tiles), and each tile is of size +.B tile_width +by +.B tile_height +pixels. Each cache tile is made with a single call to +.B im_prepare(3). + +This is a lower-level operation than +.B im_cache(3) +since it does no subdivision. It is suitable for caching the output of +operations like +.B im_exr2vips(3) +on tiled images. + +.SH RETURN VALUE +The function returns 0 on success, and non-zero on error, setting +im_error(). +.SH SEE ALSO +im_prepare(3) +.SH AUTHOR +J Cupitt, 2006 diff --git a/libsrc/conversion/man3/im_vips2bufjpeg.3 b/libsrc/conversion/man3/im_vips2bufjpeg.3 new file mode 100644 index 00000000..d7fbd38d --- /dev/null +++ b/libsrc/conversion/man3/im_vips2bufjpeg.3 @@ -0,0 +1 @@ +.so man3/im_jpeg2vips.3 diff --git a/libsrc/conversion/man3/im_vips2csv.3 b/libsrc/conversion/man3/im_vips2csv.3 new file mode 100644 index 00000000..e4558b4a --- /dev/null +++ b/libsrc/conversion/man3/im_vips2csv.3 @@ -0,0 +1 @@ +.so man3/im_csv2vips.3 diff --git a/libsrc/conversion/man3/im_vips2jpeg.3 b/libsrc/conversion/man3/im_vips2jpeg.3 new file mode 100644 index 00000000..d7fbd38d --- /dev/null +++ b/libsrc/conversion/man3/im_vips2jpeg.3 @@ -0,0 +1 @@ +.so man3/im_jpeg2vips.3 diff --git a/libsrc/conversion/man3/im_vips2mask.3 b/libsrc/conversion/man3/im_vips2mask.3 new file mode 100644 index 00000000..9be32f55 --- /dev/null +++ b/libsrc/conversion/man3/im_vips2mask.3 @@ -0,0 +1,26 @@ +.TH IM_VIPS2MASK 3 "6 June 1994" +.SH NAME +im_vips2mask, im_mask2vips \- convert between masks and images +.SH SYNOPSIS +.B #include + +DOUBLEMASK *im_vips2mask( IMAGE *in, char *out_name ) + +int im_mask2vips( DOUBLEMASK *in, IMAGE *out ) +.SH DESCRIPTION +.B im_vips2mask(3) +returns a DOUBLEMASK of name out_name containing the data from IMAGE in. +Images must have either one band, a width of 1, or a height of 1. +Returns NULL on error, and a new mask on success. +The scale field is set to 1.0 and the offset to 0.0. + +.B im_mask2vips(3) +writes the doubles held in mask in to the IMAGE out. It creates a one-band +image of the same size as in. It returns 0 on success and -1 on error. The +scale and offset fields are ignored. + +.SH SEE ALSO +im_openin(3), im_openout(3), im_setbuf(3), edvips(3), im_create_dmask(3). +.SH COPYRIGHT +.br +National Gallery, 1994 diff --git a/libsrc/conversion/man3/im_vips2mimejpeg.3 b/libsrc/conversion/man3/im_vips2mimejpeg.3 new file mode 100644 index 00000000..d7fbd38d --- /dev/null +++ b/libsrc/conversion/man3/im_vips2mimejpeg.3 @@ -0,0 +1 @@ +.so man3/im_jpeg2vips.3 diff --git a/libsrc/conversion/man3/im_vips2png.3 b/libsrc/conversion/man3/im_vips2png.3 new file mode 100644 index 00000000..4a4b4cd7 --- /dev/null +++ b/libsrc/conversion/man3/im_vips2png.3 @@ -0,0 +1 @@ +.so man3/im_png2vips.3 diff --git a/libsrc/conversion/man3/im_vips2ppm.3 b/libsrc/conversion/man3/im_vips2ppm.3 new file mode 100644 index 00000000..de0d1282 --- /dev/null +++ b/libsrc/conversion/man3/im_vips2ppm.3 @@ -0,0 +1 @@ +.so man3/im_ppm2vips.3 diff --git a/libsrc/conversion/man3/im_vips2tiff.3 b/libsrc/conversion/man3/im_vips2tiff.3 new file mode 100644 index 00000000..78ab0c6a --- /dev/null +++ b/libsrc/conversion/man3/im_vips2tiff.3 @@ -0,0 +1 @@ +.so man3/im_tiff2vips.3 diff --git a/libsrc/conversion/man3/im_zoom.3 b/libsrc/conversion/man3/im_zoom.3 new file mode 100644 index 00000000..440fdd84 --- /dev/null +++ b/libsrc/conversion/man3/im_zoom.3 @@ -0,0 +1,23 @@ +.TH IM_ZOOM 3 "11 April 1990" +.SH NAME +im_zoom \- zoom an image +.SH SYNOPSIS +.B #include + +.B int im_zoom(in, out, xfac, yfac) +.br +.B IMAGE *in, *out; +.B int xfac, yfac; + +.SH DESCRIPTION +.B im_zoom() +zooms (ie. nearest neighbour upsampling) the image held by the image +descriptor in by factors xfac and yfac and writes the result to the image +descriptor out. Works for any type of image, even LABPACK. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_copy(3), im_affine(3). +.SH COPYRIGHT +National Gallery, 1994. diff --git a/libsrc/convolution/Makefile.am b/libsrc/convolution/Makefile.am new file mode 100644 index 00000000..fb6e4f54 --- /dev/null +++ b/libsrc/convolution/Makefile.am @@ -0,0 +1,33 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libconvolution.la + +libconvolution_la_SOURCES = \ + rotmask.c \ + rw_mask.c \ + convol_dispatch.c \ + im_addgnoise.c \ + im_compass.c \ + im_conv.c \ + im_convf.c \ + im_convsep.c \ + im_convsepf.c \ + im_convsub.c \ + im_contrast_surface.c \ + im_embed.c \ + im_fastcor.c \ + im_gaussmasks.c \ + im_gaussnoise.c \ + im_gradcor.c \ + im_logmasks.c \ + im_rank_image.c \ + im_mpercent.c \ + im_rank.c \ + im_resize_linear.c \ + im_sharpen.c \ + im_shrink.c \ + im_spcor.c \ + im_stretch3.c \ + im_zerox.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/convolution/convol_dispatch.c b/libsrc/convolution/convol_dispatch.c new file mode 100644 index 00000000..0bf098e2 --- /dev/null +++ b/libsrc/convolution/convol_dispatch.c @@ -0,0 +1,1407 @@ +/* VIPS function dispatch tables for convolution. + * + * J. Cupitt, 14/2/95. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Args to im_stretch3. + */ +static im_arg_desc stretch3_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "xdisp" ), + IM_INPUT_DOUBLE( "ydisp" ) +}; + +/* Call im_stretch3 via arg vector. + */ +static int +stretch3_vec( im_object *argv ) +{ + double xdisp = *((int *) argv[2]); + double ydisp = *((int *) argv[3]); + + return( im_stretch3( argv[0], argv[1], xdisp, ydisp ) ); +} + +/* Description of im_stretch3. + */ +static im_function stretch3_desc = { + "im_stretch3", /* Name */ + "stretch 3%, sub-pixel displace by xdisp/ydisp", + IM_FN_PIO, /* Flags */ + stretch3_vec, /* Dispatch function */ + IM_NUMBER( stretch3_args ), /* Size of arg list */ + stretch3_args /* Arg list */ +}; + +/* Args to im_contrast_surface. + */ +static im_arg_desc contrast_surface_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "half_win_size" ), + IM_INPUT_INT( "spacing" ) +}; + +/* Call im_contrast_surface via arg vector. + */ +static int +contrast_surface_vec( im_object *argv ) +{ + int half_win_size = *((int *) argv[2]); + int spacing = *((int *) argv[3]); + + return( im_contrast_surface( argv[0], argv[1], + half_win_size, spacing ) ); +} + +/* Description of im_contrast_surface. + */ +static im_function contrast_surface_desc = { + "im_contrast_surface", /* Name */ + "find high-contrast points in an image", + IM_FN_PIO, /* Flags */ + contrast_surface_vec, /* Dispatch function */ + IM_NUMBER( contrast_surface_args ),/* Size of arg list */ + contrast_surface_args /* Arg list */ +}; + +/* Args to im_contrast_surface_raw. + */ +static im_arg_desc contrast_surface_raw_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "half_win_size" ), + IM_INPUT_INT( "spacing" ) +}; + +/* Call im_contrast_surface_raw via arg vector. + */ +static int +contrast_surface_raw_vec( im_object *argv ) +{ + int half_win_size = *((int *) argv[2]); + int spacing = *((int *) argv[3]); + + return( im_contrast_surface_raw( argv[0], argv[1], + half_win_size, spacing ) ); +} + +/* Description of im_contrast_surface_raw. + */ +static im_function contrast_surface_raw_desc = { + "im_contrast_surface_raw", /* Name */ + "find high-contrast points in an image", + IM_FN_PIO, /* Flags */ + contrast_surface_raw_vec, /* Dispatch function */ + IM_NUMBER( contrast_surface_raw_args ),/* Size of arg list */ + contrast_surface_raw_args /* Arg list */ +}; + +/* Args to im_rank. + */ +static im_arg_desc rank_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xsize" ), + IM_INPUT_INT( "ysize" ), + IM_INPUT_INT( "n" ) +}; + +/* Call im_rank via arg vector. + */ +static int +rank_vec( im_object *argv ) +{ + int xsize = *((int *) argv[2]); + int ysize = *((int *) argv[3]); + int n = *((int *) argv[4]); + + return( im_rank( argv[0], argv[1], xsize, ysize, n ) ); +} + +/* Description of im_rank. + */ +static im_function rank_desc = { + "im_rank", /* Name */ + "rank filter nth element of xsize/ysize window", + IM_FN_PIO, /* Flags */ + rank_vec, /* Dispatch function */ + IM_NUMBER( rank_args ), /* Size of arg list */ + rank_args /* Arg list */ +}; + +/* Call im_rank_raw via arg vector. + */ +static int +rank_raw_vec( im_object *argv ) +{ + int xsize = *((int *) argv[2]); + int ysize = *((int *) argv[3]); + int n = *((int *) argv[4]); + + return( im_rank_raw( argv[0], argv[1], xsize, ysize, n ) ); +} + +/* Description of im_rank_raw. + */ +static im_function rank_raw_desc = { + "im_rank_raw", /* Name */ + "rank filter nth element of xsize/ysize window, no border", + IM_FN_PIO, /* Flags */ + rank_raw_vec, /* Dispatch function */ + IM_NUMBER( rank_args ), /* Size of arg list */ + rank_args /* Arg list */ +}; + +/* Args to im_sharpen. + */ +static im_arg_desc sharpen_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "mask_size" ), + IM_INPUT_DOUBLE( "x1" ), + IM_INPUT_DOUBLE( "y2" ), + IM_INPUT_DOUBLE( "y3" ), + IM_INPUT_DOUBLE( "m1" ), + IM_INPUT_DOUBLE( "m2" ) +}; + +/* Call im_sharpen via arg vector. + */ +static int +sharpen_vec( im_object *argv ) +{ + int mask_size = *((int *) argv[2]); + double x1 = *((double *) argv[3]); + double x2 = *((double *) argv[4]); + double x3 = *((double *) argv[5]); + double m1 = *((double *) argv[6]); + double m2 = *((double *) argv[7]); + + return( im_sharpen( argv[0], argv[1], mask_size, x1, x2, x3, m1, m2 ) ); +} + +/* Description of im_sharpen. + */ +static im_function sharpen_desc = { + "im_sharpen", /* Name */ + "sharpen high frequencies of L channel of LabQ", + IM_FN_PIO, /* Flags */ + sharpen_vec, /* Dispatch function */ + IM_NUMBER( sharpen_args ), /* Size of arg list */ + sharpen_args /* Arg list */ +}; + +/* Args to im_addgnoise. + */ +static im_arg_desc addgnoise_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "sigma" ) +}; + +/* Call im_addgnoise via arg vector. + */ +static int +addgnoise_vec( im_object *argv ) +{ + double sigma = *((double *) argv[2]); + + return( im_addgnoise( argv[0], argv[1], sigma ) ); +} + +/* Description of im_addgnoise. + */ +static im_function addgnoise_desc = { + "im_addgnoise", /* Name */ + "add gaussian noise with mean 0 and std. dev. sigma", + IM_FN_PIO, /* Flags */ + addgnoise_vec, /* Dispatch function */ + IM_NUMBER( addgnoise_args ), /* Size of arg list */ + addgnoise_args /* Arg list */ +}; + +/* Args for im_read_dmask() + */ +static im_arg_desc read_dmask_args[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_DMASK( "mask" ) +}; + +/* Call im_read_dmask via arg vector. + */ +static int +read_dmask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[1]; + + if( !(mo->mask = im_read_dmask( argv[0] )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_read_dmask(). + */ +static im_function read_dmask_desc = { + "im_read_dmask", /* Name */ + "read matrix of double from file", + 0, /* Flags */ + read_dmask_vec, /* Dispatch function */ + IM_NUMBER( read_dmask_args ), /* Size of arg list */ + read_dmask_args /* Arg list */ +}; + +/* Args for convolver with imask. + */ +static im_arg_desc conv_imask[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMASK( "matrix" ) +}; + +/* Args for convolver with dmask. + */ +static im_arg_desc conv_dmask[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DMASK( "matrix" ) +}; + +/* Call im_compass via arg vector. + */ +static int +compass_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_compass( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_compass. + */ +static im_function compass_desc = { + "im_compass", /* Name */ + "convolve with 8-way rotating integer mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + compass_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_conv via arg vector. + */ +static int +conv_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_conv( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_conv. + */ +static im_function conv_desc = { + "im_conv", /* Name */ + "convolve", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + conv_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_conv_raw via arg vector. + */ +static int +conv_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_conv_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_conv_raw. + */ +static im_function conv_raw_desc = { + "im_conv_raw", /* Name */ + "convolve, no border", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + conv_raw_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_convf via arg vector. + */ +static int +convf_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convf( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convf. + */ +static im_function convf_desc = { + "im_convf", /* Name */ + "convolve, with DOUBLEMASK", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + convf_vec, /* Dispatch function */ + IM_NUMBER( conv_dmask ), /* Size of arg list */ + conv_dmask /* Arg list */ +}; + +/* Call im_convf_raw via arg vector. + */ +static int +convf_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convf_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convf_raw. + */ +static im_function convf_raw_desc = { + "im_convf_raw", /* Name */ + "convolve, with DOUBLEMASK, no border", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + convf_raw_vec, /* Dispatch function */ + IM_NUMBER( conv_dmask ), /* Size of arg list */ + conv_dmask /* Arg list */ +}; + +/* Call im_convsep via arg vector. + */ +static int +convsep_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convsep( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convsep. + */ +static im_function convsep_desc = { + "im_convsep", /* Name */ + "seperable convolution", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + convsep_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_convsep_raw via arg vector. + */ +static int +convsep_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convsep_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convsep_raw. + */ +static im_function convsep_raw_desc = { + "im_convsep_raw", /* Name */ + "seperable convolution, no border", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + convsep_raw_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_convsepf via arg vector. + */ +static int +convsepf_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convsepf( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convsepf. + */ +static im_function convsepf_desc = { + "im_convsepf", /* Name */ + "seperable convolution, with DOUBLEMASK", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + convsepf_vec, /* Dispatch function */ + IM_NUMBER( conv_dmask ), /* Size of arg list */ + conv_dmask /* Arg list */ +}; + +/* Call im_convsepf_raw via arg vector. + */ +static int +convsepf_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_convsepf_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_convsepf_raw. + */ +static im_function convsepf_raw_desc = { + "im_convsepf_raw", /* Name */ + "seperable convolution, with DOUBLEMASK, no border", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + convsepf_raw_vec, /* Dispatch function */ + IM_NUMBER( conv_dmask ), /* Size of arg list */ + conv_dmask /* Arg list */ +}; + +/* Args for im_convsub. + */ +static im_arg_desc convsub_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMASK( "matrix" ), + IM_INPUT_INT( "xskip" ), + IM_INPUT_INT( "yskip" ) +}; + +/* Call im_convsub via arg vector. + */ +static int +convsub_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + int xskip = *((int *) argv[3]); + int yskip = *((int *) argv[4]); + + return( im_convsub( argv[0], argv[1], mo->mask, xskip, yskip ) ); +} + +/* Description of im_convsub. + */ +static im_function convsub_desc = { + "im_convsub", /* Name */ + "convolve uchar to uchar, sub-sampling by xskip, yskip", + IM_FN_TRANSFORM, /* Flags */ + convsub_vec, /* Dispatch function */ + IM_NUMBER( convsub_args ), /* Size of arg list */ + convsub_args /* Arg list */ +}; + +/* Call im_fastcor via arg vector. + */ +static int +fastcor_vec( im_object *argv ) +{ + return( im_fastcor( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_fastcor. + */ +static im_function fastcor_desc = { + "im_fastcor", /* Name */ + "fast correlate in2 within in1", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + fastcor_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_fastcor_raw via arg vector. + */ +static int +fastcor_raw_vec( im_object *argv ) +{ + return( im_fastcor_raw( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_fastcor_raw. + */ +static im_function fastcor_raw_desc = { + "im_fastcor_raw", /* Name */ + "fast correlate in2 within in1, no border", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + fastcor_raw_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args for im_gauss_dmask. + */ +static im_arg_desc gauss_dmask_args[] = { + IM_OUTPUT_DMASK( "mask" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "min_amp" ) +}; + +/* Call im_gauss_dmask via arg vector. + */ +static int +gauss_dmask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + double sigma = *((double *) argv[1]); + double min_amp = *((double *) argv[2]); + + if( !(mo->mask = + im_gauss_dmask( mo->name, sigma, min_amp )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_gauss_dmask. + */ +static im_function gauss_dmask_desc = { + "im_gauss_dmask", /* Name */ + "generate gaussian DOUBLEMASK", + 0, /* Flags */ + gauss_dmask_vec, /* Dispatch function */ + IM_NUMBER( gauss_dmask_args ), /* Size of arg list */ + gauss_dmask_args /* Arg list */ +}; + +/* Args for im_gauss_imask. + */ +static im_arg_desc gauss_imask_args[] = { + IM_OUTPUT_IMASK( "mask" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "min_amp" ) +}; + +/* Call im_gauss_imask via arg vector. + */ +static int +gauss_imask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + double sigma = *((double *) argv[1]); + double min_amp = *((double *) argv[2]); + + if( !(mo->mask = + im_gauss_imask( mo->name, sigma, min_amp )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_gauss_imask. + */ +static im_function gauss_imask_desc = { + "im_gauss_imask", /* Name */ + "generate gaussian INTMASK", + 0, /* Flags */ + gauss_imask_vec, /* Dispatch function */ + IM_NUMBER( gauss_imask_args ), /* Size of arg list */ + gauss_imask_args /* Arg list */ +}; + +/* Args for im_gaussnoise. + */ +static im_arg_desc gaussnoise_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xsize" ), + IM_INPUT_INT( "ysize" ), + IM_INPUT_DOUBLE( "mean" ), + IM_INPUT_DOUBLE( "sigma" ) +}; + +/* Call im_gaussnoise via arg vector. + */ +static int +gaussnoise_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + double mean = *((double *) argv[3]); + double sigma = *((double *) argv[4]); + + if( im_gaussnoise( argv[0], xsize, ysize, mean, sigma ) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_gaussnoise. + */ +static im_function gaussnoise_desc = { + "im_gaussnoise", /* Name */ + "generate image of gaussian noise with specified statistics", + IM_FN_PIO, /* Flags */ + gaussnoise_vec, /* Dispatch function */ + IM_NUMBER( gaussnoise_args ), /* Size of arg list */ + gaussnoise_args /* Arg list */ +}; + +/* Call im_grad_x via arg vector. + */ +static int +grad_x_vec( im_object *argv ) +{ + return( im_grad_x( argv[0], argv[1] ) ); +} + +/* Description of im_grad_x. + */ +static im_function grad_x_desc = { + "im_grad_x", /* Name */ + "x component of gradient of image", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + grad_x_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_grad_y via arg vector. + */ +static int +grad_y_vec( im_object *argv ) +{ + return( im_grad_y( argv[0], argv[1] ) ); +} + +/* Description of im_grad_y. + */ +static im_function grad_y_desc = { + "im_grad_y", /* Name */ + "y component of gradient of image", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + grad_y_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_gradcor via arg vector. + */ +static int +gradcor_vec( im_object *argv ) +{ + return( im_gradcor( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_gradcor. + */ +static im_function gradcor_desc = { + "im_gradcor", /* Name */ + "non-normalised correlation of gradient of in2 within in1", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + gradcor_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_gradcor_raw via arg vector. + */ +static int +gradcor_raw_vec( im_object *argv ) +{ + return( im_gradcor_raw( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_gradcor_raw. + */ +static im_function gradcor_raw_desc = { + "im_gradcor_raw", /* Name */ + "non-normalised correlation of gradient of in2 within in1, no padding", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + gradcor_raw_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_gradient via arg vector. + */ +static int +gradient_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_gradient( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_gradient. + */ +static im_function gradient_desc = { + "im_gradient", /* Name */ + "convolve with 2-way rotating mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + gradient_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Call im_lindetect via arg vector. + */ +static int +lindetect_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_lindetect( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_lindetect. + */ +static im_function lindetect_desc = { + "im_lindetect", /* Name */ + "convolve with 4-way rotating mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + lindetect_vec, /* Dispatch function */ + IM_NUMBER( conv_imask ), /* Size of arg list */ + conv_imask /* Arg list */ +}; + +/* Args for im_log_imask. + */ +static im_arg_desc log_imask_args[] = { + IM_OUTPUT_IMASK( "mask" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "min_amp" ) +}; + +/* Call im_log_imask via arg vector. + */ +static int +log_imask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + double sigma = *((double *) argv[1]); + double min_amp = *((double *) argv[2]); + + if( !(mo->mask = + im_log_imask( mo->name, sigma, min_amp )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_log_imask. + */ +static im_function log_imask_desc = { + "im_log_imask", /* Name */ + "generate laplacian of gaussian INTMASK", + 0, /* Flags */ + log_imask_vec, /* Dispatch function */ + IM_NUMBER( log_imask_args ), /* Size of arg list */ + log_imask_args /* Arg list */ +}; + +/* Args for im_log_dmask. + */ +static im_arg_desc log_dmask_args[] = { + IM_OUTPUT_DMASK( "maskfile" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "min_amp" ) +}; + +/* Call im_log_dmask via arg vector. + */ +static int +log_dmask_vec( im_object *argv ) +{ + im_mask_object *mo = argv[0]; + double sigma = *((double *) argv[1]); + double min_amp = *((double *) argv[2]); + + if( !(mo->mask = + im_log_dmask( mo->name, sigma, min_amp )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_log_dmask. + */ +static im_function log_dmask_desc = { + "im_log_dmask", /* Name */ + "generate laplacian of gaussian DOUBLEMASK", + 0, /* Flags */ + log_dmask_vec, /* Dispatch function */ + IM_NUMBER( log_dmask_args ), /* Size of arg list */ + log_dmask_args /* Arg list */ +}; + +/* Args for im_resize_linear. + */ +static im_arg_desc resize_linear_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "X" ), + IM_INPUT_INT( "Y" ) +}; + +/* Call im_resize_linear via arg vector. + */ +static int +resize_linear_vec( im_object *argv ) +{ + int X = *((int *) argv[2]); + int Y = *((int *) argv[3]); + + return( im_resize_linear( argv[0], argv[1], X, Y ) ); +} + +/* Description of im_resize_linear. + */ +static im_function resize_linear_desc = { + "im_resize_linear", /* Name */ + "resize to X by Y pixels with linear interpolation", + 0, /* Flags */ + resize_linear_vec, /* Dispatch function */ + IM_NUMBER( resize_linear_args ), /* Size of arg list */ + resize_linear_args /* Arg list */ +}; + +/* Args for im_mpercent. + */ +static im_arg_desc mpercent_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_DOUBLE( "percent" ), + IM_OUTPUT_INT( "thresh" ) +}; + +/* Call im_mpercent via arg vector. + */ +static int +mpercent_vec( im_object *argv ) +{ + double percent = *((double *) argv[1]); + + return( im_mpercent( argv[0], percent, argv[2] ) ); +} + +/* Description of im_mpercent. + */ +static im_function mpercent_desc = { + "im_mpercent", /* Name */ + "find threshold above which there are percent values", + 0, /* Flags */ + mpercent_vec, /* Dispatch function */ + IM_NUMBER( mpercent_args ), /* Size of arg list */ + mpercent_args /* Arg list */ +}; + +/* Args for im_shrink. + */ +static im_arg_desc shrink_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "xfac" ), + IM_INPUT_DOUBLE( "yfac" ) +}; + +/* Call im_shrink via arg vector. + */ +static int +shrink_vec( im_object *argv ) +{ + double xshrink = *((double *) argv[2]); + double yshrink = *((double *) argv[3]); + + return( im_shrink( argv[0], argv[1], xshrink, yshrink ) ); +} + +/* Description of im_shrink. + */ +static im_function shrink_desc = { + "im_shrink", /* Name */ + "shrink image by xfac, yfac times", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + shrink_vec, /* Dispatch function */ + IM_NUMBER( shrink_args ), /* Size of arg list */ + shrink_args /* Arg list */ +}; + +/* Call im_spcor via arg vector. + */ +static int +spcor_vec( im_object *argv ) +{ + return( im_spcor( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_spcor. + */ +static im_function spcor_desc = { + "im_spcor", /* Name */ + "normalised correlation of in2 within in1", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + spcor_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_spcor_raw via arg vector. + */ +static int +spcor_raw_vec( im_object *argv ) +{ + return( im_spcor_raw( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_spcor_raw. + */ +static im_function spcor_raw_desc = { + "im_spcor_raw", /* Name */ + "normalised correlation of in2 within in1, no black padding", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + spcor_raw_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_spcor2 via arg vector. + */ +static int +spcor2_vec( im_object *argv ) +{ + return( im_spcor2( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_spcor2. + */ +static im_function spcor2_desc = { + "im_spcor2", /* Name */ + "normalised correlation of in2 within in1", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + spcor2_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_spcor2_raw via arg vector. + */ +static int +spcor2_raw_vec( im_object *argv ) +{ + return( im_spcor2_raw( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_spcor2_raw. + */ +static im_function spcor2_raw_desc = { + "im_spcor2_raw", /* Name */ + "normalised correlation of in2 within in1, no black padding", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + spcor2_raw_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Args for im_zerox. + */ +static im_arg_desc zerox_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "flag" ) +}; + +/* Call im_zerox via arg vector. + */ +static int +zerox_vec( im_object *argv ) +{ + int flag = *((int *) argv[2]); + + return( im_zerox( argv[0], argv[1], flag ) ); +} + +/* Description of im_zerox. + */ +static im_function zerox_desc = { + "im_zerox", /* Name */ + "find +ve or -ve zero crossings in image", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + zerox_vec, /* Dispatch function */ + IM_NUMBER( zerox_args ), /* Size of arg list */ + zerox_args /* Arg list */ +}; + +/* Args for im_embed. + */ +static im_arg_desc embed_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "type" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ), + IM_INPUT_INT( "w" ), + IM_INPUT_INT( "h" ) +}; + +/* Call im_embed via arg vector. + */ +static int +embed_vec( im_object *argv ) +{ + int type = *((int *) argv[2]); + int x = *((int *) argv[3]); + int y = *((int *) argv[4]); + int w = *((int *) argv[5]); + int h = *((int *) argv[6]); + + return( im_embed( argv[0], argv[1], type, x, y, w, h ) ); +} + +/* Description of im_embed. + */ +static im_function embed_desc = { + "im_embed", /* Name */ + "embed in within a set of borders", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + embed_vec, /* Dispatch function */ + IM_NUMBER( embed_args ), /* Size of arg list */ + embed_args /* Arg list */ +}; + +/* Mask functions! + */ +static im_arg_desc imask_args[] = { + IM_INPUT_IMASK( "in" ), + IM_OUTPUT_IMASK( "out" ) +}; + +static im_arg_desc dmask_args[] = { + IM_INPUT_DMASK( "in" ), + IM_OUTPUT_DMASK( "out" ) +}; + +/* Call im_rotate_imask45 via arg vector. + */ +static int +rotate_imask45_vec( im_object *argv ) +{ + im_mask_object *min = argv[0]; + im_mask_object *mout = argv[1]; + + if( !(mout->mask = im_rotate_imask45( min->mask, mout->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_rotate_imask45. + */ +static im_function rotate_imask45_desc = { + "im_rotate_imask45", /* Name */ + "rotate INTMASK clockwise by 45 degrees", + 0, /* Flags */ + rotate_imask45_vec, /* Dispatch function */ + IM_NUMBER( imask_args ), /* Size of arg list */ + imask_args /* Arg list */ +}; + +/* Call im_rotate_imask90 via arg vector. + */ +static int +rotate_imask90_vec( im_object *argv ) +{ + im_mask_object *min = argv[0]; + im_mask_object *mout = argv[1]; + + if( !(mout->mask = im_rotate_imask90( min->mask, mout->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_rotate_imask90. + */ +static im_function rotate_imask90_desc = { + "im_rotate_imask90", /* Name */ + "rotate INTMASK clockwise by 90 degrees", + 0, /* Flags */ + rotate_imask90_vec, /* Dispatch function */ + IM_NUMBER( imask_args ), /* Size of arg list */ + imask_args /* Arg list */ +}; + +/* Call im_rotate_dmask45 via arg vector. + */ +static int +rotate_dmask45_vec( im_object *argv ) +{ + im_mask_object *min = argv[0]; + im_mask_object *mout = argv[1]; + + if( !(mout->mask = im_rotate_dmask45( min->mask, mout->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_rotate_dmask45. + */ +static im_function rotate_dmask45_desc = { + "im_rotate_dmask45", /* Name */ + "rotate DOUBLEMASK clockwise by 45 degrees", + 0, /* Flags */ + rotate_dmask45_vec, /* Dispatch function */ + IM_NUMBER( dmask_args ), /* Size of arg list */ + dmask_args /* Arg list */ +}; + +/* Call im_rotate_dmask90 via arg vector. + */ +static int +rotate_dmask90_vec( im_object *argv ) +{ + im_mask_object *min = argv[0]; + im_mask_object *mout = argv[1]; + + if( !(mout->mask = im_rotate_dmask90( min->mask, mout->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_rotate_dmask90. + */ +static im_function rotate_dmask90_desc = { + "im_rotate_dmask90", /* Name */ + "rotate DOUBLEMASK clockwise by 90 degrees", + 0, /* Flags */ + rotate_dmask90_vec, /* Dispatch function */ + IM_NUMBER( dmask_args ), /* Size of arg list */ + dmask_args /* Arg list */ +}; + +static im_arg_desc maxvalue_args[] = { + IM_INPUT_IMAGEVEC( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +static int +maxvalue_vec( im_object *argv ) +{ + im_imagevec_object *iv = (im_imagevec_object *) argv[0]; + + return( im_maxvalue( iv->vec, argv[1], iv->n ) ); +} + +static im_function maxvalue_desc = { + "im_maxvalue", /* Name */ + "point-wise maximum value", /* Description */ + IM_FN_PIO, /* Flags */ + maxvalue_vec, /* Dispatch function */ + IM_NUMBER( maxvalue_args ), /* Size of arg list */ + maxvalue_args /* Arg list */ +}; + +static im_arg_desc rank_image_args[] = { + IM_INPUT_IMAGEVEC( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "index" ) +}; + +static int +rank_image_vec( im_object *argv ) +{ + im_imagevec_object *iv = (im_imagevec_object *) argv[0]; + int index = *((int *) argv[2]); + + return( im_rank_image( iv->vec, argv[1], iv->n, index ) ); +} + +static im_function rank_image_desc = { + "im_rank_image", /* Name */ + "point-wise pixel rank", /* Description */ + IM_FN_PIO, /* Flags */ + rank_image_vec, /* Dispatch function */ + IM_NUMBER( rank_image_args ), /* Size of arg list */ + rank_image_args /* Arg list */ +}; + +static int +imask_xsize_vec( im_object *argv ) +{ + *( (int*) argv[1] )= ( (INTMASK*) ( ( (im_mask_object*) argv[0] )-> mask ) )-> xsize; + return 0; +} + +static int +imask_ysize_vec( im_object *argv ) +{ + *( (int*) argv[1] )= ( (INTMASK*) ( ( (im_mask_object*) argv[0] )-> mask ) )-> ysize; + return 0; +} + +static int +dmask_xsize_vec( im_object *argv ) +{ + *( (int*) argv[1] )= ( (DOUBLEMASK*) ( ( (im_mask_object*) argv[0] )-> mask ) )-> xsize; + return 0; +} + +static int +dmask_ysize_vec( im_object *argv ) +{ + *( (int*) argv[1] )= ( (DOUBLEMASK*) ( ( (im_mask_object*) argv[0] )-> mask ) )-> ysize; + return 0; +} + +static im_arg_desc imask_size_args[] = { + IM_INPUT_IMASK( "mask" ), + IM_OUTPUT_INT( "size" ) +}; + +static im_arg_desc dmask_size_args[] = { + IM_INPUT_DMASK( "mask" ), + IM_OUTPUT_INT( "size" ) +}; + +static im_function imask_xsize_desc = { + "im_imask_xsize", /* Name */ + "horizontal size of an intmask", /* Description */ + 0, /* Flags */ + imask_xsize_vec, /* Dispatch function */ + IM_NUMBER( imask_size_args ), /* Size of arg list */ + imask_size_args /* Arg list */ +}; + +static im_function imask_ysize_desc = { + "im_imask_ysize", /* Name */ + "vertical size of an intmask", /* Description */ + 0, /* Flags */ + imask_ysize_vec, /* Dispatch function */ + IM_NUMBER( imask_size_args ), /* Size of arg list */ + imask_size_args /* Arg list */ +}; + +static im_function dmask_xsize_desc = { + "im_dmask_xsize", /* Name */ + "horizontal size of a doublemask", /* Description */ + 0, /* Flags */ + dmask_xsize_vec, /* Dispatch function */ + IM_NUMBER( dmask_size_args ), /* Size of arg list */ + dmask_size_args /* Arg list */ +}; + +static im_function dmask_ysize_desc = { + "im_dmask_ysize", /* Name */ + "vertical size of a doublemask", /* Description */ + 0, /* Flags */ + dmask_ysize_vec, /* Dispatch function */ + IM_NUMBER( dmask_size_args ), /* Size of arg list */ + dmask_size_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *convol_list[] = { + &addgnoise_desc, + &compass_desc, + &contrast_surface_desc, + &contrast_surface_raw_desc, + &conv_desc, + &conv_raw_desc, + &convf_desc, + &convf_raw_desc, + &convsep_desc, + &convsep_raw_desc, + &convsepf_desc, + &convsepf_raw_desc, + &convsub_desc, + &dmask_xsize_desc, + &dmask_ysize_desc, + &embed_desc, + &fastcor_desc, + &fastcor_raw_desc, + &gauss_dmask_desc, + &gauss_imask_desc, + &gaussnoise_desc, + &grad_x_desc, + &grad_y_desc, + &gradcor_desc, + &gradcor_raw_desc, + &gradient_desc, + &imask_xsize_desc, + &imask_ysize_desc, + &rank_image_desc, + &lindetect_desc, + &log_dmask_desc, + &log_imask_desc, + &maxvalue_desc, + &mpercent_desc, + &rank_desc, + &rank_raw_desc, + &read_dmask_desc, + &resize_linear_desc, + &rotate_dmask45_desc, + &rotate_dmask90_desc, + &rotate_imask45_desc, + &rotate_imask90_desc, + &sharpen_desc, + &shrink_desc, + &spcor_desc, + &spcor_raw_desc, + &spcor2_desc, + &spcor2_raw_desc, + &stretch3_desc, + &zerox_desc +}; + +/* Package of functions. + */ +im_package im__convolution = { + "convolution", + IM_NUMBER( convol_list ), + convol_list +}; diff --git a/libsrc/convolution/im_addgnoise.c b/libsrc/convolution/im_addgnoise.c new file mode 100644 index 00000000..2b6d93c2 --- /dev/null +++ b/libsrc/convolution/im_addgnoise.c @@ -0,0 +1,96 @@ +/* @(#) Add gaussian noise with mean 0 and variance sigma to image + * @(#) The noise is generated by averaging 12 random numbers + * @(#) page 78 PIETGEN 1989 n = 12 + * @(#) Input image is any, output is float + * @(#) If running on SYSTEM V CONSTANT should be replaced by 2**15 - 1 + * @(#) Usage + * @(#) + * @(#) int im_addgnoise(imin, imout, sigma) + * @(#) IMAGE *imin, *imout; + * @(#) double sigma; + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright 1990, N. Dessipris. + * + * File written on 2/12/1986 + * Author : N. Dessipris + * Updated : 22/01/1991 + * 1/2/95 JC + * - junked! + * - now uses partial im_gaussnoise() and partial im_add() to do the + * same job + & 1/5/01 JC + * - oops, failed for not-1-band images + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define MAX_BANDS (200) + +int +im_addgnoise( IMAGE *in, IMAGE *out, double sigma ) +{ + IMAGE *noise[MAX_BANDS]; + IMAGE *nb; + int i; + + if( in->Bands > MAX_BANDS ) { + im_error( "im_addgnoise", _( "too many bands" ) ); + return( -1 ); + } + + /* Make n-band noise image. + */ + for( i = 0; i < in->Bands; i++ ) + if( !(noise[i] = im_open_local( out, "im_addgnoise:2", "p" )) || + im_gaussnoise( noise[i], in->Xsize, in->Ysize, + 0.0, sigma ) ) + return( -1 ); + + if( !(nb = im_open_local( out, "im_addgnoise:3", "p" )) || + im_gbandjoin( noise, out, in->Bands ) || + im_add( in, nb, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_compass.c b/libsrc/convolution/im_compass.c new file mode 100644 index 00000000..7b357da7 --- /dev/null +++ b/libsrc/convolution/im_compass.c @@ -0,0 +1,148 @@ +/* @(#) im_complass: Optimised convolution for line detection + * @(#) Uses the entered mask and 7 rotated versions of it (each by 45 degrees) + * @(#) + * @(#) Usage + * @(#) int im_compass( in, out, m ) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *m; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * @(#) Returns an int pointer to valid offsets for rotating a square mask + * @(#) of odd size by 45 degrees. + * @(#) + * @(#) Usage + * @(#) int *im_offsets45( size ) + * @(#) int size; + * @(#) + * @(#) Returns an int pointer to valid offsets on sucess and -1 on error + * @(#) + * + * Author: N. Dessipris (Copyright, N. Dessipris 1991) + * Written on: 08/05/1991 + * Modified on: + * 11/3/01 JC + * - rewritten, calling im_conv() and im_maxvalue() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_compass( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *filtered[8]; + IMAGE *absed[8]; + int i; + + if( im_open_local_array( out, filtered, 8, "im_compass:1", "p" ) || + im_open_local_array( out, absed, 8, "im_compass:2", "p" ) ) + return( -1 ); + + for( i = 0; i < 8; i++ ) { + if( im_conv( in, filtered[i], mask ) || + !(mask = (INTMASK *) im_local( out, + (im_construct_fn) im_rotate_imask45, + (im_callback_fn) im_free_imask, + mask, mask->filename, NULL )) ) + return( -1 ); + } + + for( i = 0; i < 8; i++ ) + if( im_abs( filtered[i], absed[i] ) ) + return( -1 ); + + return( im_maxvalue( absed, out, 8 ) ); +} + +int +im_lindetect( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *filtered[4]; + IMAGE *absed[4]; + int i; + + if( im_open_local_array( out, filtered, 4, "im_lindetect:1", "p" ) || + im_open_local_array( out, absed, 4, "im_lindetect:2", "p" ) ) + return( -1 ); + + for( i = 0; i < 4; i++ ) { + if( im_conv( in, filtered[i], mask ) || + !(mask = (INTMASK *) im_local( out, + (im_construct_fn) im_rotate_imask45, + (im_callback_fn) im_free_imask, + mask, mask->filename, NULL )) ) + return( -1 ); + } + + for( i = 0; i < 4; i++ ) + if( im_abs( filtered[i], absed[i] ) ) + return( -1 ); + + return( im_maxvalue( absed, out, 4 ) ); +} + +#define GTEMPS (4) + +int +im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *t[GTEMPS]; + INTMASK *rmask; + + if( im_open_local_array( out, t, GTEMPS, "im_gradient", "p" ) ) + return( -1 ); + + if( !(rmask = (INTMASK *) im_local( out, + (im_construct_fn) im_rotate_imask90, + (im_callback_fn) im_free_imask, mask, mask->filename, NULL )) ) + return( -1 ); + + if( im_conv( in, t[0], mask ) || + im_conv( in, t[1], rmask ) || + im_abs( t[0], t[2] ) || + im_abs( t[1], t[3] ) || + im_add( t[2], t[3], out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_contrast_surface.c b/libsrc/convolution/im_contrast_surface.c new file mode 100644 index 00000000..f10faf07 --- /dev/null +++ b/libsrc/convolution/im_contrast_surface.c @@ -0,0 +1,274 @@ +/* @(#) Generate an image where the value of each pixel represents the + * @(#) contrast within a window of half_win_size from the corresponsing + * @(#) point in the input image. Sub-sample by a factor of spacing. + * @(#) + * @(#) Pixels beyond the edges of the image are considered to be have the + * @(#) value zero (black). + * @(#) + * @(#) Input must be single-band uncoded uchar, WIO or PIO. + * @(#) + * @(#) Output is single-band uncoded uint, WIO or PIO. + * @(#) + * @(#) int + * @(#) im_contrast_surface( + * @(#) IMAGE *in, + * @(#) IMAGE *out, + * @(#) int half_win_size, + * @(#) int spacing + * @(#) ); + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * @(#) Also: im_contrast_surface_raw(). As above, but pixels within + * @(#) half_win_size of the edge are not calculated, and output is smaller + * @(#) accordingly. + * + * Copyright: 2006, The Nottingham Trent University + * + * Author: Tom Vajzovic + * (based on algorithm by Nicos Dessipris & John Cupitt) + * + * Written on: 2006-03-13 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H */ + +#ifdef NOT_IN_VIPS +#define _(s) (s) +#else +#include +#endif + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + +/** MACROS **/ + +/* from simple_macros.h */ +#define LESSER(a,b) ((a)<(b)?(a):(b)) +#define DOUBLE(a) ( (a)<<1 ) +#define DOUBLE_ADD_ONE(a) ( 1 | ( (a)<<1 ) ) + +/** LOCAL TYPES **/ + +typedef struct cont_surf_params_s +{ + int half_win_size; + int spacing; + +} cont_surf_params_t; + +/** LOCAL FUNCTIONS DECLARATIONS **/ + +int +im_contrast_surface (IMAGE * in, IMAGE * out, int half_win_size, int spacing); + +int +im_contrast_surface_raw (IMAGE * in, IMAGE * out, int half_win_size, + int spacing); + +static int cont_surf_gen (REGION * to_make, REGION * make_from, + void *unrequired, cont_surf_params_t * params); + +static unsigned int calc_cont (REGION * reg, int win_size_less_one, + int x_left, int y_top); + +/** EXPORTED FUNCTIONS **/ + +int +im_contrast_surface (IMAGE * in, IMAGE * out, int half_win_size, int spacing) +{ + IMAGE *t1 = im_open_local (out, "im_contrast_surface intermediate", "p"); + + if (!t1 + || im_embed (in, t1, 1, half_win_size, half_win_size, + in->Xsize + DOUBLE (half_win_size), + in->Ysize + DOUBLE (half_win_size)) + || im_contrast_surface_raw (t1, out, half_win_size, spacing)) + + return -1; + + out->Xoffset = 0; + out->Yoffset = 0; + + return 0; +} + +int +im_contrast_surface_raw (IMAGE * in, IMAGE * out, int half_win_size, + int spacing) +{ +#define FUNCTION_NAME "im_contrast_surface_raw" + + cont_surf_params_t *params; + + if (im_piocheck (in, out)) + return -1; + + if (IM_CODING_NONE != in->Coding || IM_BANDFMT_UCHAR != in->BandFmt + || 1 != in->Bands) + { + im_error (FUNCTION_NAME, _("one band uncoded uchar only")); + return -1; + } + + if (half_win_size < 1 || spacing < 1) + { + im_error (FUNCTION_NAME, _("bad parameters")); + return -1; + } + + if (DOUBLE (half_win_size) >= LESSER (in->Xsize, in->Ysize)) + { + im_error (FUNCTION_NAME, + _("parameters would result in zero size output image")); + return -1; + } + + params = IM_NEW (out, cont_surf_params_t); + + if (!params) + return -1; + + params->half_win_size = half_win_size; + params->spacing = spacing; + + if (im_cp_desc (out, in)) + return -1; + + out->BandFmt = IM_BANDFMT_UINT; + out->Bbits = sizeof (unsigned int) << 3; + + out->Xsize = 1 + ((in->Xsize - DOUBLE_ADD_ONE (half_win_size)) / spacing); + out->Ysize = 1 + ((in->Ysize - DOUBLE_ADD_ONE (half_win_size)) / spacing); + + out->Xoffset = -half_win_size; + out->Yoffset = -half_win_size; + + if (im_demand_hint (out, IM_FATSTRIP, in, NULL)) + return -1; + + return im_generate (out, im_start_one, cont_surf_gen, im_stop_one, in, + params); + +#undef FUNCTION_NAME +} + +/** LOCAL FUNCTIONS DEFINITIONS **/ +static int +cont_surf_gen (REGION * to_make, REGION * make_from, void *unrequired, + cont_surf_params_t * params) +{ + /* I don't need *in, but I will recieve it anyway since im_start_one() needs it */ + + unsigned int *row = + (unsigned int *) IM_REGION_ADDR (to_make, to_make->valid.left, + to_make->valid.top); + int xoff; + int y; + int bottom = to_make->valid.top + to_make->valid.height; + size_t lskip = IM_REGION_LSKIP (to_make) / sizeof (unsigned int); + + Rect area = { + params->spacing * to_make->valid.left, + params->spacing * to_make->valid.top, + DOUBLE_ADD_ONE (params->half_win_size) + + (params->spacing * (to_make->valid.width - 1)), + DOUBLE_ADD_ONE (params->half_win_size) + + (params->spacing * (to_make->valid.height - 1)) + }; + + if (im_prepare (make_from, &area) + || !im_rect_equalsrect (&make_from->valid, &area)) + return -1; + + for (y = to_make->valid.top; y < bottom; ++y, row += lskip) + + for (xoff = 0; xoff < to_make->valid.width; ++xoff) + + row[xoff] = + calc_cont (make_from, DOUBLE (params->half_win_size), + (xoff + to_make->valid.left) * params->spacing, + y * params->spacing); + + return 0; +} + +static unsigned int +calc_cont (REGION * reg, int win_size_less_one, int x_left, int y_top) +{ + unsigned char val; + unsigned char all_black = 1; + unsigned char *row; + unsigned int contrast = 0; + int xoff; + int yoff; + size_t lskip = IM_REGION_LSKIP (reg) / sizeof (unsigned char); + + row = (unsigned char *) IM_REGION_ADDR (reg, x_left, y_top); + val = *row; + + for (yoff = 0; yoff <= win_size_less_one && all_black; ++yoff, row += lskip) + for (xoff = 0; xoff <= win_size_less_one; ++xoff) + if (row[xoff] != val) + { + all_black = 0; + break; + } + + if (all_black) + return contrast; + + row = (unsigned char *) IM_REGION_ADDR (reg, x_left, y_top); + + for (yoff = 0; yoff < win_size_less_one; ++yoff, row += lskip) + { + for (xoff = 0; xoff < win_size_less_one; ++xoff) + contrast += + abs (row[xoff + 1] - row[xoff]) + abs (row[xoff + lskip] - + row[xoff]); + + contrast += abs (row[xoff + lskip] - row[xoff]); + } + + for (xoff = 0; xoff < win_size_less_one; ++xoff) + contrast += abs (row[xoff + 1] - row[xoff]); + + return contrast; +} diff --git a/libsrc/convolution/im_conv.c b/libsrc/convolution/im_conv.c new file mode 100644 index 00000000..891ae643 --- /dev/null +++ b/libsrc/convolution/im_conv.c @@ -0,0 +1,538 @@ +/* @(#) Convolve an image with an INTMASK. Image can have any number of bands, + * @(#) any non-complex type. Size and type of output image matches type of + * @(#) input image. + * @(#) + * @(#) int + * @(#) im_conv( in, out, mask ) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *mask; + * @(#) + * @(#) Also: im_conv_raw(). As above, but does not add a black border. + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * @(#) Old code, kept for use of other old code in this package: + * @(#) + * @(#) Creates int luts for all non zero elm of the original mask; + * @(#) which is kept in buffer of length buffersize + * @(#) cnt is needed for freeing luts. Called by the above. + * @(#) + * @(#) int im__create_int_luts( buffer, buffersize, orig_luts, luts, cnt ) + * @(#) int *buffer, buffersize; + * @(#) int **orig_luts, **luts, *cnt; + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris & Kirk Martinez + * Written on: 29/04/1991 + * Modified on: 19/05/1991 + * 8/7/93 JC + * - adapted for partial v2 + * - memory leaks fixed + * - ANSIfied + * 23/7/93 JC + * - inner loop unrolled with a switch - 25% speed-up! + * 13/12/93 JC + * - tiny rounding error removed + * 7/10/94 JC + * - new IM_ARRAY() macro + * - various simplifications + * - evalend callback added + * 1/2/95 JC + * - use of IM_REGION_ADDR() updated + * - output size was incorrect! see comment below + * - bug with large non-square matricies fixed too + * - uses new im_embed() function + * 13/7/98 JC + * - wierd bug ... im_free_imask is no longer directly called for close + * callback, caused SIGKILL on solaris 2.6 ... linker bug? + * 9/3/01 JC + * - reworked and simplified, about 10% faster + * - slightly better range clipping + * 27/7/01 JC + * - rejects masks with scale == 0 + * 7/4/04 + * - im_conv() now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + * 11/11/05 + * - simpler inner loop avoids gcc4 bug + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our parameters ... we take a copy of the mask argument, plus we make a + * smaller version with the zeros squeezed out. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + INTMASK *mask; /* Copy of mask arg */ + + int nnz; /* Number of non-zero mask elements */ + int *coeff; /* Array of non-zero mask coefficients */ + + int underflow; /* Global underflow/overflow counts */ + int overflow; +} Conv; + +static int +conv_destroy( Conv *conv ) +{ + /* Print underflow/overflow count. + */ + if( conv->overflow || conv->underflow ) + im_warning( "im_conv: %d overflows and %d underflows detected", + conv->overflow, conv->underflow ); + + if( conv->mask ) { + (void) im_free_imask( conv->mask ); + conv->mask = NULL; + } + + return( 0 ); +} + +static Conv * +conv_new( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + Conv *conv = IM_NEW( out, Conv ); + const int ne = mask->xsize * mask->ysize; + int i; + + if( !conv ) + return( NULL ); + + conv->in = in; + conv->out = out; + conv->mask = NULL; + conv->nnz = 0; + conv->coeff = NULL; + conv->underflow = 0; + conv->overflow = 0; + + if( im_add_close_callback( out, + (im_callback_fn) conv_destroy, conv, NULL ) || + !(conv->coeff = IM_ARRAY( out, ne, int )) || + !(conv->mask = im_dup_imask( mask, "conv_mask" )) ) + return( NULL ); + + /* Find non-zero mask elements. + */ + for( i = 0; i < ne; i++ ) + if( mask->coeff[i] ) + conv->coeff[conv->nnz++] = mask->coeff[i]; + + return( conv ); +} + +/* Our sequence value. + */ +typedef struct { + Conv *conv; + REGION *ir; /* Input region */ + + int *offsets; /* Offsets for each non-zero matrix element */ + PEL **pts; /* Per-non-zero mask element image pointers */ + + int underflow; /* Underflow/overflow counts */ + int overflow; +} ConvSequence; + +/* Free a sequence value. + */ +static int +stop_conv( ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + /* Add local under/over counts to global counts. + */ + conv->overflow += seq->overflow; + conv->underflow += seq->underflow; + + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +start_conv( IMAGE *out, IMAGE *in, Conv *conv ) +{ + ConvSequence *seq = IM_NEW( out, ConvSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->conv = conv; + seq->ir = NULL; + seq->pts = NULL; + seq->underflow = 0; + seq->overflow = 0; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->offsets = IM_ARRAY( out, conv->nnz, int ); + seq->pts = IM_ARRAY( out, conv->nnz, PEL * ); + if( !seq->ir || !seq->offsets || !seq->pts ) { + stop_conv( seq, in, conv ); + return( NULL ); + } + + return( (void *) seq ); +} + +#define INNER sum += *t++ * (*p++)[x] + +/* INT and FLOAT inner loops. + */ +#define CONV_INT( TYPE, IM_CLIP ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + int sum = 0; \ + int *t = conv->coeff; \ + TYPE **p = (TYPE **) seq->pts; \ + \ + z = 0; \ + IM_UNROLL( conv->nnz, INNER ); \ + \ + sum = ((sum + rounding) / mask->scale) + mask->offset; \ + \ + IM_CLIP; \ + \ + q[x] = sum; \ + } \ +} + +#define CONV_FLOAT( TYPE ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + double sum = 0; \ + int *t = conv->coeff; \ + TYPE **p = (TYPE **) seq->pts; \ + \ + z = 0; \ + IM_UNROLL( conv->nnz, INNER ); \ + \ + sum = (sum / mask->scale) + mask->offset; \ + \ + q[x] = sum; \ + } \ +} + +/* Convolve! + */ +static int +gen_conv( REGION *or, ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + REGION *ir = seq->ir; + INTMASK *mask = conv->mask; + int rounding = (mask->scale + 1)/2; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM( r ); + int sz = IM_REGION_N_ELEMENTS( or ); + + int x, y, z, i; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += mask->xsize - 1; + s.height += mask->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Fill offset array. + */ + z = 0; + for( i = 0, y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++, i++ ) + if( mask->coeff[i] ) + seq->offsets[z++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + + for( y = to; y < bo; y++ ) { + /* Init pts for this line of PELs. + */ + for( z = 0; z < conv->nnz; z++ ) + seq->pts[z] = seq->offsets[z] + + (PEL *) IM_REGION_ADDR( ir, le, y ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + CONV_INT( unsigned char, IM_CLIP_UCHAR( sum, seq ) ); + break; + + case IM_BANDFMT_CHAR: + CONV_INT( signed char, IM_CLIP_CHAR( sum, seq ) ); + break; + + case IM_BANDFMT_USHORT: + CONV_INT( unsigned short, IM_CLIP_USHORT( sum, seq ) ); + break; + + case IM_BANDFMT_SHORT: + CONV_INT( signed short, IM_CLIP_SHORT( sum, seq ) ); + break; + + case IM_BANDFMT_UINT: + CONV_INT( unsigned int, IM_CLIP_NONE( sum, seq ) ); + break; + + case IM_BANDFMT_INT: + CONV_INT( signed int, IM_CLIP_NONE( sum, seq ) ); + break; + + case IM_BANDFMT_FLOAT: + CONV_FLOAT( float ); + break; + + case IM_BANDFMT_DOUBLE: + CONV_FLOAT( double ); + break; + + default: + assert( 0 ); + } + } + + return( 0 ); +} + +int +im_conv_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + Conv *conv; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_errormsg( "im_conv: input non-complex uncoded please!"); + return( -1 ); + } + if( !mask || mask->xsize > 1000 || mask->ysize > 1000 || + mask->xsize <= 0 || mask->ysize <= 0 || !mask->coeff || + mask->scale == 0 ) { + im_errormsg( "im_conv: nonsense mask parameters" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( !(conv = conv_new( in, out, mask )) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= mask->xsize - 1; + out->Ysize -= mask->ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_conv: image too small for mask" ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + if( im_generate( out, start_conv, gen_conv, stop_conv, in, conv ) ) + return( -1 ); + + out->Xoffset = -mask->xsize / 2; + out->Yoffset = -mask->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_conv( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *t1 = im_open_local( out, "im_conv intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, mask->xsize / 2, mask->ysize / 2, + in->Xsize + mask->xsize - 1, + in->Ysize + mask->ysize - 1 ) || + im_conv_raw( t1, out, mask ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} + +/* im__create_int_luts is not used in this file. We have to keep it for the use + * of other conv functions in this directory which have not yet been + * rewritten. + + FIXME ... the only one left is im_convsub() which I'm sure no one + uses. Scrap this junk in the next version. Kill off the old gradient + and lindetect things too. + + */ + +/* Create multiplication luts for all non zero elements of the original mask; + * which is kept in buffer of length buffersize + * cnt is needed for freeing luts + */ +int +im__create_int_luts( int *buffer, int buffersize, + int **orig_luts, int **luts, int *cnt ) +{ + int *pbuffer; + int *buf1, *buf2, *pbuf1, *pbuf2; + int i, j; + int min, max; + int mark; /* used to mark the buffer mark = max+1 */ + int counter; /* counts the no of unique elms in mask; returned in cnt*/ + + buf1 = (int*)calloc( (unsigned)buffersize, sizeof(int) ); + buf2 = (int*)calloc( (unsigned)buffersize, sizeof(int) ); + if ( ( buf1 == NULL ) || ( buf2 == NULL ) ) + { + im_errormsg("im_create_int_luts: calloc failed (1)"); + return( -1 ); + } + + pbuffer = buffer; + pbuf1 = buf1; + /* find max and copy mask to buf1 */ + max = *pbuffer; + for ( i=0; i < buffersize; i++ ) + { + if ( *pbuffer > max ) + max = *pbuffer; + *pbuf1++ = *pbuffer++; + } + mark = max + 1; + pbuf1 = buf1; + pbuf2 = buf2; + counter = 0; +/* find a min at a time; put it into buf2 and mark all values of + * buf1 equal to found min, to INT_MAX + */ + for ( i=0; i < buffersize; i++ ) + { + min = mark + 1; /* force min to be greater than mark */ + pbuf1 = buf1; + /* find a min */ + for ( j=0; j < buffersize; j++ ) + { + if ( *pbuf1 < min ) + min = *pbuf1; + pbuf1++; + } + if ( min == mark ) /* all min are found */ + break; + *pbuf2++ = min; + counter++; + pbuf1 = buf1; + for ( j=0; j < buffersize; j++ ) /* mark values equal to min */ + { + if ( *pbuf1 == min ) + *pbuf1 = mark; + pbuf1++; + } + } +/* buf2 should keep now counter unique values of the mask, descending order + * Malloc counter luts and initialise them + */ + pbuf2 = buf2; + for ( i=0; i +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our parameters ... we take a copy of the mask argument, plus we make a + * smaller version with the zeros squeezed out. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + DOUBLEMASK *mask; /* Copy of mask arg */ + + int nnz; /* Number of non-zero mask elements */ + double *coeff; /* Array of non-zero mask coefficients */ +} Conv; + +static int +conv_destroy( Conv *conv ) +{ + if( conv->mask ) { + (void) im_free_dmask( conv->mask ); + conv->mask = NULL; + } + + return( 0 ); +} + +static Conv * +conv_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + Conv *conv = IM_NEW( out, Conv ); + const int ne = mask->xsize * mask->ysize; + int i; + + if( !conv ) + return( NULL ); + + conv->in = in; + conv->out = out; + conv->mask = NULL; + conv->nnz = 0; + conv->coeff = NULL; + + if( im_add_close_callback( out, + (im_callback_fn) conv_destroy, conv, NULL ) || + !(conv->coeff = IM_ARRAY( out, ne, double )) || + !(conv->mask = im_dup_dmask( mask, "conv_mask" )) ) + return( NULL ); + + /* Find non-zero mask elements. + */ + for( i = 0; i < ne; i++ ) + if( mask->coeff[i] ) + conv->coeff[conv->nnz++] = mask->coeff[i]; + + return( conv ); +} + +/* Our sequence value. + */ +typedef struct { + Conv *conv; + REGION *ir; /* Input region */ + + int *offsets; /* Offsets for each non-zero matrix element */ + PEL **pts; /* Per-non-zero mask element image pointers */ +} ConvSequence; + +/* Free a sequence value. + */ +static int +stop_conv( ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +start_conv( IMAGE *out, IMAGE *in, Conv *conv ) +{ + ConvSequence *seq = IM_NEW( out, ConvSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->conv = conv; + seq->ir = NULL; + seq->pts = NULL; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->offsets = IM_ARRAY( out, conv->nnz, int ); + seq->pts = IM_ARRAY( out, conv->nnz, PEL * ); + if( !seq->ir || !seq->offsets || !seq->pts ) { + stop_conv( seq, in, conv ); + return( NULL ); + } + + return( (void *) seq ); +} + +#define INNER sum += *t++ * (*p++)[x] + +#define CONV_FLOAT( ITYPE, OTYPE ) { \ + OTYPE *q = (OTYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + double sum = 0; \ + double *t = conv->coeff; \ + ITYPE **p = (ITYPE **) seq->pts; \ + \ + z = 0; \ + IM_UNROLL( conv->nnz, INNER ); \ + \ + sum = (sum / mask->scale) + mask->offset; \ + \ + q[x] = sum; \ + } \ +} + +/* Convolve! + */ +static int +gen_conv( REGION *or, ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + REGION *ir = seq->ir; + DOUBLEMASK *mask = conv->mask; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int x, y, z, i; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += mask->xsize - 1; + s.height += mask->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Fill offset array. + */ + z = 0; + for( i = 0, y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++, i++ ) + if( mask->coeff[i] ) + seq->offsets[z++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + + for( y = to; y < bo; y++ ) { + /* Init pts for this line of PELs. + */ + for( z = 0; z < conv->nnz; z++ ) + seq->pts[z] = seq->offsets[z] + + (PEL *) IM_REGION_ADDR( ir, le, y ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + CONV_FLOAT( unsigned char, float ); break; + case IM_BANDFMT_CHAR: + CONV_FLOAT( signed char, float ); break; + case IM_BANDFMT_USHORT: + CONV_FLOAT( unsigned short, float ); break; + case IM_BANDFMT_SHORT: + CONV_FLOAT( signed short, float ); break; + case IM_BANDFMT_UINT: + CONV_FLOAT( unsigned int, float ); break; + case IM_BANDFMT_INT: + CONV_FLOAT( signed int, float ); break; + case IM_BANDFMT_FLOAT: + CONV_FLOAT( float, float ); break; + case IM_BANDFMT_DOUBLE: + CONV_FLOAT( double, double ); break; + + default: + assert( 0 ); + } + } + + return( 0 ); +} + +int +im_convf_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + Conv *conv; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_errormsg( "im_convf: input non-complex uncoded please!"); + return( -1 ); + } + if( !mask || mask->xsize > 1000 || mask->ysize > 1000 || + mask->xsize <= 0 || mask->ysize <= 0 || !mask->coeff || + mask->scale == 0 ) { + im_errormsg( "im_convf: nonsense mask parameters" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( !(conv = conv_new( in, out, mask )) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + out->Xsize -= mask->xsize - 1; + out->Ysize -= mask->ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_convf: image too small for mask" ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + if( im_generate( out, start_conv, gen_conv, stop_conv, in, conv ) ) + return( -1 ); + + out->Xoffset = -mask->xsize / 2; + out->Yoffset = -mask->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_convf( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + IMAGE *t1 = im_open_local( out, "im_convf intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, mask->xsize / 2, mask->ysize / 2, + in->Xsize + mask->xsize - 1, + in->Ysize + mask->ysize - 1 ) || + im_convf_raw( t1, out, mask ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_convsep.c b/libsrc/convolution/im_convsep.c new file mode 100644 index 00000000..19cd010a --- /dev/null +++ b/libsrc/convolution/im_convsep.c @@ -0,0 +1,429 @@ +/* @(#) Convolve an image with a seperable (1xN, or Nx1) INTMASK. Image can + * @(#) have any number of bands, any non-complex type. Size and type of + * @(#) output image matches type of input image. + * @(#) + * @(#) int + * @(#) im_convsep( in, out, mask ) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *mask; + * @(#) + * @(#) Also: im_convsep_raw(). As above, but does not add a black border. + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 29/04/1991 + * Modified on: 29/4/93 K.Martinez for Sys5 + * 9/3/01 JC + * - rewritten using im_conv() + * 27/7/01 JC + * - rejects masks with scale == 0 + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + * 21/4/04 + * - scale down int convolves at 1/2 way mark, much less likely to integer + * overflow on intermediates + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our parameters ... we take a copy of the mask argument. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + INTMASK *mask; /* Copy of mask arg */ + + int size; /* N for our 1xN or Nx1 mask */ + int scale; /* Our scale ... we have to square mask->scale */ + + int underflow; /* Global underflow/overflow counts */ + int overflow; +} Conv; + +/* End of evaluation --- print overflows and underflows. + */ +static int +conv_destroy( Conv *conv ) +{ + /* Print underflow/overflow count. + */ + if( conv->overflow || conv->underflow ) + im_warn( "im_convsep", _( "%d overflows and %d underflows " + "detected" ), conv->overflow, conv->underflow ); + + if( conv->mask ) { + (void) im_free_imask( conv->mask ); + conv->mask = NULL; + } + + return( 0 ); +} + +static Conv * +conv_new( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + Conv *conv = IM_NEW( out, Conv ); + + if( !conv ) + return( NULL ); + + conv->in = in; + conv->out = out; + conv->mask = NULL; + conv->size = mask->xsize * mask->ysize; + conv->scale = mask->scale * mask->scale; + conv->underflow = 0; + conv->overflow = 0; + + if( im_add_close_callback( out, + (im_callback_fn) conv_destroy, conv, NULL ) || + !(conv->mask = im_dup_imask( mask, "conv_mask" )) ) + return( NULL ); + + return( conv ); +} + +/* Our sequence value. + */ +typedef struct { + Conv *conv; + REGION *ir; /* Input region */ + + PEL *sum; /* Line buffer */ + + int underflow; /* Underflow/overflow counts */ + int overflow; +} ConvSequence; + +/* Free a sequence value. + */ +static int +stop_conv( ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + /* Add local under/over counts to global counts. + */ + conv->overflow += seq->overflow; + conv->underflow += seq->underflow; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +start_conv( IMAGE *out, IMAGE *in, Conv *conv ) +{ + ConvSequence *seq = IM_NEW( out, ConvSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->conv = conv; + seq->ir = NULL; + seq->sum = NULL; + seq->underflow = 0; + seq->overflow = 0; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + if( im_isint( conv->out ) ) + seq->sum = (PEL *) + IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), int ); + else + seq->sum = (PEL *) + IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), double ); + if( !seq->ir || !seq->sum ) { + stop_conv( seq, in, conv ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* What we do for every point in the mask, for each pixel. + */ +#define VERTICAL_CONV { z -= 1; li -= lskip; sum += coeff[z] * vfrom[li]; } +#define HORIZONTAL_CONV { z -= 1; li -= bands; sum += coeff[z] * hfrom[li]; } + +/* INT and FLOAT inner loops. + */ +#define CONV_INT( TYPE, IM_CLIP ) { \ + TYPE *vfrom; \ + int *vto; \ + int *hfrom; \ + TYPE *hto; \ + \ + /* Convolve to sum array. We convolve the full width of \ + * this input line. \ + */ \ + vfrom = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + vto = (int *) seq->sum; \ + for( x = 0; x < isz; x++ ) { \ + int sum; \ + \ + z = conv->size; \ + li = lskip * z; \ + sum = 0; \ + \ + IM_UNROLL( z, VERTICAL_CONV ); \ + \ + sum = ((sum + rounding) / mask->scale) + mask->offset; \ + \ + vto[x] = sum; \ + vfrom += 1; \ + } \ + \ + /* Convolve sums to output. \ + */ \ + hfrom = (int *) seq->sum; \ + hto = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + for( x = 0; x < osz; x++ ) { \ + int sum; \ + \ + z = conv->size; \ + li = bands * z; \ + sum = 0; \ + \ + IM_UNROLL( z, HORIZONTAL_CONV ); \ + \ + sum = ((sum + rounding) / mask->scale) + mask->offset; \ + \ + IM_CLIP; \ + \ + hto[x] = sum; \ + hfrom += 1; \ + } \ +} + +#define CONV_FLOAT( TYPE ) { \ + TYPE *vfrom; \ + double *vto; \ + double *hfrom; \ + TYPE *hto; \ + \ + /* Convolve to sum array. We convolve the full width of \ + * this input line. \ + */ \ + vfrom = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + vto = (double *) seq->sum; \ + for( x = 0; x < isz; x++ ) { \ + double sum; \ + \ + z = conv->size; \ + li = lskip * z; \ + sum = 0; \ + \ + IM_UNROLL( z, VERTICAL_CONV ); \ + \ + vto[x] = sum; \ + vfrom += 1; \ + } \ + \ + /* Convolve sums to output. \ + */ \ + hfrom = (double *) seq->sum; \ + hto = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + for( x = 0; x < osz; x++ ) { \ + double sum; \ + \ + z = conv->size; \ + li = bands * z; \ + sum = 0; \ + \ + IM_UNROLL( z, HORIZONTAL_CONV ); \ + \ + sum = (sum / conv->scale) + mask->offset; \ + \ + hto[x] = sum; \ + hfrom += 1; \ + } \ +} + +/* Convolve! + */ +static int +gen_conv( REGION *or, ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + REGION *ir = seq->ir; + INTMASK *mask = conv->mask; + int rounding = (mask->scale + 1)/2; + int bands = in->Bands; + int *coeff = conv->mask->coeff; + + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int osz = IM_REGION_N_ELEMENTS( or ); + + Rect s; + int lskip; + int isz; + int x, y, z, li; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += conv->size - 1; + s.height += conv->size - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + lskip = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( in ); + isz = IM_REGION_N_ELEMENTS( ir ); + + for( y = to; y < bo; y++ ) { + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + CONV_INT( unsigned char, IM_CLIP_UCHAR( sum, seq ) ); + break; + case IM_BANDFMT_CHAR: + CONV_INT( signed char, IM_CLIP_CHAR( sum, seq ) ); + break; + case IM_BANDFMT_USHORT: + CONV_INT( unsigned short, IM_CLIP_USHORT( sum, seq ) ); + break; + case IM_BANDFMT_SHORT: + CONV_INT( signed short, IM_CLIP_SHORT( sum, seq ) ); + break; + case IM_BANDFMT_UINT: + CONV_INT( unsigned int, IM_CLIP_NONE( sum, seq ) ); + break; + case IM_BANDFMT_INT: + CONV_INT( signed int, IM_CLIP_NONE( sum, seq ) ); + break; + case IM_BANDFMT_FLOAT: + CONV_FLOAT( float ); + break; + case IM_BANDFMT_DOUBLE: + CONV_FLOAT( double ); + break; + + default: + assert( 0 ); + } + } + + return( 0 ); +} + +int +im_convsep_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + Conv *conv; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_error( "im_convsep", _( "non-complex uncoded only" ) ); + return( -1 ); + } + if( !mask || mask->xsize > 1000 || mask->ysize > 1000 || + mask->xsize <= 0 || mask->ysize <= 0 || !mask->coeff || + mask->scale == 0 ) { + im_error( "im_convsep", _( "nonsense mask parameters" ) ); + return( -1 ); + } + if( mask->xsize != 1 && mask->ysize != 1 ) { + im_error( "im_convsep", _( "expect 1xN or Nx1 input mask" ) ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( !(conv = conv_new( in, out, mask )) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= conv->size - 1; + out->Ysize -= conv->size - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_error( "im_convsep", _( "image too small for mask" ) ); + return( -1 ); + } + + /* SMALLTILE seems the fastest in benchmarks. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) || + im_generate( out, start_conv, gen_conv, stop_conv, in, conv ) ) + return( -1 ); + + out->Xoffset = -mask->xsize / 2; + out->Yoffset = -mask->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_convsep( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + IMAGE *t1 = im_open_local( out, "im_convsep intermediate", "p" ); + int size = mask->xsize * mask->ysize; + + if( !t1 || + im_embed( in, t1, 1, size / 2, size / 2, + in->Xsize + size - 1, + in->Ysize + size - 1 ) || + im_convsep_raw( t1, out, mask ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_convsepf.c b/libsrc/convolution/im_convsepf.c new file mode 100644 index 00000000..4eb1bf12 --- /dev/null +++ b/libsrc/convolution/im_convsepf.c @@ -0,0 +1,347 @@ +/* @(#) Convolve an image with a DOUBLEMASK. Image can have any number of bands, + * @(#) any non-complex type. Output is IM_BANDFMT_FLOAT for all non-complex inputs + * @(#) except IM_BANDFMT_DOUBLE, which gives IM_BANDFMT_DOUBLE. + * @(#) Separable mask of sizes 1xN or Nx1 + * @(#) + * @(#) int im_convsepf( in, out, mask ) + * @(#) IMAGE *in, *out; + * @(#) DOUBLEMASK *mask; details in mask.h + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * @(#) Picture can have any number of channels (max 64). + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 29/04/1991 + * Modified on: 29/4/93 K.Martinez for sys5 + * 9/3/01 JC + * - rewritten using im_conv() + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our parameters ... we take a copy of the mask argument. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + DOUBLEMASK *mask; /* Copy of mask arg */ + + int size; /* N for our 1xN or Nx1 mask */ + int scale; /* Our scale ... we have to ^2 mask->scale */ +} Conv; + +/* End of evaluation. + */ +static int +conv_destroy( Conv *conv ) +{ + if( conv->mask ) { + (void) im_free_dmask( conv->mask ); + conv->mask = NULL; + } + + return( 0 ); +} + +static Conv * +conv_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + Conv *conv = IM_NEW( out, Conv ); + + if( !conv ) + return( NULL ); + + conv->in = in; + conv->out = out; + conv->mask = NULL; + conv->size = mask->xsize * mask->ysize; + conv->scale = mask->scale * mask->scale; + + if( im_add_close_callback( out, + (im_callback_fn) conv_destroy, conv, NULL ) || + !(conv->mask = im_dup_dmask( mask, "conv_mask" )) ) + return( NULL ); + + return( conv ); +} + +/* Our sequence value. + */ +typedef struct { + Conv *conv; + REGION *ir; /* Input region */ + + PEL *sum; /* Line buffer */ +} ConvSequence; + +/* Free a sequence value. + */ +static int +stop_conv( ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +start_conv( IMAGE *out, IMAGE *in, Conv *conv ) +{ + ConvSequence *seq = IM_NEW( out, ConvSequence ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->conv = conv; + seq->ir = NULL; + seq->sum = NULL; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + if( im_isint( conv->out ) ) + seq->sum = (PEL *) + IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), int ); + else + seq->sum = (PEL *) + IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), double ); + if( !seq->ir || !seq->sum ) { + stop_conv( seq, in, conv ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* What we do for every point in the mask, for each pixel. + */ +#define VERTICAL_CONV { z -= 1; li -= lskip; sum += coeff[z] * vfrom[li]; } +#define HORIZONTAL_CONV { z -= 1; li -= bands; sum += coeff[z] * hfrom[li]; } + +#define CONV_FLOAT( ITYPE, OTYPE ) { \ + ITYPE *vfrom; \ + double *vto; \ + double *hfrom; \ + OTYPE *hto; \ + \ + /* Convolve to sum array. We convolve the full width of \ + * this input line. \ + */ \ + vfrom = (ITYPE *) IM_REGION_ADDR( ir, le, y ); \ + vto = (double *) seq->sum; \ + for( x = 0; x < isz; x++ ) { \ + double sum; \ + \ + z = conv->size; \ + li = lskip * z; \ + sum = 0; \ + \ + IM_UNROLL( z, VERTICAL_CONV ); \ + \ + vto[x] = sum; \ + vfrom += 1; \ + } \ + \ + /* Convolve sums to output. \ + */ \ + hfrom = (double *) seq->sum; \ + hto = (OTYPE *) IM_REGION_ADDR( or, le, y ); \ + for( x = 0; x < osz; x++ ) { \ + double sum; \ + \ + z = conv->size; \ + li = bands * z; \ + sum = 0; \ + \ + IM_UNROLL( z, HORIZONTAL_CONV ); \ + \ + sum = (sum / conv->scale) + mask->offset; \ + \ + hto[x] = sum; \ + hfrom += 1; \ + } \ +} + +/* Convolve! + */ +static int +gen_conv( REGION *or, ConvSequence *seq, IMAGE *in, Conv *conv ) +{ + REGION *ir = seq->ir; + DOUBLEMASK *mask = conv->mask; + double *coeff = conv->mask->coeff; + int bands = in->Bands; + + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int osz = IM_REGION_N_ELEMENTS( or ); + + Rect s; + int lskip; + int isz; + int x, y, z, li; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += conv->size - 1; + s.height += conv->size - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + lskip = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( in ); + isz = IM_REGION_N_ELEMENTS( ir ); + + for( y = to; y < bo; y++ ) { + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + CONV_FLOAT( unsigned char, float ); break; + case IM_BANDFMT_CHAR: + CONV_FLOAT( signed char, float ); break; + case IM_BANDFMT_USHORT: + CONV_FLOAT( unsigned short, float ); break; + case IM_BANDFMT_SHORT: + CONV_FLOAT( signed short, float ); break; + case IM_BANDFMT_UINT: + CONV_FLOAT( unsigned int, float ); break; + case IM_BANDFMT_INT: + CONV_FLOAT( signed int, float ); break; + case IM_BANDFMT_FLOAT: + CONV_FLOAT( float, float ); break; + case IM_BANDFMT_DOUBLE: + CONV_FLOAT( double, double ); break; + + default: + assert( 0 ); + } + } + + return( 0 ); +} + +int +im_convsepf_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + Conv *conv; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_error( "im_convsepf", _( "non-complex uncoded only" ) ); + return( -1 ); + } + if( !mask || mask->xsize > 1000 || mask->ysize > 1000 || + mask->xsize <= 0 || mask->ysize <= 0 || !mask->coeff || + mask->scale == 0 ) { + im_error( "im_convsepf", _( "bad mask parameters" ) ); + return( -1 ); + } + if( mask->xsize != 1 && mask->ysize != 1 ) { + im_error( "im_convsepf", _( "expect 1xN or Nx1 input mask" ) ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( !(conv = conv_new( in, out, mask )) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_isint( in ) ) { + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + } + out->Xsize -= conv->size - 1; + out->Ysize -= conv->size - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_error( "im_convsepf", _( "image too small for mask" ) ); + return( -1 ); + } + + /* SMALLTILE seems fastest. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) || + im_generate( out, start_conv, gen_conv, stop_conv, in, conv ) ) + return( -1 ); + + out->Xoffset = -conv->size / 2; + out->Yoffset = -conv->size / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_convsepf( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + IMAGE *t1 = im_open_local( out, "im_convsepf intermediate", "p" ); + int size = mask->xsize * mask->ysize; + + if( !t1 || + im_embed( in, t1, 1, size / 2, size / 2, + in->Xsize + size - 1, + in->Ysize + size - 1 ) || + im_convsepf_raw( t1, out, mask ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_convsub.c b/libsrc/convolution/im_convsub.c new file mode 100644 index 00000000..738d240f --- /dev/null +++ b/libsrc/convolution/im_convsub.c @@ -0,0 +1,264 @@ +/* @(#) Function which convolves and subsamples VASARI format picture + * @(#) with a mask stored in a file argument. + * @(#) + * @(#) int im_convsub( in, out, mask, xskip, yskip ) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *mask; details in vips.h + * @(#) int xskip, yskip; is the subsamping factor along both directions + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * @(#) + * @(#) Picture can have any number of channels (max 64). + * @(#) It is assummed that the output picture is subsampled on + * @(#) both directions by a factor of xskip horizontally and yskip vertically. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 29/04/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + + +int im_convsub( in, out, m, xskip, yskip ) +IMAGE *in, *out; +INTMASK *m; +int xskip, yskip; +{ + + + int x; /* horizontal direction */ + int y; /* vertical direction */ + int n_clipped = 0; + int p_clipped = 0; + int i, b; + PEL **pnts, **cpnt1s, **cpnt2s; /* to keep pointers to data */ + PEL **pnt, **cpnt1, **cpnt2; /* to keep pointers to data */ + PEL *input, *line, *cpline; + int *pm; /* pointer to mask coefficients */ + int count; /* no of non zero elms of the original mask */ + int *newm, *pnewm; /* pointer to non zero mask coefficients */ + int os; /* size of an input line of data */ + int ms; /* is m->xsize * m->ysize */ + int **lut_orig, **lut; + int lutcnt = 0; + int rounding, sum; + int tempsize; + +/* Check input, output and vars */ + if ((xskip < 1)||(yskip < 1)) + { + im_errormsg("im_convsub: xskip and yskip must be >= 1"); + return(-1); + } + if (im_iocheck(in, out) == -1) + { im_errormsg("im_convsub: Unable to im_iocheck"); return(-1); } + + if ( (in->Coding != IM_CODING_NONE)||(in->Bbits != IM_BBITS_BYTE) + ||(in->BandFmt != IM_BANDFMT_UCHAR) ) + { + im_errormsg("im_convsub:input should be unsigned char uncoded"); + return(-1); + } + +/* Prepare output */ + if (im_cp_desc(out, in) == -1) + { im_errormsg("im_convsub: im_cp_desc failed"); return(-1); } + tempsize = in->Xsize/xskip; + while ( 1 ) + { + if ( tempsize * xskip + m->xsize < in->Xsize ) + break; + else + tempsize--; + if ( tempsize < 0 ) + break; + } + out->Xsize = tempsize; + tempsize = in->Ysize/yskip; + while ( 1 ) + { + if ( tempsize * yskip + m->ysize < in->Ysize ) + break; + else + tempsize--; + if ( tempsize < 0 ) + break; + } + out->Ysize = tempsize; + if ( ( out->Xsize < 2 )||( out->Ysize < 2 ) ) + {im_errormsg("im_convsub: too small output sizes");return(-1); } + + if( im_setupout(out) == -1) + {im_errormsg("im_convsub: im_setupout failed"); return(-1); } + +/* Malloc one line of output data */ + os = out->Xsize * out->Bands; + if ( (line=(PEL*)calloc( (unsigned)os, sizeof(char))) == NULL) + { im_errormsg("im_convsub: unable to calloc(1)"); return(-1); } + +/* Malloc pointers and put them at correct location */ + ms = m->xsize * m->ysize; + count = 0; /* exclude the non-zero elms */ + pm = m->coeff; + for ( i=0; idata; + pm = m->coeff; + pnewm = newm; + for (y=0; yysize; y++) + { + for (x=0; xxsize; x++) + { + if ( *pm != 0 ) + { + *pnewm++ = *pm; + pnt[i] = (input +(x + y*in->Xsize) * in->Bands); + i++; + } + pm++; + } + } + + if ( i != count ) + { im_errormsg("im_convsub: impossible state"); return(-1); } + +/* Malloc pointers; not all lut_orig are used necessarily */ + lut_orig = (int**)calloc((unsigned)count, sizeof(int**) ); + lut = (int**)calloc((unsigned)count, sizeof(int**) ); + if ( (lut == NULL) || (lut_orig == NULL) ) + { im_errormsg("im_conv: unable to calloc(1)"); return(-1); } + +/* Create luts; count is needed for freeing pointers. Not all lut_orig are used + * if zero elms are detected. + */ + if ( im__create_int_luts(newm, count, lut_orig, lut, &lutcnt ) == -1 ) + { + im_errormsg("im_convsub: im_create_int_luts failed"); + return(-1); + } + + rounding = m->scale/2; + +/* Output out->Ysize processed lines */ + for(y=0; y < out->Ysize; y++) + { + cpline = line; + for (i=0; iXsize * in->Bands * yskip ); + } + + /* process out->Xsize points */ + for( x = 0; x < out->Xsize; x++ ) + { + for (i=0; iBands; + } + for ( b=0; bBands; b++ ) + { + sum = 0; + for (i=0; iscale ) + m->offset; + + if ( sum < (int)0 ) + { n_clipped++; sum = (int)0; } + else if ( sum > (int)255) + { p_clipped++; sum = (int)255; } + *cpline++ = (unsigned char)sum; + } + } + + /* Output the calculated line */ + if ( im_writeline(y, out, (PEL*)line) == -1 ) + { + im_errormsg("im_convsub: im_writeline failed(2)"); + free((char*)line); free((char*)newm); + free((char*)pnts); + free((char*)cpnt1s); free((char*)cpnt2s); + for ( i=0; i +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Per-call struct. + */ +typedef struct _Embed { + IMAGE *in; + IMAGE *out; + int flag; + int x, y, w, h; + + /* Geometry calculations. + */ + Rect rout; /* Whole output area */ + Rect rsub; /* Rect occupied by image */ + + /* The 8 border pieces. The 4 borders strictly up/down/left/right of + * the main image, and the 4 corner pieces. + */ + Rect border[8]; +} Embed; + +/* Paint 'value' into an area of a region. 0/255 for value usually. + */ +static void +embed_paint_rect( REGION *or, Rect *r, int value ) +{ + Rect ovl; + + im_rect_intersectrect( r, &or->valid, &ovl ); + if( !im_rect_isempty( &ovl ) ) { + PEL *q = (PEL *) IM_REGION_ADDR( or, ovl.left, ovl.top ); + int wd = ovl.width * IM_IMAGE_SIZEOF_PEL( or->im ); + int ls = IM_REGION_LSKIP( or ); + int y; + + for( y = 0; y < ovl.height; y++ ) { + memset( (char *) q, value, wd ); + q += ls; + } + } +} + +/* r is the bit we are trying to paint, guaranteed to be entirely within + * border area i. Set out to be the edge of the image we need to paint the + * pixels in r. + */ +static void +embed_find_edge( Embed *embed, Rect *r, int i, Rect *out ) +{ + /* Expand the border by 1 pixel, intersect with the image area, and we + * get the edge. Usually too much though: eg. we could make the entire + * right edge. + */ + *out = embed->border[i]; + im_rect_marginadjust( out, 1 ); + im_rect_intersectrect( out, &embed->rsub, out ); + + /* Usually too much though: eg. we could make the entire + * right edge. If we're strictly up/down/left/right of the image, we + * can trim. + */ + if( i == 0 || i == 2 ) { + Rect extend; + + /* Above or below. + */ + extend = *r; + extend.top = 0; + extend.height = embed->h; + im_rect_intersectrect( out, &extend, out ); + } + if( i == 1 || i == 3 ) { + Rect extend; + + /* Left or right. + */ + extend = *r; + extend.left = 0; + extend.width = embed->w; + im_rect_intersectrect( out, &extend, out ); + } +} + +/* Copy a single pixel sideways into a line of pixels. + */ +static void +embed_copy_pixel( Embed *embed, PEL *q, PEL *p, int n ) +{ + const int bs = IM_IMAGE_SIZEOF_PEL( embed->in ); + + int x, b; + + for( x = 0; x < n; x++ ) + for( b = 0; b < bs; b++ ) + *q++ = p[b]; +} + +/* Paint r of region or. It's a border area, lying entirely within + * embed->border[i]. p points to the top-left source pixel to fill with. + * plsk is the line stride. + */ +static void +embed_paint_edge( Embed *embed, REGION *or, int i, Rect *r, PEL *p, int plsk ) +{ + const int bs = IM_IMAGE_SIZEOF_PEL( embed->in ); + + Rect todo; + PEL *q; + int y; + + /* Pixels left to paint. + */ + todo = *r; + + /* Corner pieces ... copy the single pixel to paint the top line of + * todo, then use the line copier below to paint the rest of it. + */ + if( i > 3 ) { + q = (PEL *) IM_REGION_ADDR( or, todo.left, todo.top ); + embed_copy_pixel( embed, q, p, todo.width ); + + p = q; + todo.top += 1; + todo.height -= 1; + } + + if( i == 1 || i == 3 ) { + /* Vertical line of pixels to copy. + */ + for( y = 0; y < todo.height; y++ ) { + q = (PEL *) IM_REGION_ADDR( or, + todo.left, todo.top + y ); + embed_copy_pixel( embed, q, p, todo.width ); + p += plsk; + } + } + else { + /* Horizontal line of pixels to copy. + */ + for( y = 0; y < todo.height; y++ ) { + q = (PEL *) IM_REGION_ADDR( or, + todo.left, todo.top + y ); + memcpy( q, p, bs * todo.width ); + } + } +} + +static int +embed_gen( REGION *or, REGION *ir, IMAGE *in, Embed *embed ) +{ + Rect *r = &or->valid; + + Rect ovl; + int i; + PEL *p; + int plsk; + + /* Entirely within the input image? Generate the subimage and copy + * pointers. + */ + if( im_rect_includesrect( &embed->rsub, r ) ) { + Rect need; + + need = *r; + need.left -= embed->x; + need.top -= embed->y; + if( im_prepare( ir, &need ) || + im_region_region( or, ir, r, need.left, need.top ) ) + return( -1 ); + + return( 0 ); + } + + /* Does any of the input image appear in the area we have been asked + * to make? Paste it in. + */ + im_rect_intersectrect( r, &embed->rsub, &ovl ); + if( !im_rect_isempty( &ovl ) ) { + /* Paint the bits coming from the input image. + */ + ovl.left -= embed->x; + ovl.top -= embed->y; + if( im_prepare_to( ir, or, &ovl, + ovl.left + embed->x, ovl.top + embed->y ) ) + return( -1 ); + ovl.left += embed->x; + ovl.top += embed->y; + } + + switch( embed->flag ) { + case 0: + case 4: + /* Paint the borders a solid value. + */ + for( i = 0; i < 8; i++ ) + embed_paint_rect( or, &embed->border[i], + embed->flag == 0 ? 0 : 255 ); + break; + + case 1: + /* Extend the borders. + */ + for( i = 0; i < 8; i++ ) { + Rect todo; + Rect edge; + + im_rect_intersectrect( r, &embed->border[i], &todo ); + if( !im_rect_isempty( &todo ) ) { + embed_find_edge( embed, &todo, i, &edge ); + + /* Did we paint any of the input image? If we + * did, we can fetch the edge pixels from + * that. + */ + if( !im_rect_isempty( &ovl ) ) { + p = (PEL *) IM_REGION_ADDR( or, + edge.left, edge.top ); + plsk = IM_REGION_LSKIP( or ); + } + else { + /* No pixels painted ... fetch + * directly from the input image. + */ + edge.left -= embed->x; + edge.top -= embed->y; + if( im_prepare( ir, &edge ) ) + return( -1 ); + p = (PEL *) IM_REGION_ADDR( ir, + edge.left, edge.top ); + plsk = IM_REGION_LSKIP( ir ); + } + + embed_paint_edge( embed, + or, i, &todo, p, plsk ); + } + } + + break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +static Embed * +embed_new( IMAGE *in, IMAGE *out, int flag, int x, int y, int w, int h ) +{ + Embed *embed = IM_NEW( out, Embed ); + Rect want; + + /* Take a copy of args. + */ + embed->in = in; + embed->out = out; + embed->flag = flag; + embed->x = x; + embed->y = y; + embed->w = w; + embed->h = h; + + /* Whole output area. + */ + embed->rout.left = 0; + embed->rout.top = 0; + embed->rout.width = out->Xsize; + embed->rout.height = out->Ysize; + + /* Rect occupied by image (can be clipped to nothing). + */ + want.left = x; + want.top = y; + want.width = in->Xsize; + want.height = in->Ysize; + im_rect_intersectrect( &want, &embed->rout, &embed->rsub ); + + /* FIXME ... actually, it can't. embed_find_edge() will fail if rsub + * is empty. Make this more general at some point and remove this + * test. + */ + if( im_rect_isempty( &embed->rsub ) ) { + im_error( "im_embed", _( "bad dimensions" ) ); + return( NULL ); + } + + /* Edge rects of new pixels ... top, right, bottom, left. Order + * important. Can be empty. + */ + embed->border[0].left = embed->rsub.left; + embed->border[0].top = 0; + embed->border[0].width = embed->rsub.width; + embed->border[0].height = embed->rsub.top; + + embed->border[1].left = IM_RECT_RIGHT( &embed->rsub ); + embed->border[1].top = embed->rsub.top; + embed->border[1].width = out->Xsize - IM_RECT_RIGHT( &embed->rsub ); + embed->border[1].height = embed->rsub.height; + + embed->border[2].left = embed->rsub.left; + embed->border[2].top = IM_RECT_BOTTOM( &embed->rsub ); + embed->border[2].width = embed->rsub.width; + embed->border[2].height = out->Ysize - IM_RECT_BOTTOM( &embed->rsub ); + + embed->border[3].left = 0; + embed->border[3].top = embed->rsub.top; + embed->border[3].width = embed->rsub.left; + embed->border[3].height = embed->rsub.height; + + /* Corner rects. Top-left, top-right, bottom-right, bottom-left. Order + * important. + */ + embed->border[4].left = 0; + embed->border[4].top = 0; + embed->border[4].width = embed->rsub.left; + embed->border[4].height = embed->rsub.top; + + embed->border[5].left = IM_RECT_RIGHT( &embed->rsub ); + embed->border[5].top = 0; + embed->border[5].width = out->Xsize - IM_RECT_RIGHT( &embed->rsub ); + embed->border[5].height = embed->rsub.top; + + embed->border[6].left = IM_RECT_RIGHT( &embed->rsub ); + embed->border[6].top = IM_RECT_BOTTOM( &embed->rsub ); + embed->border[6].width = out->Xsize - IM_RECT_RIGHT( &embed->rsub ); + embed->border[6].height = out->Ysize - IM_RECT_BOTTOM( &embed->rsub ); + + embed->border[7].left = 0; + embed->border[7].top = IM_RECT_BOTTOM( &embed->rsub ); + embed->border[7].width = embed->rsub.left; + embed->border[7].height = out->Ysize - IM_RECT_BOTTOM( &embed->rsub ); + + return( embed ); +} + +/* Do flag 0/4 (black/white) and 1 (extend). + */ +static int +embed( IMAGE *in, IMAGE *out, int flag, int x, int y, int w, int h ) +{ + Embed *embed; + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = w; + out->Ysize = h; + + if( !(embed = embed_new( in, out, flag, x, y, w, h )) || + im_demand_hint( out, IM_SMALLTILE, in, NULL ) || + im_generate( out, + im_start_one, embed_gen, im_stop_one, + in, embed ) ) + return( -1 ); + + return( 0 ); +} + +int +im_embed( IMAGE *in, IMAGE *out, int flag, int x, int y, int w, int h ) +{ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_embed", _( "unknown image coding type" ) ); + return( -1 ); + } + if( flag < 0 || flag > 4 ) { + im_error( "im_embed", _( "unknown flag" ) ); + return( -1 ); + } + if( w <= 0 || h <= 0 ) { + im_error( "im_embed", _( "bad dimensions" ) ); + return( -1 ); + } + + /* nip can generate this quite often ... just copy. + */ + if( x == 0 && y == 0 && w == in->Xsize && h == in->Ysize ) + return( im_copy( in, out ) ); + + switch( flag ) { + case 0: + case 1: + case 4: + if( embed( in, out, flag, x, y, w, h ) ) + return( -1 ); + break; + + case 2: +{ + /* Clock arithmetic: we want negative x/y to wrap around + * nicely. + */ + const int nx = x < 0 ? + -x % in->Xsize : + in->Xsize - x % in->Xsize; + const int ny = y < 0 ? + -y % in->Ysize : + in->Ysize - y % in->Ysize; + + IMAGE *t[1]; + + if( im_open_local_array( out, t, 1, "embed-flag2", "p" ) || + im_replicate( in, t[0], + w / in->Xsize + 2, h / in->Ysize + 2 ) || + im_extract_area( t[0], out, nx, ny, w, h ) ) + return( -1 ); +} + break; + + case 3: +{ + /* As case 2, but the tiles are twice the size because of + * mirroring. + */ + const int w2 = in->Xsize * 2; + const int h2 = in->Ysize * 2; + + const int nx = x < 0 ? -x % w2 : w2 - x % w2; + const int ny = y < 0 ? -y % h2 : h2 - y % h2; + + IMAGE *t[7]; + + if( im_open_local_array( out, t, 7, "embed-flag3", "p" ) || + /* Cache the edges of in, since we may well be reusing + * them repeatedly. Will only help for tiny borders + * (up to 20 pixels?), but that's our typical case + * with im_conv() etc. + im_cache( in, t[0], IM__TILE_WIDTH, IM__TILE_HEIGHT, + 3 * (in->Xsize / IM__TILE_WIDTH + 1) + + 3 * (in->Ysize / IM__TILE_HEIGHT + 1) ) || + */ + + /* + + FIXME ... alternatively, don't cache, hmm, + need to time this for typical cases + + */ + im_copy( in, t[0] ) || + + /* Make a 2x2 mirror tile. + */ + im_fliphor( t[0], t[1] ) || + im_lrjoin( t[0], t[1], t[2] ) || + im_flipver( t[2], t[3] ) || + im_tbjoin( t[2], t[3], t[4] ) || + + /* Repeat, then cut out the centre. + */ + im_replicate( t[4], t[5], + w / t[4]->Xsize + 2, h / t[4]->Ysize + 2 ) || + im_extract_area( t[5], t[6], nx, ny, w, h ) || + + /* Overwrite the centre with the input, much faster + * for centre pixels. + */ + im_insert_noexpand( t[6], in, out, x, y ) ) + return( -1 ); +} + break; + + default: + assert( 0 ); + } + + out->Xoffset = x; + out->Yoffset = y; + + return( 0 ); +} diff --git a/libsrc/convolution/im_fastcor.c b/libsrc/convolution/im_fastcor.c new file mode 100644 index 00000000..6669ce5b --- /dev/null +++ b/libsrc/convolution/im_fastcor.c @@ -0,0 +1,209 @@ +/* @(#) Functions which calculates spatial correlation between two images. + * @(#) by taking absolute differences pixel by pixel without calculating + * @(#) the correlation coefficient. + * @(#) + * @(#) The function works as follows: + * @(#) + * @(#) int im_fastcor( im, ref, out ) + * @(#) IMAGE *im, *ref, *out; + * @(#) + * @(#) ref must be smaller than in. The correlation is + * @(#) calculated by overlaping im on the top left corner of ref + * @(#) and moving it all over ref calculating the correlation coefficient + * @(#) at each point. The resultant coefficients are written as unsigned int + * @(#) numbers in out which has the size of im. + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : 15/03/1991 + * 20/2/95 JC + * - ANSIfied + * - in1 and in2 swapped, to match order for im_spcor + * - memory leaks fixed + * 21/2/95 JC + * - partialed + * - speed-ups + * 7/4/04 + * - now uses im_embed() with edge stretching on the output + * - sets Xoffset / Yoffset + * 8/3/06 JC + * - use im_embed() with edge stretching on the input, not the output + * - calculate sum of squares of differences, rather than abs of + * difference + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Fastcor generate function. + */ +static int +fastcor_gen( REGION *or, REGION *ir, IMAGE *in, IMAGE *ref ) +{ + Rect irect; + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ri = IM_RECT_RIGHT(r); + + int x, y, i, j; + int lsk; + + /* What part of ir do we need? + */ + irect.left = or->valid.left; + irect.top = or->valid.top; + irect.width = or->valid.width + ref->Xsize - 1; + irect.height = or->valid.height + ref->Ysize - 1; + + if( im_prepare( ir, &irect ) ) + return( -1 ); + lsk = IM_REGION_LSKIP( ir ); + + /* Loop over or. + */ + for( y = to; y < bo; y++ ) { + PEL *a = (PEL *) IM_REGION_ADDR( ir, le, y ); + unsigned int *q = (unsigned int *) IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + int sum = 0; + PEL *b = (PEL *) ref->data; + PEL *a1 = a; + + for( j = 0; j < ref->Ysize; j++ ) { + PEL *a2 = a1; + + for( i = 0; i < ref->Xsize; i++ ) { + int t = *b++ - *a2++; + + sum += t * t; + } + + a1 += lsk; + } + + *q++ = sum; + a += 1; + } + } + + return( 0 ); +} + +/* Raw fastcor, with no borders. + */ +int +im_fastcor_raw( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + /* PIO between in and out; WIO from ref. + */ + if( im_piocheck( in, out ) || im_incheck( ref ) ) + return( -1 ); + + /* Check sizes. + */ + if( in->Xsize < ref->Xsize || in->Ysize < ref->Ysize ) { + im_errormsg( "im_fastcor: ref not smaller than in" ); + return( -1 ); + } + + /* Check types. + */ + if( in->Coding != IM_CODING_NONE || in->Bands != 1 || + in->BandFmt != IM_BANDFMT_UCHAR || + ref->Coding != IM_CODING_NONE || ref->Bands != 1 || + ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_fastcor_raw: input not uncoded 1 band uchar" ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_descv( out, in, ref, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_INT; + out->BandFmt = IM_BANDFMT_UINT; + out->Xsize = in->Xsize - ref->Xsize + 1; + out->Ysize = in->Ysize - ref->Ysize + 1; + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Write the correlation. + */ + if( im_generate( out, + im_start_one, fastcor_gen, im_stop_one, in, ref ) ) + return( -1 ); + + out->Xoffset = -ref->Xsize / 2; + out->Yoffset = -ref->Ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_fastcor( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_fastcor intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + ref->Xsize / 2, ref->Ysize / 2, + in->Xsize + ref->Xsize - 1, + in->Ysize + ref->Ysize - 1 ) || + im_fastcor_raw( t1, ref, out ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_gaussmasks.c b/libsrc/convolution/im_gaussmasks.c new file mode 100644 index 00000000..3402ccab --- /dev/null +++ b/libsrc/convolution/im_gaussmasks.c @@ -0,0 +1,182 @@ +/* @(#) Returns a circularly symmetric Gaussian mask + * @(#) min_amplitude should be greater than 0.0 and less than 1.0 + * @(#) min_amplitude determines the size of the mask; if for instance + * @(#) the value .1 is entered this means that the produced mask is clipped + * @(#) at values less than 10 percent of the minimum negative amplitude. + * @(#) If the value of min_amplitude is too small, then the filter coefficients + * @(#) are calculated for masksize equal to the min of 8 * sigma or 256. + * @(#) The mask can be directly used with the vasari convolution programs, + * @(#) the default offset set is 0 + * @(#) + * @(#) DOUBLEMASK *im_gauss_dmask( filename, sigma, min_amplitude ) + * @(#) char *filename; + * @(#) double sigma, min_amplitude; + * @(#) + * @(#) Returns a laplacian of Gaussian square double mask or NULL on error + * @(#) + * @(#) DOUBLEMASK *im_gauss_imask( filename, sigma, min_amplitude ) + * @(#) char *filename; + * @(#) double sigma, min_amplitude; + * @(#) + * @(#) Returns a laplacian of Gaussian square int mask or NULL on error + */ + +/* Written on: 30/11/1989 by Nicos + * Updated on: 6/12/1991 + * 7/8/96 JC + * - ansified, mem leaks plugged + * 20/11/98 JC + * - mask too large check added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define IM_MAXMASK 256 + +DOUBLEMASK * +im_gauss_dmask( const char *filename, double sigma, double min_ampl ) +{ + int x, y, k; + double distance; + double temp; + double *pt1, *pt2, *pt3, *pt4; + int max_x; + int xm, ym; + int xm2, ym2; /* xm2 = xm/2 */ + int offset; + double *cf, *cfs, *mc; + DOUBLEMASK *m; + double sig2, sum; /* sig2 = 2. * sigma * sigma */ + + /* Find the size of the mask depending on the entered data + */ + sig2 = 2. * sigma * sigma; + max_x = 8 * sigma > IM_MAXMASK ? IM_MAXMASK : 8 * sigma ; + for( x = 0; x < max_x; x++ ) { + temp = exp( - ((double)(x * x))/sig2 ); + if( temp < min_ampl ) + break; + } + if( x == max_x ) { + im_errormsg( "im_gauss_dmask: mask too large" ); + return( NULL ); + } + + xm2 = x; + ym2 = x; + xm = xm2 * 2 + 1; + ym = ym2 * 2 + 1; + + if( !(cfs = IM_ARRAY( NULL, (xm2+1)*(ym2+1), double )) ) + return( NULL ); + + for( k = 0, y = 0; y <= ym2; y++ ) { + for( x = 0; x <= xm2; x++, k++ ) { + distance = x*x + y*y; + cfs[k] = exp( -distance / sig2 ); + } + } + +#ifdef PIM_RINT + for( k = 0, y = 0; y <= ymask_2; y++ ) { + for( x = 0; x <= xmask_2; x++, k++ ) + fprintf(stderr, "%3.2f ", cfs[k] ); + fprintf(stderr, "\n"); + } +#endif + + if( !(m = im_create_dmask( filename, xm, ym )) ) { + im_free( cfs ); + return( NULL ); + } + + /* copy the 1/4 cfs into the m + */ + cf = cfs; + offset = xm2 * (xm + 1); + mc = m->coeff + offset; + for( y = 0; y <= ym2; y++ ) { + for( x = 0; x <= xm2; x++ ) { + pt1 = mc + (y * xm) + x; + pt2 = mc - (y * xm) + x; + pt3 = mc + (y * xm) - x; + pt4 = mc - (y * xm) - x; + + *pt1 = cf[x]; + *pt2 = cf[x]; + *pt3 = cf[x]; + *pt4 = cf[x]; + } + + cf += (xm2 + 1); + } + im_free( cfs ); + + sum = 0.0; + for( k = 0, y = 0; y < m->ysize; y++ ) + for( x = 0; x < m->xsize; x++, k++ ) + sum += m->coeff[k]; + m->scale = sum; + m->offset = 0.0; + +#ifdef PIM_RINT + im_print_dmask( m ); +#endif + return( m ); +} + +INTMASK * +im_gauss_imask( const char *filename, double sigma, double min_amplitude ) +{ + DOUBLEMASK *dm; + INTMASK *im; + + if( !(dm = im_gauss_dmask( filename, sigma, min_amplitude )) ) + return( NULL ); + + if( !(im = im_scale_dmask( dm, dm->filename )) ) { + im_free_dmask( dm ); + return( NULL ); + } + im_free_dmask( dm ); + + return( im ) ; +} diff --git a/libsrc/convolution/im_gaussnoise.c b/libsrc/convolution/im_gaussnoise.c new file mode 100644 index 00000000..d3da077c --- /dev/null +++ b/libsrc/convolution/im_gaussnoise.c @@ -0,0 +1,157 @@ +/* @(#) Creates a Gaussian noisy float image with mean of 0 and variance of 1 + * @(#) by averaging 12 random numbers + * @(#) Creates one band float image + * @(#) page 78 PIETGEN 1989 n = 12 + * @(#) Usage + * @(#) + * @(#) int im_gaussnoise(image, xsize, ysize, mean, sigma) + * @(#) IMAGE *image; + * @(#) int xsize, ysize; + * @(#) double mean, sigma; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright 1990, N. Dessipris. + * + * File written on 2/12/1986 + * Author : N. Dessipris + * Updated : 6/6/1991 + * 21/7/93 JC + * - im_outcheck() call added + * 1/2/95 JC + * - declaration for drand48() added + * - partialised, adapting im_black() + * 23/10/98 JC + * - drand48() chaged to random() for poartability + * 21/10/02 JC + * - tries rand() if random() is not available + * - uses RAND_MAX, d'oh + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Keep parameters here. + */ +typedef struct { + double mean; + double sigma; +} GnoiseInfo; + +/* Generate function --- just fill the region with noise. "dummy" is our + * sequence value: we don't need one. + */ +/*ARGSUSED*/ +static int +gnoise_gen( REGION *or, void *dummy, GnoiseInfo *gin ) +{ + int x, y, i; + int sz = IM_REGION_N_ELEMENTS( or ); + + for( y = 0; y < or->valid.height; y++ ) { + float *q = (float *) + IM_REGION_ADDR( or, or->valid.left, y + or->valid.top ); + + for( x = 0; x < sz; x++ ) { + double sum = 0.0; + + for( i = 0; i < 12; i++ ) +#ifdef HAVE_RANDOM + sum += (double) random() / RAND_MAX; +#else /*HAVE_RANDOM*/ +#ifdef HAVE_RAND + sum += (double) rand() / RAND_MAX; +#else /*HAVE_RAND*/ +#error "no random number generator found" +#endif /*HAVE_RAND*/ +#endif /*HAVE_RAND*/ + + q[x] = (sum - 6.0) * gin->sigma + gin->mean; + } + } + + return( 0 ); +} + +/* Make a one band float image of gaussian noise. + */ +int +im_gaussnoise( IMAGE *out, int x, int y, double mean, double sigma ) +{ + GnoiseInfo *gin; + + /* Check parameters. + */ + if( x < 0 || y < 0 ) { + im_errormsg( "im_gaussnoise: bad parameter" ); + return( -1 ); + } + + /* Check descriptor. + */ + if( im_poutcheck( out ) ) + return( -1 ); + + /* Set fields. + */ + im_initdesc( out, + x, y, 1, + IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, IM_CODING_NONE, IM_TYPE_B_W, + 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Save parameters. + */ + if( !(gin = IM_NEW( out, GnoiseInfo )) ) + return( -1 ); + gin->mean = mean; + gin->sigma = sigma; + + /* Generate image. + */ + if( im_generate( out, NULL, gnoise_gen, NULL, gin, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_gradcor.c b/libsrc/convolution/im_gradcor.c new file mode 100644 index 00000000..1d7743ad --- /dev/null +++ b/libsrc/convolution/im_gradcor.c @@ -0,0 +1,536 @@ +/* @(#) Like im_spcor(), but with a new metric. Docs to follow. + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 2007 Nottingham Trent University + * + * Author: Tom Vajzovic + * Written on: 2007-06-07 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + + +/** LOCAL TYPES **/ + +typedef struct { + REGION *reg; + int *region_xgrad; + int *region_ygrad; + size_t region_xgrad_area; + size_t region_ygrad_area; +} +gradcor_seq_t; + + +/** LOCAL FUNCTION DECLARATIONS **/ + +static void *gradcor_start( IMAGE *out, void *vptr_large, void *unrequired ); +static int gradcor_stop( void *vptr_seq, void *unrequired, void *unreq2 ); +static int gradcor_gen( REGION *to_make, void *vptr_seq, void *unrequired, void *vptr_grads ); + +#define XGRAD_GEN_DECLARATION( TYPE ) static int xgrad_gen_ ## TYPE( REGION *to_make, void *vptr_make_from, void *unrequired, void *unreq2 ) +#define YGRAD_GEN_DECLARATION( TYPE ) static int ygrad_gen_ ## TYPE( REGION *to_make, void *vptr_make_from, void *unrequired, void *unreq2 ) + +XGRAD_GEN_DECLARATION( guint8 ); +YGRAD_GEN_DECLARATION( guint8 ); +XGRAD_GEN_DECLARATION( gint8 ); +YGRAD_GEN_DECLARATION( gint8 ); +XGRAD_GEN_DECLARATION( guint16 ); +YGRAD_GEN_DECLARATION( guint16 ); +XGRAD_GEN_DECLARATION( gint16 ); +YGRAD_GEN_DECLARATION( gint16 ); +XGRAD_GEN_DECLARATION( guint32 ); +YGRAD_GEN_DECLARATION( guint32 ); +XGRAD_GEN_DECLARATION( gint32 ); +YGRAD_GEN_DECLARATION( gint32 ); +#if 0 +XGRAD_GEN_DECLARATION( float ); +YGRAD_GEN_DECLARATION( float ); +XGRAD_GEN_DECLARATION( double ); +YGRAD_GEN_DECLARATION( double ); +#endif + + +/** EXPORTED FUNCTION DEFINITIONS **/ + +int +im_gradcor( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ +#define FUNCTION_NAME "im_gradcor" + IMAGE *t1 = im_open_local( out, FUNCTION_NAME " intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + ref->Xsize / 2, ref->Ysize / 2, + in->Xsize + ref->Xsize - 1, + in->Ysize + ref->Ysize - 1 ) || + im_gradcor_raw( t1, ref, out ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +#undef FUNCTION_NAME +} + +int im_gradcor_raw( IMAGE *large, IMAGE *small, IMAGE *out ){ +#define FUNCTION_NAME "im_gradcor_raw" + + if( im_piocheck( large, out ) || im_pincheck( small ) ) + return -1; + + if( ! im_isint( large ) || ! im_isint( small ) ){ + im_error( FUNCTION_NAME, "image does not have integer band format" ); + return -1; + } + if( large-> Coding || small-> Coding ){ + im_error( FUNCTION_NAME, "image is not uncoded" ); + return -1; + } + if( 1 != large-> Bands || 1 != small-> Bands ){ + im_error( FUNCTION_NAME, "image is multi-band" ); + return -1; + } + if( large-> Xsize < small-> Xsize || large-> Ysize < small-> Ysize ){ + im_error( FUNCTION_NAME, "second image must be smaller than first" ); + return -1; + } + if( im_cp_desc( out, large ) ) + return -1; + + out-> Xsize= 1 + large-> Xsize - small-> Xsize; + out-> Ysize= 1 + large-> Ysize - small-> Ysize; + out-> BandFmt= IM_BANDFMT_FLOAT; + out-> Bbits= IM_BBITS_FLOAT; + + if( im_demand_hint( out, IM_FATSTRIP, large, NULL ) ) + return -1; + + { + IMAGE *xgrad= im_open_local( out, FUNCTION_NAME ": xgrad", "t" ); + IMAGE *ygrad= im_open_local( out, FUNCTION_NAME ": ygrad", "t" ); + IMAGE **grads= im_allocate_input_array( out, xgrad, ygrad, NULL ); + + return im_grad_x( small, xgrad ) + || im_grad_y( small, ygrad ) + || im_generate( out, gradcor_start, gradcor_gen, gradcor_stop, (void*) large, (void*) grads ); + } +#undef FUNCTION_NAME +} + +int im_grad_x( IMAGE *in, IMAGE *out ){ +#define FUNCTION_NAME "im_grad_x" + + if( im_piocheck( in, out ) ) + return -1; + + if( ! im_isint( in ) ){ + im_error( FUNCTION_NAME, "image does not have integer band format" ); + return -1; + } + if( in-> Coding ){ + im_error( FUNCTION_NAME, "image is not uncoded" ); + return -1; + } + if( 1 != in-> Bands ){ + im_error( FUNCTION_NAME, "image is multi-band" ); + return -1; + } + if( im_cp_desc( out, in ) ) + return -1; + + -- out-> Xsize; + out-> BandFmt= IM_BANDFMT_INT; /* do not change without updating im_gradcor() */ + out-> Bbits= IM_BBITS_INT; + + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return -1; + +#define RETURN_GENERATE( TYPE ) return im_generate( out, im_start_one, xgrad_gen_ ## TYPE, im_stop_one, (void*) in, NULL ) + + switch( in-> BandFmt ){ + + case IM_BANDFMT_UCHAR: + RETURN_GENERATE( guint8 ); + + case IM_BANDFMT_CHAR: + RETURN_GENERATE( gint8 ); + + case IM_BANDFMT_USHORT: + RETURN_GENERATE( guint16 ); + + case IM_BANDFMT_SHORT: + RETURN_GENERATE( gint16 ); + + case IM_BANDFMT_UINT: + RETURN_GENERATE( guint32 ); + + case IM_BANDFMT_INT: + RETURN_GENERATE( gint32 ); +#if 0 + case IM_BANDFMT_FLOAT: + RETURN_GENERATE( float ); + case IM_BANDFMT_DOUBLE: + RETURN_GENERATE( double ); +#endif +#undef RETURN_GENERATE + } +#undef FUNCTION_NAME +} + +int im_grad_y( IMAGE *in, IMAGE *out ){ +#define FUNCTION_NAME "im_grad_y" + + if( im_piocheck( in, out ) ) + return -1; + + if( ! im_isint( in ) ){ + im_error( FUNCTION_NAME, "image does not have integer band format" ); + return -1; + } + if( in-> Coding ){ + im_error( FUNCTION_NAME, "image is not uncoded" ); + return -1; + } + if( 1 != in-> Bands ){ + im_error( FUNCTION_NAME, "image is multi-band" ); + return -1; + } + if( im_cp_desc( out, in ) ) + return -1; + + -- out-> Ysize; + out-> BandFmt= IM_BANDFMT_INT; /* do not change without updating im_gradcor() */ + out-> Bbits= IM_BBITS_INT; + + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return -1; + +#define RETURN_GENERATE( TYPE ) return im_generate( out, im_start_one, ygrad_gen_ ## TYPE, im_stop_one, (void*) in, NULL ) + + switch( in-> BandFmt ){ + + case IM_BANDFMT_UCHAR: + RETURN_GENERATE( guint8 ); + + case IM_BANDFMT_CHAR: + RETURN_GENERATE( gint8 ); + + case IM_BANDFMT_USHORT: + RETURN_GENERATE( guint16 ); + + case IM_BANDFMT_SHORT: + RETURN_GENERATE( gint16 ); + + case IM_BANDFMT_UINT: + RETURN_GENERATE( guint32 ); + + case IM_BANDFMT_INT: + RETURN_GENERATE( gint32 ); +#if 0 + case IM_BANDFMT_FLOAT: + RETURN_GENERATE( float ); + case IM_BANDFMT_DOUBLE: + RETURN_GENERATE( double ); +#endif +#undef RETURN_GENERATE + } +#undef FUNCTION_NAME +} + + +/** LOCAL FUNCTION DEFINITIONS **/ + +static void *gradcor_start( IMAGE *out, void *vptr_large, void *unrequired ){ + + gradcor_seq_t *seq= IM_NEW( NULL, gradcor_seq_t ); + if( ! seq ) + return NULL; + + seq-> region_xgrad= (int*) NULL; + seq-> region_ygrad= (int*) NULL; + seq-> region_xgrad_area= 0; + seq-> region_ygrad_area= 0; + + seq-> reg= im_region_create( (IMAGE*) vptr_large ); + if( ! seq-> reg ){ + im_free( (void*) seq ); + return NULL; + } + return (void*) seq; +} + +static int gradcor_stop( void *vptr_seq, void *unrequired, void *unreq2 ){ + + gradcor_seq_t *seq= (gradcor_seq_t*) vptr_seq; + if( seq ){ + im_free( (void*) seq-> region_xgrad ); + im_free( (void*) seq-> region_ygrad ); + im_region_free( seq-> reg ); + seq-> region_xgrad= (int*) NULL; + seq-> region_ygrad= (int*) NULL; + seq-> reg= (REGION*) NULL; + im_free( (void*) seq ); + } + return 0; +} + +static int gradcor_gen( REGION *to_make, void *vptr_seq, void *unrequired, void *vptr_grads ){ + + gradcor_seq_t *seq= (gradcor_seq_t*) vptr_seq; + REGION *make_from= seq-> reg; + + IMAGE **grads= (IMAGE**) vptr_grads; + IMAGE *small_xgrad= grads[0]; + IMAGE *small_ygrad= grads[1]; + + Rect require= { + to_make-> valid. left, + to_make-> valid. top, + to_make-> valid. width + small_xgrad-> Xsize, + to_make-> valid. height + small_ygrad-> Ysize + }; + size_t region_xgrad_width= require. width - 1; + size_t region_ygrad_height= require. height - 1; + + if( im_prepare( make_from, &require ) ) + return -1; + +#define FILL_BUFFERS( TYPE ) /* fill region_xgrad */ \ + { \ + TYPE *reading= (TYPE*) IM_REGION_ADDR( make_from, require. left, require. top ); \ + size_t read_skip= ( IM_REGION_LSKIP( make_from ) / sizeof(TYPE) ) - region_xgrad_width; \ + size_t area_need= region_xgrad_width * require. height; \ + \ + if( seq-> region_xgrad_area < area_need ){ \ + free( seq-> region_xgrad ); \ + seq-> region_xgrad= malloc( area_need * sizeof(int) ); \ + if( ! seq-> region_xgrad ) \ + return -1; \ + seq-> region_xgrad_area= area_need; \ + } \ + { \ + int *writing= seq-> region_xgrad; \ + int *write_end= writing + area_need; \ + int *write_stop; \ + for( ; writing < write_end; reading+= read_skip ) \ + for( write_stop= writing + region_xgrad_width; writing < write_stop; ++reading, ++writing ) \ + *writing= reading[1] - reading[0]; \ + } \ + } \ + { /* fill region_ygrad */ \ + TYPE *reading= (TYPE*) IM_REGION_ADDR( make_from, require. left, require. top ); \ + size_t read_line= IM_REGION_LSKIP( make_from ) / sizeof(TYPE); \ + size_t read_skip= read_line - require. width; \ + size_t area_need= require. width * region_ygrad_height; \ + \ + if( seq-> region_ygrad_area < area_need ){ \ + free( seq-> region_ygrad ); \ + seq-> region_ygrad= malloc( area_need * sizeof(int) ); \ + if( ! seq-> region_ygrad ) \ + return -1; \ + seq-> region_ygrad_area= area_need; \ + } \ + { \ + int *writing= seq-> region_ygrad; \ + int *write_end= writing + area_need; \ + int *write_stop; \ + for( ; writing < write_end; reading+= read_skip ) \ + for( write_stop= writing + require. width; writing < write_stop; ++reading, ++writing ) \ + *writing= reading[ read_line ] - reading[0]; \ + } \ + } + switch( make_from-> im-> BandFmt ){ + case IM_BANDFMT_UCHAR: + FILL_BUFFERS( unsigned char ) + break; + case IM_BANDFMT_CHAR: + FILL_BUFFERS( signed char ) + break; + case IM_BANDFMT_USHORT: + FILL_BUFFERS( unsigned short int ) + break; + case IM_BANDFMT_SHORT: + FILL_BUFFERS( signed short int ) + break; + case IM_BANDFMT_UINT: + FILL_BUFFERS( unsigned int ) + break; + case IM_BANDFMT_INT: + FILL_BUFFERS( signed int ) + break; + } + { /* write to output */ + size_t write_skip= IM_REGION_LSKIP( to_make ) / sizeof( float ); + float *writing= (float*) IM_REGION_ADDR_TOPLEFT( to_make ); + float *write_end= writing + write_skip * to_make-> valid. height; + float *write_stop; + size_t write_width= to_make-> valid. width; + + size_t small_xgrad_width= small_xgrad-> Xsize; + size_t small_ygrad_width= small_ygrad-> Xsize; + int *small_xgrad_end= (int*) small_xgrad-> data + small_xgrad_width * small_xgrad-> Ysize; + int *small_ygrad_end= (int*) small_ygrad-> data + small_ygrad_width * small_ygrad-> Ysize; + + int *region_xgrad_start= seq-> region_xgrad; + int *region_ygrad_start= seq-> region_ygrad; + size_t region_xgrad_start_skip= region_xgrad_width - write_width; + size_t region_ygrad_start_skip= require. width - write_width; + + size_t region_xgrad_read_skip= region_xgrad_width - small_xgrad_width; + size_t region_ygrad_read_skip= require. width - small_ygrad_width; + + write_skip-= write_width; + + for( ; writing < write_end; writing+= write_skip, region_xgrad_start+= region_xgrad_start_skip, region_ygrad_start+= region_ygrad_start_skip ) + for( write_stop= writing + write_width; writing < write_stop; ++writing, ++region_xgrad_start, ++region_ygrad_start ){ + gint64 sum= 0; + { + int *small_xgrad_read= (int*) small_xgrad-> data; + int *small_xgrad_stop; + int *region_xgrad_read= region_xgrad_start; + + for( ; small_xgrad_read < small_xgrad_end; region_xgrad_read+= region_xgrad_read_skip ) + for( small_xgrad_stop= small_xgrad_read + small_xgrad_width; small_xgrad_read < small_xgrad_stop; ++small_xgrad_read, ++region_xgrad_read ) + sum+= *small_xgrad_read * *region_xgrad_read; + } + { + int *small_ygrad_read= (int*) small_ygrad-> data; + int *small_ygrad_stop; + int *region_ygrad_read= region_ygrad_start; + + for( ; small_ygrad_read < small_ygrad_end; region_ygrad_read+= region_ygrad_read_skip ) + for( small_ygrad_stop= small_ygrad_read + small_ygrad_width; small_ygrad_read < small_ygrad_stop; ++small_ygrad_read, ++region_ygrad_read ) + sum+= *small_ygrad_read * *region_ygrad_read; + } + *writing= sum; + } + } + return 0; +} + +#define XGRAD_GEN_DEFINITION( TYPE ) \ +static int xgrad_gen_ ## TYPE( REGION *to_make, void *vptr_make_from, void *unrequired, void *unreq2 ){ \ + \ + REGION *make_from= (REGION*) vptr_make_from; \ + Rect require= { \ + to_make-> valid. left, \ + to_make-> valid. top, \ + to_make-> valid. width + 1, \ + to_make-> valid. height \ + }; \ + if( im_prepare( make_from, &require ) ) \ + return -1; \ + \ + { \ + int *writing= (int*) IM_REGION_ADDR_TOPLEFT( to_make ); \ + size_t write_skip= IM_REGION_LSKIP( to_make ) / sizeof(int); \ + int *write_end= writing + write_skip * to_make-> valid. height; \ + size_t write_width= to_make-> valid. width; \ + int *write_stop; \ + \ + TYPE *reading= (TYPE*) IM_REGION_ADDR( make_from, require. left, require. top ); \ + size_t read_skip= ( IM_REGION_LSKIP( make_from ) / sizeof(TYPE) ) - write_width; \ + \ + write_skip-= write_width; \ + \ + for( ; writing < write_end; writing+= write_skip, reading+= read_skip ) \ + for( write_stop= writing + write_width; writing < write_stop; ++writing, ++reading ) \ + *writing= (int)( reading[1] - reading[0] ); \ + } \ + return 0; \ +} + +#define YGRAD_GEN_DEFINITION( TYPE ) \ +static int ygrad_gen_ ## TYPE( REGION *to_make, void *vptr_make_from, void *unrequired, void *unreq2 ){ \ + \ + REGION *make_from= (REGION*) vptr_make_from; \ + Rect require= { \ + to_make-> valid. left, \ + to_make-> valid. top, \ + to_make-> valid. width, \ + to_make-> valid. height + 1 \ + }; \ + if( im_prepare( make_from, &require ) ) \ + return -1; \ + \ + { \ + int *writing= (int*) IM_REGION_ADDR_TOPLEFT( to_make ); \ + size_t write_skip= IM_REGION_LSKIP( to_make ) / sizeof(int); \ + int *write_end= writing + write_skip * to_make-> valid. height; \ + size_t write_width= to_make-> valid. width; \ + int *write_stop; \ + \ + TYPE *reading= (TYPE*) IM_REGION_ADDR( make_from, require. left, require. top ); \ + size_t read_line= IM_REGION_LSKIP( make_from ) / sizeof(TYPE); \ + size_t read_skip= read_line - write_width; \ + \ + write_skip-= write_width; \ + \ + for( ; writing < write_end; writing+= write_skip, reading+= read_skip ) \ + for( write_stop= writing + write_width; writing < write_stop; ++writing, ++reading ) \ + *writing= (int)( reading[ read_line ] - reading[0] ); \ + } \ + return 0; \ +} + +XGRAD_GEN_DEFINITION( guint8 ) +YGRAD_GEN_DEFINITION( guint8 ) +XGRAD_GEN_DEFINITION( gint8 ) +YGRAD_GEN_DEFINITION( gint8 ) +XGRAD_GEN_DEFINITION( guint16 ) +YGRAD_GEN_DEFINITION( guint16 ) +XGRAD_GEN_DEFINITION( gint16 ) +YGRAD_GEN_DEFINITION( gint16 ) +XGRAD_GEN_DEFINITION( guint32 ) +YGRAD_GEN_DEFINITION( guint32 ) +XGRAD_GEN_DEFINITION( gint32 ) +YGRAD_GEN_DEFINITION( gint32 ) +#if 0 +XGRAD_GEN_DEFINITION( float ) +YGRAD_GEN_DEFINITION( float ) +XGRAD_GEN_DEFINITION( double ) +YGRAD_GEN_DEFINITION( double ) +#endif diff --git a/libsrc/convolution/im_logmasks.c b/libsrc/convolution/im_logmasks.c new file mode 100644 index 00000000..4aec46f6 --- /dev/null +++ b/libsrc/convolution/im_logmasks.c @@ -0,0 +1,220 @@ +/* @(#) Returns a circularly symmetric difference of Gaussian mask + * @(#) min_amplitude should be greater than 0.0 and less than 1.0 + * @(#) min_amplitude determines the size of the mask; if for instance + * @(#) the value .1 is entered this means that the produced mask is clipped + * @(#) at values less than 10 percent of the minimum negative amplitude. + * @(#) If the value of min_amplitude is too small, then the filter coefficients + * @(#) are calculated for masksize equal to the min of 8 * sigma or 256. + * @(#) The mask can be directly used with the vasari convolution programs, + * @(#) the default offset set is 0 + * @(#) + * @(#) DOUBLEMASK *im_log_dmask( filename, sigma, min_amplitude ) + * @(#) char *filename; + * @(#) double sigma, min_amplitude; + * @(#) + * @(#) Returns a laplacian of Gaussian square double mask or NULL on error + * @(#) + * @(#) DOUBLEMASK *im_log_imask( filename, sigma, min_amplitude ) + * @(#) char *filename; + * @(#) double sigma, min_amplitude; + * @(#) + * @(#) Returns a laplacian of Gaussian square int mask or NULL on error + */ + +/* Written on: 30/11/1989 + * Updated on: 6/12/1991 + * 7/8/96 JC + * - ansified, mem leaks plugged + * 20/11/98 JC + * - mask too large check added + * 26/3/02 JC + * - ahem, was broken since '96, thanks matt + * 16/7/03 JC + * - makes mask out to zero, not out to minimum, thanks again matt + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define PIM_RINT 1 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define IM_MAXMASK 256 + +DOUBLEMASK * +im_log_dmask( const char *filename, double sigma, double min_ampl ) +{ + const double sig2 = sigma * sigma; + + double last; + int x, y, k; + + double *pt1, *pt2, *pt3, *pt4; + int xm, ym; + int xm2, ym2; /* xm2 = xm/2 */ + int offset; + double *cf, *cfs, *mc; + DOUBLEMASK *m; + double sum; + + /* Stop used-before-set warnings. + */ + last = 0.0; + + /* Find the size of the mask depending on the entered data. We want to + * eval the mask out to the flat zero part, ie. beyond the minimum and + * to the point where it comes back up towards zero. + */ + for( x = 0; x < IM_MAXMASK; x++ ) { + const double distance = x * x; + double val; + + /* Handbook of Pattern Recognition and image processing + * by Young and Fu AP 1986 pp 220-221 + * temp = (1.0 / (2.0 * IM_PI * sig4)) * + (2.0 - (distance / sig2)) * + exp( (-1.0) * distance / (2.0 * sig2) ) + + .. use 0.5 to normalise + */ + val = 0.5 * + (2.0 - (distance / sig2)) * + exp( -distance / (2.0 * sig2) ); + + /* Stop when change in temp (ie. difference from the last + * point) and absolute value are both less than the min. + */ + if( x > 0 && + fabs( val ) < min_ampl && + fabs( val - last ) < min_ampl ) + break; + + last = val; + } + if( x == IM_MAXMASK ) { + im_errormsg( "im_log_dmask: mask too large" ); + return( NULL ); + } + + xm2 = x; ym2 = x; + xm = xm2 * 2 + 1; ym = ym2 * 2 + 1; + + if( !(cfs = IM_ARRAY( NULL, (xm2 + 1) * (ym2 + 1), double )) ) + return( NULL ); + + /* Make 1/4 of the mask. + */ + for( k = 0, y = 0; y <= ym2; y++ ) + for( x = 0; x <= xm2; x++, k++ ) { + const double distance = x * x + y * y; + + cfs[k] = 0.5 * + (2.0 - (distance / sig2)) * + exp( -distance / (2.0 * sig2) ); + } + +#ifdef PIM_RINT + for( k = 0, y = 0; y <= ym2; y++ ) { + for( x = 0; x <= xm2; x++, k++ ) + fprintf( stderr, "%3.2f ", cfs[k] ); + fprintf( stderr, "\n" ); + } +#endif + + if( !(m = im_create_dmask( filename, xm, ym )) ) { + im_free( cfs ); + return( NULL ); + } + + /* Copy the 1/4 cfs into the m + */ + cf = cfs; + offset = xm2 * (xm + 1); + mc = m->coeff + offset; + for( y = 0; y <= ym2; y++ ) { + for( x = 0; x <= xm2; x++ ) { + pt1 = mc + (y * xm) + x; + pt2 = mc - (y * xm) + x; + pt3 = mc + (y * xm) - x; + pt4 = mc - (y * xm) - x; + + *pt1 = cf[x]; + *pt2 = cf[x]; + *pt3 = cf[x]; + *pt4 = cf[x]; + } + + cf += (xm2 + 1); + } + im_free( cfs ); + + sum = 0.0; + for( k = 0, y = 0; y < m->ysize; y++ ) + for( x = 0; x < m->xsize; x++, k++ ) + sum += m->coeff[k]; + m->scale = sum; + m->offset = 0.0; + +#ifdef PIM_RINT + im_print_dmask( m ); +#endif + + return( m ); +} + +INTMASK * +im_log_imask( const char *filename, double sigma, double min_ampl ) +{ + DOUBLEMASK *dm; + INTMASK *im; + + if( !(dm = im_log_dmask( filename, sigma, min_ampl )) ) + return( NULL ); + + if( !(im = im_scale_dmask( dm, dm->filename )) ) { + im_free_dmask( dm ); + return( NULL ); + } + im_free_dmask( dm ); + + return( im ) ; +} diff --git a/libsrc/convolution/im_mpercent.c b/libsrc/convolution/im_mpercent.c new file mode 100644 index 00000000..0c6dc56e --- /dev/null +++ b/libsrc/convolution/im_mpercent.c @@ -0,0 +1,101 @@ +/* @(#) Function which returns an int. This integer is the threshold + * @(#) above which there are percent values of the input images + * @(#) If for instance precent=.1, the number of pels of the input image + * @(#) with values greater than the returned int will correspond to + * @(#) 10% of all pels of the image. + * @(#) One band IM_BANDFMT_UCHAR images only. + * @(#) + * @(#) Function im_mpercent() assumes that input + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) int im_percent(in, percent) + * @(#) IMAGE *in; + * @(#) double percent; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, N. Dessipris + * + * Author: N. Dessipris + * Written on: 02/08/1990 + * Modified on : 29/4/93 K.Martinez for Sys5 + * 20/2/95 JC + * - now returns result through parameter + * - ANSIfied a little + * 19/1/07 + * - redone with the vips hist operators + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What threshold will select a specified percentage of the pixels in the + * image. + */ +int +im_mpercent( IMAGE *in, double percent, int *out ) +{ + IMAGE *base; + IMAGE *t[6]; + double pos; + + if( !(base = im_open( "im_mpercent1", "p" )) ) + return( -1 ); + if( im_open_local_array( base, t, 6, "im_mpercent", "p" ) ) { + im_close( base ); + return( -1 ); + } + + if( im_histgr( in, t[0], -1 ) || + im_histcum( t[0], t[1] ) || + im_histnorm( t[1], t[2] ) || + im_lessconst( t[2], t[3], percent * t[2]->Xsize ) || + im_fliphor( t[3], t[4] ) || + im_profile( t[4], t[5], 1 ) || + im_avg( t[5], &pos ) ) { + im_close( base ); + return( -1 ); + } + im_close( base ); + + *out = pos; + + return( 0 ); +} diff --git a/libsrc/convolution/im_rank.c b/libsrc/convolution/im_rank.c new file mode 100644 index 00000000..7d81b033 --- /dev/null +++ b/libsrc/convolution/im_rank.c @@ -0,0 +1,424 @@ +/* @(#) Rank filter. + * @(#) + * @(#) int + * @(#) im_rank( in, out, xsize, ysize, order ) + * @(#) IMAGE *in, *out; + * @(#) int xsize, ysize; + * @(#) int order; + * @(#) + * @(#) Also: im_rank_raw(). As above, but does not add a black border. + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * Author: JC + * Written on: 19/8/96 + * Modified on: + * JC 20/8/96 + * - now uses insert-sort rather than bubble-sort + * - now works for any non-complex type + * JC 22/6/01 + * - oops, sanity check on n wrong + * JC 28/8/03 + * - cleanups + * - better selection algorithm ... same speed for 3x3, about 3x faster + * for 5x5, faster still for larger windows + * - index from zero for consistency with other parts of vips + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + * 7/10/04 + * - oops, im_embed() size was wrong + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Global state: save our parameters here. + */ +typedef struct { + IMAGE *in, *out; /* Images we run */ + int xsize, ysize; /* Window size */ + int order; /* Element select */ + int n; /* xsize * ysize */ +} RankInfo; + +/* Sequence value: just the array we sort in. + */ +typedef struct { + REGION *ir; + PEL *sort; +} SeqInfo; + +/* Free a sequence value. + */ +static int +stop_rank( SeqInfo *seq, IMAGE *in, RankInfo *rnk ) +{ + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Rank start function. + */ +static void * +start_rank( IMAGE *out, IMAGE *in, RankInfo *rnk ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->sort = NULL; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->sort = IM_ARRAY( out, + IM_IMAGE_SIZEOF_ELEMENT( in ) * rnk->n, PEL ); + if( !seq->ir || !seq->sort ) { + stop_rank( seq, in, rnk ); + return( NULL ); + } + + return( (void *) seq ); +} + +#define SWAP( TYPE, A, B ) { \ + TYPE t = (A); \ + (A) = (B); \ + (B) = t; \ +} + +/* Inner loop for select-sorting TYPE. + */ +#define LOOP_SELECT( TYPE ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + TYPE *sort = (TYPE *) seq->sort; \ + TYPE a; \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE *d = p + x; \ + \ + /* Copy window into sort[]. + */ \ + for( k = 0, j = 0; j < rnk->ysize; j++ ) { \ + for( i = 0; i < eaw; i += bands, k++ ) \ + sort[k] = d[i]; \ + d += ls; \ + } \ + \ + /* Rearrange sort[] to make the order-th element the order-th + * smallest, adapted from Numerical Recipes in C. + */ \ + lower = 0; /* Range we know the result lies in */ \ + upper = rnk->n - 1; \ + for(;;) { \ + if( upper - lower < 2 ) { \ + /* 1 or 2 elements left. + */ \ + if( upper - lower == 1 && \ + sort[lower] > sort[upper] ) \ + SWAP( TYPE, \ + sort[lower], sort[upper] ); \ + break; \ + } \ + else { \ + /* Pick mid-point of remaining elements. + */ \ + mid = (lower + upper) >> 1; \ + \ + /* Sort lower/mid/upper elements, hold + * midpoint in sort[lower + 1] for + * partitioning. + */ \ + SWAP( TYPE, sort[lower + 1], sort[mid] ); \ + if( sort[lower] > sort[upper] ) \ + SWAP( TYPE, \ + sort[lower], sort[upper] ); \ + if( sort[lower + 1] > sort[upper] ) \ + SWAP( TYPE, \ + sort[lower + 1], sort[upper] );\ + if( sort[lower] > sort[lower + 1] ) \ + SWAP( TYPE, \ + sort[lower], sort[lower + 1] ) \ + \ + i = lower + 1; \ + j = upper; \ + a = sort[lower + 1]; \ + \ + for(;;) { \ + /* Search for out of order elements. + */ \ + do \ + i++; \ + while( sort[i] < a ); \ + do \ + j--; \ + while( sort[j] > a ); \ + if( j < i ) \ + break; \ + SWAP( TYPE, sort[i], sort[j] ); \ + } \ + \ + /* Replace mid element. + */ \ + sort[lower + 1] = sort[j]; \ + sort[j] = a; \ + \ + /* Move to partition with the kth element. + */ \ + if( j >= rnk->order ) \ + upper = j - 1; \ + if( j <= rnk->order ) \ + lower = i; \ + } \ + } \ + \ + q[x] = sort[rnk->order]; \ + } \ +} + +/* Loop for find max of window. + */ +#define LOOP_MAX( TYPE ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE *d = &p[x]; \ + TYPE max; \ + \ + max = *d; \ + for( j = 0; j < rnk->ysize; j++ ) { \ + TYPE *e = d; \ + \ + for( i = 0; i < rnk->xsize; i++ ) { \ + if( *e > max ) \ + max = *e; \ + \ + e += bands; \ + } \ + \ + d += ls; \ + } \ + \ + q[x] = max; \ + } \ +} + +/* Loop for find min of window. + */ +#define LOOP_MIN( TYPE ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE *d = &p[x]; \ + TYPE min; \ + \ + min = *d; \ + for( j = 0; j < rnk->ysize; j++ ) { \ + TYPE *e = d; \ + \ + for( i = 0; i < rnk->xsize; i++ ) { \ + if( *e < min ) \ + min = *e; \ + \ + e += bands; \ + } \ + \ + d += ls; \ + } \ + \ + q[x] = min; \ + } \ +} + +#define SWITCH( OPERATION ) \ + switch( rnk->out->BandFmt ) { \ + case IM_BANDFMT_UCHAR: OPERATION( unsigned char ); break; \ + case IM_BANDFMT_CHAR: OPERATION( signed char ); break; \ + case IM_BANDFMT_USHORT: OPERATION( unsigned short ); break; \ + case IM_BANDFMT_SHORT: OPERATION( signed short ); break; \ + case IM_BANDFMT_UINT: OPERATION( unsigned int ); break; \ + case IM_BANDFMT_INT: OPERATION( signed int ); break; \ + case IM_BANDFMT_FLOAT: OPERATION( float ); break; \ + case IM_BANDFMT_DOUBLE: OPERATION( double ); break; \ + \ + default: \ + assert( 0 ); \ + } + +/* Rank of a REGION. + */ +static int +gen_rank( REGION *or, SeqInfo *seq, IMAGE *in, RankInfo *rnk ) +{ + REGION *ir = seq->ir; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int ls; + int bands = in->Bands; + int eaw = rnk->xsize * bands; /* elements across window */ + + int x, y; + int i, j, k; + int upper, lower, mid; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += rnk->xsize - 1; + s.height += rnk->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + ls = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( in ); + + for( y = to; y < bo; y++ ) { + if( rnk->order == 0 ) + SWITCH( LOOP_MIN ) + else if( rnk->order == rnk->n - 1 ) + SWITCH( LOOP_MAX ) + else + SWITCH( LOOP_SELECT ) } + + return( 0 ); +} + +/* Rank filter. + */ +int +im_rank_raw( IMAGE *in, IMAGE *out, int xsize, int ysize, int order ) +{ + RankInfo *rnk; + + /* Check parameters. + */ + if( !in || in->Coding != IM_CODING_NONE || im_iscomplex( in ) ) { + im_errormsg( "im_rank: input non-complex uncoded only" ); + return( -1 ); + } + if( xsize > 1000 || ysize > 1000 || xsize <= 0 || ysize <= 0 || + order < 0 || order > xsize * ysize - 1 ) { + im_errormsg( "im_rank: bad parameters" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Save parameters. + */ + if( !(rnk = IM_NEW( out, RankInfo )) ) + return( -1 ); + rnk->in = in; + rnk->out = out; + rnk->xsize = xsize; + rnk->ysize = ysize; + rnk->order = order; + rnk->n = xsize * ysize; + + /* Prepare output. Consider a 7x7 window and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= xsize - 1; + out->Ysize -= ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_rank: image too small for window" ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, start_rank, gen_rank, stop_rank, in, rnk ) ) + return( -1 ); + + out->Xoffset = -xsize / 2; + out->Yoffset = -ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_rank( IMAGE *in, IMAGE *out, int xsize, int ysize, int order ) +{ + IMAGE *t1 = im_open_local( out, "im_rank:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + xsize/2, ysize/2, + in->Xsize + xsize - 1, in->Ysize + ysize - 1 ) || + im_rank_raw( t1, out, xsize, ysize, order ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_rank_image.c b/libsrc/convolution/im_rank_image.c new file mode 100644 index 00000000..6ba67f6f --- /dev/null +++ b/libsrc/convolution/im_rank_image.c @@ -0,0 +1,327 @@ +/* @(#) Sort a set of images, pixelwise, and pick out the index at each point. + * @(#) + * @(#) int im_rank_image( imarray, imout, no, index ) + * @(#) IMAGE *imarray[], *imout; + * @(#) int no, index; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * 19/8/03 + * - from im_maxvalue(), via im_gbandjoin() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Parameters. + */ +typedef struct Rank { + IMAGE **in; /* Array of input images, NULL-terminated */ + IMAGE *out; + int n; /* Number of input images */ + int index; /* Pick out this one */ +} Rank; + +/* Make a Rank struct. + */ +static Rank * +rank_new( IMAGE **in, IMAGE *out, int n, int index ) +{ + int i; + Rank *rank; + + if( !(rank = IM_NEW( out, Rank )) ) + return( NULL ); + + rank->n = n; + rank->index = index; + rank->out = out; + if( !(rank->in = IM_ARRAY( out, n + 1, IMAGE * )) ) + return( NULL ); + for( i = 0; i < n; i++ ) + rank->in[i] = in[i]; + rank->in[n] = NULL; + + return( rank ); +} + +/* Our sequence value. + */ +typedef struct { + Rank *rank; + + REGION **ir; /* Input regions */ + PEL **pts; /* Per-input region data pointer */ + PEL *sort; /* Sort pixels here */ +} RankSequence; + +/* Free a sequence value. + */ +static int +stop_rank( RankSequence *seq, IMAGE **in, Rank *rank ) +{ + int i; + + for( i = 0; i < rank->n; i++ ) + if( seq->ir[i] ) { + im_region_free( seq->ir[i] ); + seq->ir[i] = NULL; + } + + return( 0 ); +} + +/* Make a sequence value. + */ +static void * +start_rank( IMAGE *out, IMAGE **in, Rank *rank ) +{ + RankSequence *seq; + int i; + + if( !(seq = IM_NEW( out, RankSequence )) ) + return( NULL ); + + /* Init! + */ + seq->rank = rank; + seq->ir = NULL; + seq->pts = NULL; + + /* Attach regions and arrays. + */ + seq->ir = IM_ARRAY( out, rank->n + 1, REGION * ); + seq->pts = IM_ARRAY( out, rank->n + 1, PEL * ); + seq->sort = IM_ARRAY( out, + rank->n * IM_IMAGE_SIZEOF_ELEMENT( in[0] ), PEL ); + if( !seq->ir || !seq->pts || !seq->sort ) { + stop_rank( seq, in, rank ); + return( NULL ); + } + + for( i = 0; i < rank->n; i++ ) + if( !(seq->ir[i] = im_region_create( in[i] )) ) { + stop_rank( seq, in, rank ); + return( NULL ); + } + seq->ir[i] = NULL; + + return( (void *) seq ); +} + +/* Special-case max and min (rather common). + */ +#define FIND_IM_MAX( TYPE ) { \ + for( x = 0; x < sz; x++ ) { \ + TYPE top = ((TYPE *) seq->pts[0])[x]; \ + \ + for( i = 1; i < rank->n; i++ ) { \ + TYPE v = ((TYPE *) seq->pts[i])[x]; \ + \ + if( v > top ) \ + top = v; \ + } \ + \ + ((TYPE *) q)[x] = top; \ + } \ +} + +#define FIND_IM_MIN( TYPE ) { \ + for( x = 0; x < sz; x++ ) { \ + TYPE bot = ((TYPE *) seq->pts[0])[x]; \ + \ + for( i = 1; i < rank->n; i++ ) { \ + TYPE v = ((TYPE *) seq->pts[i])[x]; \ + \ + if( v < bot ) \ + bot = v; \ + } \ + \ + ((TYPE *) q)[x] = bot; \ + } \ +} + +/* Inner loop for sorting. + */ +#define FIND_IM_RANK( TYPE ) { \ + TYPE *sort = (TYPE *) seq->sort; \ + \ + for( x = 0; x < sz; x++ ) { \ + for( i = 0; i < rank->n; i++ ) { \ + TYPE v = ((TYPE *) seq->pts[i])[x]; \ + \ + /* Search for element >v. + */\ + for( j = 0; j < i; j++ ) \ + if( sort[j] > v ) \ + break; \ + \ + /* Move remaining elements down. + */ \ + for( k = i; k > j; k-- ) \ + sort[k] = sort[k - 1]; \ + \ + /* Insert this element. + */ \ + sort[j] = v; \ + } \ + \ + ((TYPE *) q)[x] = sort[rank->index]; \ + } \ +} + +#define SWITCH( OPERATION ) \ + switch( rank->out->BandFmt ) { \ + case IM_BANDFMT_UCHAR: OPERATION( unsigned char ); break; \ + case IM_BANDFMT_CHAR: OPERATION( signed char ); break; \ + case IM_BANDFMT_USHORT: OPERATION( unsigned short ); break; \ + case IM_BANDFMT_SHORT: OPERATION( signed short ); break; \ + case IM_BANDFMT_UINT: OPERATION( unsigned int ); break; \ + case IM_BANDFMT_INT: OPERATION( signed int ); break; \ + case IM_BANDFMT_FLOAT: OPERATION( float ); break; \ + case IM_BANDFMT_DOUBLE: OPERATION( double ); break; \ + \ + default: \ + assert( 0 ); \ + } + +static int +find_rank( REGION *or, RankSequence *seq, IMAGE **in, Rank *rank ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int x, y, i, j, k; + + /* Prepare each input area. + */ + for( i = 0; i < rank->n; i++ ) + if( im_prepare( seq->ir[i], r ) ) + return( -1 ); + + /* Loop over output! + */ + for( y = to; y < bo; y++ ) { + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + for( i = 0; i < rank->n; i++ ) + seq->pts[i] = (PEL *) + IM_REGION_ADDR( seq->ir[i], le, y ); + + /* Special-case max and min. + */ + if( rank->index == 0 ) + SWITCH( FIND_IM_MIN ) + else if( rank->index == rank->n - 1 ) + SWITCH( FIND_IM_MAX ) + else + SWITCH( FIND_IM_RANK ) + } + + return( 0 ); +} + +/* pair-wise rank of a vector of image descriptors. + */ +int +im_rank_image( IMAGE **in, IMAGE *out, int n, int index ) +{ + int i; + Rank *rank; + + if( n < 1 ) { + im_errormsg( "im_rank_image: zero input images!" ); + return( -1 ); + } + if( index < 0 || index > n - 1 ) { + im_errormsg( "im_rank_image: " + "index should be in range 0 - %d", n - 1 ); + return( -1 ); + } + if( im_poutcheck( out ) ) + return( -1 ); + for( i = 0; i < n; i++ ) { + if( im_pincheck( in[i] ) ) + return( -1 ); + + if( in[i]->Coding != IM_CODING_NONE || im_iscomplex( in[i] ) ) { + im_errormsg( "im_rank_image: " + "uncoded non-complex only" ); + return( -1 ); + } + + if( in[0]->BandFmt != in[i]->BandFmt ) { + im_errormsg( "im_rank_image: " + "input images differ in format" ); + return( -1 ); + } + if( in[0]->Xsize != in[i]->Xsize || + in[0]->Ysize != in[i]->Ysize ) { + im_errormsg( "im_rank_image: " + "input images differ in size" ); + return( -1 ); + } + if( in[0]->Bands != in[i]->Bands ) { + im_errormsg( "im_rank_image: " + "input images differ in number of bands" ); + return( -1 ); + } + } + + if( !(rank = rank_new( in, out, n, index )) || + im_cp_desc_array( out, rank->in ) || + im_demand_hint_array( out, IM_THINSTRIP, rank->in ) || + im_generate( out, + start_rank, find_rank, stop_rank, rank->in, rank ) ) + return( -1 ); + + return( 0 ); +} + +int +im_maxvalue( IMAGE **in, IMAGE *out, int n ) +{ + return( im_rank_image( in, out, n, n - 1 ) ); +} diff --git a/libsrc/convolution/im_resize_linear.c b/libsrc/convolution/im_resize_linear.c new file mode 100644 index 00000000..fe371fd5 --- /dev/null +++ b/libsrc/convolution/im_resize_linear.c @@ -0,0 +1,194 @@ +/* im_lowpass() + * History: + * 27/10/94 JC + * - IM_ARRAY modified to use local allocation + * - im_iscomplex() call added + * 17/2/95 JC + * - modernised a little + * 18/8/95 JC + * - name changed to reflect function more closely + * 2/6/04 + * - was detecting edges incorrectly, segv for some images (thanks Javi) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What we do for each pel. + */ +#define LOOP( TYPE ) \ + if( Xint >= 0 && Yint >=0 && \ + Xint < in->Xsize-1 && Yint < in->Ysize-1 ) \ + for( bb = 0; bb < in->Bands; bb++ ) { \ + TYPE s1 = *((TYPE *) p); \ + TYPE s2 = *((TYPE *) (p + ips)); \ + TYPE s3 = *((TYPE *) (p + ils)); \ + TYPE s4 = *((TYPE *) (p + ips + ils)); \ + TYPE *t = (TYPE *) q; \ + \ + *t = (1-dx)*(1-dy)*s1 + dx*(1-dy)*s2 + \ + dy*(1-dx)*s3 + dx*dy*s4; \ + \ + p += ies; \ + q += oes; \ + } \ + else if( Xint == in->Xsize-1 && Yint >= 0 && Yint < in->Ysize - 1 ) \ + for( bb = 0; bb < in->Bands; bb++ ) { \ + TYPE s1 = *((TYPE *) p); \ + TYPE s3 = *((TYPE *) (p + ils)); \ + TYPE *t = (TYPE *) q; \ + \ + *t = (1-dy)*s1 + dy*s3; \ + \ + p += ies; \ + q += oes; \ + } \ + else if( Yint == in->Ysize-1 && Xint >= 0 && Xint < in->Xsize - 1 ) \ + for( bb = 0; bb < in->Bands; bb++ ) { \ + TYPE s1 = *((TYPE *) p); \ + TYPE s2 = *((TYPE *) (p + ips)); \ + TYPE *t = (TYPE *) q; \ + \ + *t = (1-dx)*s1 + dx*s2; \ + \ + p += ies; \ + q += oes; \ + } \ + else \ + for( bb = 0; bb < in->Bands; bb++ ) { \ + unsigned char s1 = *((unsigned char *) p); \ + TYPE *t = (TYPE *) q; \ + \ + *t = s1; \ + \ + p += ies; \ + q += oes; \ + } + +int +im_resize_linear( IMAGE *in, IMAGE *out, int X, int Y ) +{ + double dx, dy, xscale, yscale; + double Xnew, Ynew; /* inv. coord. of the interpolated pt */ + + int x, y; + int Xint, Yint; + int bb; + + PEL *input, *opline; + PEL *q, *p; + + int ils, ips, ies; /* Input and output line, pel and */ + int ols, ops, oes; /* element sizes */ + + if( im_iocheck( in, out ) ) + return( -1 ); + if( im_iscomplex( in ) ) { + im_errormsg( "im_lowpass: non-complex input only" ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) { + im_errormsg("im_lowpass: input should be uncoded"); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + out->Xsize = X; + out->Ysize = Y; + + if( im_setupout( out ) ) + return( -1 ); + + ils = IM_IMAGE_SIZEOF_LINE( in ); + ips = IM_IMAGE_SIZEOF_PEL( in ); + ies = IM_IMAGE_SIZEOF_ELEMENT( in ); + + ols = IM_IMAGE_SIZEOF_LINE( out ); + ops = IM_IMAGE_SIZEOF_PEL( out ); + oes = IM_IMAGE_SIZEOF_ELEMENT( out ); + +/* buffer lines +***************/ + if( !(opline = IM_ARRAY( out, ols, PEL )) ) + return( -1 ); + +/* Resampling +*************/ + input = (PEL*) in->data; + xscale = ((double)in->Xsize-1)/(X-1); + yscale = ((double)in->Ysize-1)/(Y-1); + +for (y=0; yBandFmt ) { + case IM_BANDFMT_UCHAR: LOOP( unsigned char); break; + case IM_BANDFMT_USHORT: LOOP( unsigned short ); break; + case IM_BANDFMT_UINT: LOOP( unsigned int ); break; + case IM_BANDFMT_CHAR: LOOP( signed char ); break; + case IM_BANDFMT_SHORT: LOOP( signed short ); break; + case IM_BANDFMT_INT: LOOP( signed int ); break; + case IM_BANDFMT_FLOAT: LOOP( float ); break; + case IM_BANDFMT_DOUBLE: LOOP( double ); break; + + default: + im_errormsg( "im_lowpass: unsupported image type" ); + return( -1 ); + /*NOTREACHED*/ + } + } + + if (im_writeline(y, out, opline) ) + return(-1); + } +return(0); +} diff --git a/libsrc/convolution/im_sharpen.c b/libsrc/convolution/im_sharpen.c new file mode 100644 index 00000000..e30fb7c3 --- /dev/null +++ b/libsrc/convolution/im_sharpen.c @@ -0,0 +1,308 @@ +/* Cored sharpen of LABQ image. + * + * Usage: + * + * int im_sharpen( IMAGE *in, IMAGE *out, + * int mask_size, + * int x1, int x2, + * double m1, double m2 ) + * + * Returns 0 on success and -1 on error + * + * Copyright: 1995 A. Abbood + * Author: A. Abbood + * Written on: 30/01/1995 + * 15/5/95 JC + * - updated for latest 7.3 mods + * - m3 parameter removed + * - bug fixes and speed-ups + * 4/7/95 JC + * - x3 parameter added + * - xs are now double + * 6/7/95 JC + * - xs are now ys + * - better LUT generation + * 12/3/01 JC + * - uses seperable convolution for umask + * - tiny clean ups + * 23/7/01 JC + * - fix for band extract index changed + * 21/4/04 + * - switched to gaussian mask and radius + * 20/11/04 + * - uses extract_bands() to remove and reattach ab for slight speedup + * - accepts LabS as well as LabQ for slight speedup + * - small code tidies + * - ~15% speed up in total + * 29/11/06 + * - convolve first to help region sharing + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* A lut --- we need indexes in the range [-x3,x2], so add x3 to indexes + * before starting to index table. + */ +typedef struct { + int *lut; /* Start of lut */ + int x1, x2, x3; /* Parameters scaled up to int */ +} SharpenLut; + +/* Make a lut. + */ +static SharpenLut * +build_lut( IMAGE *out, int x1, int x2, int x3, double m1, double m2 ) +{ + int i; + SharpenLut *slut = IM_NEW( out, SharpenLut ); + + if( !slut ) + return( NULL ); + + if( !(slut->lut = IM_ARRAY( out, x2 + x3 + 1, int )) ) + return( NULL ); + slut->x1 = x1; + slut->x2 = x2; + slut->x3 = x3; + + for( i = 0; i < x1; i++ ) { + slut->lut[x3 + i] = i*m1; + slut->lut[x3 - i] = -i*m1; + } + for( i = x1; i <= x2; i++ ) + slut->lut[x3 + i] = x1*m1 + (i-x1)*m2; + for( i = x1; i <= x3; i++ ) + slut->lut[x3 - i] = -(x1*m1 + (i-x1)*m2); + + return( slut ); +} + +/* Take the difference of in1 and in2 and LUT it. + */ +static void +buf_difflut( short **in, short *out, int n, SharpenLut *slut ) +{ + int range = slut->x2 + slut->x3; + int *lut = slut->lut; + int x3 = slut->x3; + short *p1 = in[1]; + short *p2 = in[0]; + int i; + + for( i = 0; i < n; i++ ) { + int v1 = p1[i]; + int v2 = p2[i]; + + /* v2 is the area average. If this is zero, then we pass the + * original image through unaltered. + */ + if( v2 == 0 ) + out[i] = v1; + else { + /* Find difference. Offset by x3 to get the expected + * range of values. + */ + int s1 = x3 + (v1 - v2); + int s2; + + /* Clip to LUT range. + */ + if( s1 < 0 ) + s1 = 0; + else if( s1 > range ) + s1 = range; + + /* Transform! + */ + s2 = v1 + lut[s1]; + + /* Clip to LabS range. + */ + if( s2 < 0 ) + s2 = 0; + else if( s2 > 32767 ) + s2 = 32767; + + /* And write. + */ + out[i] = s2; + } + } +} + +/* Make a 1 line gaussian of a specified radius. + */ +static INTMASK * +sharpen_mask_new( int radius ) +{ + INTMASK *base; + INTMASK *line; + int total; + int i; + + /* Stop at 20% of max ... bit mean, but means mask radius is roughly + * right. + */ + if( !(base = im_gauss_imask( "big1", radius / 2, 0.2 )) ) + return( NULL ); + + if( !(line = im_create_imask( "sharpen-line", base->xsize, 1 )) ) { + im_free_imask( base ); + return( NULL ); + } + + total = 0; + for( i = 0; i < base->xsize; i++ ) { + line->coeff[i] = + base->coeff[base->xsize * (base->ysize / 2) + i]; + total += line->coeff[i]; + } + line->scale = total; + + im_free_imask( base ); + +#ifdef DEBUG + printf( "sharpen_mask_new: created mask:\n" ); + im_print_imask( line ); +#endif /*DEBUG*/ + + return( line ); +} + +int +im_sharpen( IMAGE *in, IMAGE *out, + int mask_size, + double x1, double y2, double y3, + double m1, double m2 ) +{ + IMAGE *arry[3]; + IMAGE *t[4]; + INTMASK *mask; + SharpenLut *slut; + + /* Turn y parameters into xs. + */ + double x2 = (y2 - x1 * (m1 - m2)) / m2; + double x3 = (y3 - x1 * (m1 - m2)) / m2; + + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *tc[2]; + + if( im_open_local_array( out, tc, 2, "im_sharpen:1", "p" ) || + im_LabQ2LabS( in, tc[0] ) || + im_sharpen( tc[0], tc[1], + mask_size, x1, y2, y3, m1, m2 ) || + im_LabS2LabQ( tc[1], out ) ) + return( -1 ); + + return( 0 ); + } + + /* Check IMAGE parameters + */ + if( in->Coding != IM_CODING_NONE || + in->Bands != 3 || + in->BandFmt != IM_BANDFMT_SHORT ) { + im_error( "im_sharpen", _( "input not 3-band short" ) ); + return( -1 ); + } + + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Check number range. + */ + if( x1 < 0 || x2 < 0 || x1 > 99 || x2 > 99 || x1 > x2 || + x3 < 0 || x3 > 99 || x1 > x3 ) { + im_error( "im_sharpen", _( "parameters out of range" ) ); + return( -1 ); + } + + /* Set up data structures we need. First, the convolution mask we will + * use. + */ + if( !(mask = (INTMASK *) im_local( out, + (im_construct_fn) sharpen_mask_new, + (im_callback_fn) im_free_imask, + GINT_TO_POINTER( mask_size ), NULL, NULL )) ) + return( -1 ); + + /* Make the lut we will use. We need to scale up x1, x2, x3 to the + * LabS range. + */ + if( !(slut = build_lut( out, + x1 * 327.67, x2 * 327.67, x3 * 327.67, m1, m2 )) ) + return( -1 ); + + /* Open a set of local image descriptors. + */ + if( im_open_local_array( out, t, 4, "im_sharpen:2", "p" ) ) + return( -1 ); + + /* Extract L and ab, convolve L. + */ + if( im_extract_band( in, t[0], 0 ) || + im_extract_bands( in, t[1], 1, 2 ) || + im_convsep( t[0], t[2], mask ) ) + return( -1 ); + + /* Find difference of L channel and convolved L channel, and pass + * through LUT. + */ + if( im_cp_desc( t[3], t[2] ) ) + return( -1 ); + arry[0] = t[2]; arry[1] = t[0]; arry[2] = NULL; + if( im_wrapmany( arry, t[3], + (im_wrapmany_fn) buf_difflut, slut, NULL ) ) + return( -1 ); + + /* Reattach ab. + */ + if( im_bandjoin( t[3], t[1], out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_shrink.c b/libsrc/convolution/im_shrink.c new file mode 100644 index 00000000..2d39a5e8 --- /dev/null +++ b/libsrc/convolution/im_shrink.c @@ -0,0 +1,313 @@ +/* @(#) Shrink any non-complex image by some x, y, factor. No interpolation! + * @(#) Just average an area. Suitable for making quicklooks only! + * @(#) + * @(#) int + * @(#) im_shrink( in, out, xshrink, yshrink ) + * @(#) IMAGE *in, *out; + * @(#) double xshrink, yshrink; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Authors: Nicos Dessipris and Kirk Martinez + * Written on: 29/04/1991 + * Modified on: 2/11/92, 22/2/93 Kirk Martinez - Xres Yres & cleanup + incredibly inefficient for box filters as LUTs are used instead of + + Needs converting to a smoother filter: eg Gaussian! KM + * 15/7/93 JC + * - rewritten for partial v2 + * - ANSIfied + * - now shrinks any non-complex type + * - no longer cloned from im_convsub() + * - could be much better! see km comments above + * 3/8/93 JC + * - rounding bug fixed + * 11/1/94 JC + * - problems with .000001 and round up/down ignored! Try shrink 3738 + * pixel image by 9.345000000001 + * 7/10/94 JC + * - IM_NEW and IM_ARRAY added + * - more typedef + * 3/7/95 JC + * - IM_CODING_LABQ handling added here + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our main parameter struct. + */ +typedef struct { + double xshrink; /* Shrink factors */ + double yshrink; + int mw; /* Size of area we average */ + int mh; + int np; /* Number of pels we average */ +} ShrinkInfo; + +/* Our per-sequence parameter struct. We hold an offset for each pel we + * average. + */ +typedef struct { + REGION *ir; + int *off; +} SeqInfo; + +/* Free a sequence value. + */ +static int +shrink_stop( SeqInfo *seq, IMAGE *in, ShrinkInfo *st ) +{ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Make a sequence value. + */ +static void * +shrink_start( IMAGE *out, IMAGE *in, ShrinkInfo *st ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->off = NULL; + seq->ir = im_region_create( in ); + seq->off = IM_ARRAY( out, st->np, int ); + if( !seq->off || !seq->ir ) { + shrink_stop( seq, in, st ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* Integer shrink. + */ +#define ishrink(TYPE) \ + for( y = to; y < bo; y++ ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = le; x < ri; x++ ) { \ + int ix = x * st->xshrink; \ + int iy = y * st->yshrink; \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, ix, iy ); \ + \ + for( b = 0; b < ir->im->Bands; b++ ) { \ + int sum = 0; \ + int *t = seq->off; \ + \ + for( z = 0; z < st->np; z++ ) \ + sum += p[*t++]; \ + \ + *q++ = sum / st->np; \ + p++; \ + } \ + } \ + } + +/* FP shrink. + */ +#define fshrink(TYPE) \ + for( y = to; y < bo; y++ ) { \ + TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = le; x < ri; x++ ) { \ + int ix = x * st->xshrink; \ + int iy = y * st->yshrink; \ + TYPE *p = (TYPE *) IM_REGION_ADDR( ir, ix, iy ); \ + \ + for( b = 0; b < ir->im->Bands; b++ ) { \ + double sum = 0; \ + int *t = seq->off; \ + \ + for( z = 0; z < st->np; z++ ) \ + sum += p[*t++]; \ + \ + *q++ = sum / st->np; \ + p++; \ + } \ + } \ + } + +/* Shrink a REGION. + */ +static int +shrink_gen( REGION *or, SeqInfo *seq, IMAGE *in, ShrinkInfo *st ) +{ + REGION *ir = seq->ir; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int ri = IM_RECT_RIGHT( r ); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + int x, y, z, b; + + /* What part of the input image do we need? Very careful: round left + * down, round right up. + */ + s.left = r->left * st->xshrink; + s.top = r->top * st->yshrink; + s.width = ceil( IM_RECT_RIGHT( r ) * st->xshrink ) - s.left; + s.height = ceil( IM_RECT_BOTTOM( r ) * st->yshrink ) - s.top; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Init offsets for pel addressing. Note that offsets must be for the + * type we will address the memory array with. + */ + for( z = 0, y = 0; y < st->mh; y++ ) + for( x = 0; x < st->mw; x++ ) + seq->off[z++] = (IM_REGION_ADDR( ir, x, y ) - IM_REGION_ADDR( ir, 0, 0 )) / + IM_IMAGE_SIZEOF_ELEMENT( ir->im ); + + switch( ir->im->BandFmt ) { + case IM_BANDFMT_UCHAR: ishrink(unsigned char); break; + case IM_BANDFMT_CHAR: ishrink(char); break; + case IM_BANDFMT_USHORT: ishrink(unsigned short); break; + case IM_BANDFMT_SHORT: ishrink(short); break; + case IM_BANDFMT_UINT: ishrink(unsigned int); break; + case IM_BANDFMT_INT: ishrink(int); break; + case IM_BANDFMT_FLOAT: fshrink(float); break; + case IM_BANDFMT_DOUBLE: fshrink(double); break; + + default: + im_errormsg( "im_shrink: unsupported input format" ); + return( -1 ); + } + + return( 0 ); +} + +static int +shrink( IMAGE *in, IMAGE *out, double xshrink, double yshrink ) +{ + ShrinkInfo *st; + + /* Check parameters. + */ + if( !in || im_iscomplex( in ) ) { + im_errormsg( "im_shrink: non-complex input only" ); + return( -1 ); + } + if( xshrink < 1.0 || yshrink < 1.0 ) { + im_errormsg( "im_shrink: shrink factors should both be >1" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Prepare output. Note: we round the output width down! + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = in->Xsize / xshrink; + out->Ysize = in->Ysize / yshrink; + out->Xres = in->Xres / xshrink; + out->Yres = in->Yres / yshrink; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_shrink: image has shrunk to nothing" ); + return( -1 ); + } + + /* Build and attach state struct. + */ + if( !(st = IM_NEW( out, ShrinkInfo )) ) + return( -1 ); + st->xshrink = xshrink; + st->yshrink = yshrink; + st->mw = ceil( xshrink ); + st->mh = ceil( yshrink ); + st->np = st->mw * st->mh; + + /* Set demand hints. We want THINSTRIP, as we will be demanding a + * large area of input for each output line. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + shrink_start, shrink_gen, shrink_stop, in, st ) ) + return( -1 ); + + return( 0 ); +} + +/* Wrap up the above: do IM_CODING_LABQ as well. + */ +int +im_shrink( IMAGE *in, IMAGE *out, double xshrink, double yshrink ) +{ + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "im_shrink:1", "p" ) || + im_LabQ2LabS( in, t[0] ) || + shrink( t[0], t[1], xshrink, yshrink ) || + im_LabS2LabQ( t[1], out ) ) + return( -1 ); + } + else if( in->Coding == IM_CODING_NONE ) { + if( shrink( in, out, xshrink, yshrink ) ) + return( -1 ); + } + else { + im_errormsg( "im_shrink: unknown coding type" ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/convolution/im_spcor.c b/libsrc/convolution/im_spcor.c new file mode 100644 index 00000000..f54e415d --- /dev/null +++ b/libsrc/convolution/im_spcor.c @@ -0,0 +1,550 @@ +/* @(#) Functions which calculates the correlation coefficient between two + * @(#) images. + * @(#) + * @(#) int im_spcor( IMAGE *in, IMAGE *ref, IMAGE *out ) + * @(#) + * @(#) We calculate: + * @(#) + * @(#) sumij (ref(i,j)-mean(ref))(inkl(i,j)-mean(inkl)) + * @(#) c(k,l) = ------------------------------------------------ + * @(#) sqrt(sumij (ref(i,j)-mean(ref))^2) * + * @(#) sqrt(sumij (inkl(i,j)-mean(inkl))^2) + * @(#) + * @(#) where inkl is the area of in centred at position (k,l). + * @(#) + * @(#) Writes float to out. in and ref must be 1 band uchar, or 1 band + * @(#) ushort. + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : + * 20/2/95 JC + * - updated + * - ANSIfied, a little + * 21/2/95 JC + * - rewritten + * - partialed + * - speed-ups + * - new correlation coefficient (see above), from Niblack "An + * Introduction to Digital Image Processing,", Prentice/Hall, pp 138. + * 4/9/97 JC + * - now does short/ushort as well + * 13/2/03 JC + * - oops, could segv for short images + * 14/4/04 JC + * - sets Xoffset / Yoffset + * 8/3/06 JC + * - use im_embed() with edge stretching on the input, not the output + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold global stuff here. + */ +typedef struct { + IMAGE *ref; /* Image we are searching for */ + double rmean; /* Mean of search window */ + double c1; /* sqrt(sumij (ref(i,j)-mean(ref))^2) */ +} SpcorInfo; + +typedef struct { + + REGION *f; + int *f_cols; + size_t max_cols; + +} spcor2_seq; + +typedef struct { + + IMAGE *w; + gint64 area; + double recip_area; + double mean; + double n_var; + +} spcor2_w_inf; + +static spcor2_seq *spcor2_start( IMAGE *r, IMAGE *f ); +static int spcor2_gen( REGION *r, spcor2_seq *seq, void *unrequired, spcor2_w_inf *w_inf ); +static int spcor2_stop( spcor2_seq *seq ); + +#define LOOP(IN) \ +{ \ + IN *a = (IN *) p; \ + IN *b = (IN *) ref->data; \ + int in_lsk = lsk / sizeof( IN ); \ + IN *a1, *b1; \ + \ + /* For each pel in or, loop over ref. First, \ + * calculate mean of area in ir corresponding to ref. \ + */ \ + for( a1 = a, sum1 = 0, j = 0; j < ref->Ysize; j++, a1 += in_lsk ) \ + for( i = 0; i < ref->Xsize; i++ ) \ + sum1 += a1[i]; \ + imean = (double) sum1 / (ref->Xsize * ref->Ysize); \ + \ + /* Loop over ir again, this time calculating \ + * sum-of-squares-of-differences for this window on \ + * ir, and also sum-of-products-of-differences. \ + */ \ + for( a1 = a, b1 = b, sum2 = 0.0, sum3 = 0.0, j = 0; \ + j < ref->Ysize; j++, a1 += in_lsk, b1 += ref->Xsize ) { \ + for( i = 0; i < ref->Xsize; i++ ) { \ + /* Reference pel, and input pel. \ + */ \ + IN rp = b1[i]; \ + IN ip = a1[i]; \ + \ + /* Accumulate sum-of-squares-of- \ + * differences for input image. \ + */ \ + double t = ip - imean; \ + sum2 += t * t; \ + \ + /* Accumulate product-of-differences. \ + */ \ + sum3 += (rp - inf->rmean) * (ip - imean); \ + } \ + } \ +} + +/* spcor generate function. + */ +static int +spcor_gen( REGION *or, REGION *ir, IMAGE *in, SpcorInfo *inf ) +{ + IMAGE *ref = inf->ref; + Rect irect; + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ri = IM_RECT_RIGHT(r); + + int x, y, i, j; + int lsk; + + double imean; + double sum1; + double sum2, sum3; + double c2, cc; + + /* What part of ir do we need? + */ + irect.left = or->valid.left; + irect.top = or->valid.top; + irect.width = or->valid.width + ref->Xsize - 1; + irect.height = or->valid.height + ref->Ysize - 1; + + if( im_prepare( ir, &irect ) ) + return( -1 ); + lsk = IM_REGION_LSKIP( ir ); + + /* Loop over or. + */ + for( y = to; y < bo; y++ ) { + float *q = (float *) IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, x, y ); + + /* Find sums for this position. + */ + switch( ref->BandFmt ) { + case IM_BANDFMT_UCHAR: LOOP(unsigned char); break; + case IM_BANDFMT_USHORT: LOOP(unsigned short); break; + case IM_BANDFMT_SHORT: LOOP(signed short); break; + default: + error_exit( "im_spcor: internal error #7934" ); + + /* Keep gcc -Wall happy. + */ + return( -1 ); + } + + /* Now: calculate correlation coefficient! + */ + c2 = sqrt( sum2 ); + cc = sum3 / (inf->c1 * c2); + + *q++ = cc; + } + } + + return( 0 ); +} + +/* Pre-calculate stuff for our reference image. + */ +static SpcorInfo * +make_inf( IMAGE *out, IMAGE *ref ) +{ + SpcorInfo *inf = IM_NEW( out, SpcorInfo ); + int sz = ref->Xsize * ref->Ysize; + PEL *p = (PEL *) ref->data; + double s; + int i; + + if( !inf ) + return( NULL ); + + /* Pre-calculate stuff on our reference image. + */ + inf->ref = ref; + if( im_avg( inf->ref, &inf->rmean ) ) + return( NULL ); + + /* Find sqrt-of-sum-of-squares-of-differences. + */ + for( s = 0.0, i = 0; i < sz; i++ ) { + double t = (int) p[i] - inf->rmean; + s += t * t; + } + inf->c1 = sqrt( s ); + + return( inf ); +} + +int +im_spcor_raw( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + SpcorInfo *inf; + + /* PIO between in and out; WIO from ref. + */ + if( im_piocheck( in, out ) || im_incheck( ref ) ) + return( -1 ); + + /* Check sizes. + */ + if( in->Xsize < ref->Xsize || in->Ysize < ref->Ysize ) { + im_errormsg( "im_spcor_raw: ref not smaller than in" ); + return( -1 ); + } + + /* Check types. + */ + if( in->Coding != IM_CODING_NONE || in->Bands != 1 || + ref->Coding != IM_CODING_NONE || ref->Bands != 1 || + in->BandFmt != ref->BandFmt ) { + im_errormsg( "im_spcor_raw: input not uncoded 1 band" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR && + in->BandFmt != IM_BANDFMT_SHORT && + in->BandFmt != IM_BANDFMT_USHORT ) { + im_errormsg( "im_spcor_raw: input not char/short/ushort" ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_descv( out, in, ref, NULL ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Xsize = in->Xsize - ref->Xsize + 1; + out->Ysize = in->Ysize - ref->Ysize + 1; + + /* Pre-calculate some stuff. + */ + if( !(inf = make_inf( out, ref )) ) + return( -1 ); + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Write the correlation. + */ + if( im_generate( out, + im_start_one, spcor_gen, im_stop_one, in, inf ) ) + return( -1 ); + + out->Xoffset = -ref->Xsize / 2; + out->Yoffset = -ref->Ysize / 2; + + return( 0 ); +} + +/* The above, with the input expanded to make out the same size as in. + */ +int +im_spcor( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_spcor intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + ref->Xsize / 2, ref->Ysize / 2, + in->Xsize + ref->Xsize - 1, + in->Ysize + ref->Ysize - 1 ) || + im_spcor_raw( t1, ref, out ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} + +int +im_spcor2_raw( + IMAGE *f, + IMAGE *w, + IMAGE *r +){ +#define FUNCTION_NAME "im_spcor_raw" + + DOUBLEMASK *w_stats; + spcor2_w_inf *w_inf; + + if( im_piocheck( f, r ) || im_incheck( w ) ) + return -1; + + if( f-> Xsize < w-> Xsize || f-> Ysize < w-> Ysize ){ + im_error( FUNCTION_NAME, "window must be smaller than search area" ); + return -1; + } + if( f-> Coding || w-> Coding ){ + im_error( FUNCTION_NAME, "uncoded images only" ); + return -1; + } + if( 1 != f-> Bands || 1 != w-> Bands ){ + im_error( FUNCTION_NAME, "single band images only" ); + return -1; + } + if( !( IM_BANDFMT_UCHAR == f-> BandFmt + || IM_BANDFMT_CHAR == f-> BandFmt + || IM_BANDFMT_USHORT == f-> BandFmt + || IM_BANDFMT_SHORT == f-> BandFmt ) ){ + im_error( FUNCTION_NAME, "short or char images only" ); + return -1; + } + if( f-> BandFmt != w-> BandFmt ){ + im_error( FUNCTION_NAME, "band formats must match" ); + return -1; + } + if( im_cp_descv( r, f, w, NULL ) ) + return -1; + + r-> Xsize-= ( w-> Xsize - 1 ); + r-> Ysize-= ( w-> Ysize - 1 ); + r-> BandFmt= IM_BANDFMT_FLOAT; + r-> Bbits= IM_BBITS_FLOAT; + r-> Xoffset= - w-> Xsize / 2; + r-> Yoffset= - w-> Ysize / 2; + + if( im_demand_hint( r, IM_FATSTRIP, f, NULL ) ) + return -1; + + w_inf= IM_NEW( r, spcor2_w_inf ); + w_stats= im_stats( w ); + + if( ! w_inf || ! w_stats ) + return -1; + + w_inf-> w= w; + w_inf-> area= w-> Xsize * w-> Ysize; + w_inf-> recip_area= 1.0 / (double) w_inf-> area; + w_inf-> mean= w_stats-> coeff[ 4 ]; + w_inf-> n_var= w_stats-> coeff[ 3 ] - w_stats-> coeff[ 2 ] * w_stats-> coeff[ 2 ] * w_inf-> recip_area; + + im_free_dmask( w_stats ); + + return im_generate( r, (void*)spcor2_start, spcor2_gen, spcor2_stop, f, w_inf ); + +#undef FUNCTION_NAME +} + +static spcor2_seq * +spcor2_start( IMAGE *r, IMAGE *f ){ + + REGION *reg= im_region_create( f ); + spcor2_seq *seq; + + if( ! reg ) + return NULL; + + seq= IM_NEW( NULL, spcor2_seq ); + if( ! seq ) + return NULL; + + seq-> f= reg; + seq-> f_cols= NULL; + seq-> max_cols= 0; + + return seq; +} + +static int +spcor2_gen( + REGION *r, + spcor2_seq *seq, + void *unrequired, + spcor2_w_inf *w_inf +){ + + Rect need= { + r-> valid. left, + r-> valid. top, + r-> valid. width + w_inf-> w-> Xsize - 1, + r-> valid. height + w_inf-> w-> Ysize - 1 + }; + int j; + float *r_data= (float*) IM_REGION_ADDR( r, r-> valid. left, r-> valid. top ); + size_t r_skip= IM_REGION_LSKIP( r ) / sizeof( float ); + float *r_end= r_data + r-> valid. height * r_skip; + + r_skip-= r-> valid. width; + + if( im_prepare( seq-> f, & need ) ) + return -1; + + if( need. width > seq-> max_cols ){ + im_free( seq-> f_cols ); + + seq-> f_cols= IM_ARRAY( NULL, need. width + 1, int ); /* one spare for the last right move */ + if( ! seq-> f_cols ) + return -1; + + seq-> max_cols= need. width; + } + memset( seq-> f_cols, 0, seq-> max_cols * sizeof( int ) ); + +#define LOOPS(TYPE) { \ + TYPE *f_start= (TYPE*) IM_REGION_ADDR( seq-> f, need. left, need. top ); \ + size_t f_skip= IM_REGION_LSKIP( seq-> f ) / sizeof( TYPE ); \ + size_t f_row_skip= f_skip - r-> valid. width; \ + size_t f_win_skip= f_skip - w_inf-> w-> Xsize; \ + \ + TYPE *f_win_end= f_start; \ + TYPE *f_stop= f_win_end + f_skip * w_inf-> w-> Ysize; \ + \ + for( ; f_win_end < f_stop; f_win_end+= f_skip ) \ + for( j= 0; j < need. width; ++j ) \ + seq-> f_cols[ j ]+= f_win_end[ j ]; \ + \ + for( ; r_data < r_end; r_data+= r_skip, f_start+= f_row_skip, f_win_end+= f_skip ){ \ + double f_mean= 0.0; \ + \ + for( j= 0; j < w_inf-> w-> Xsize; ++j ) \ + f_mean+= seq-> f_cols[ j ]; \ + \ + f_mean*= w_inf-> recip_area; \ + \ + for( j= 0; j < r-> valid. width; ++f_start, ++r_data, \ + f_mean+= ( seq-> f_cols[ w_inf-> w-> Xsize + j ] - seq-> f_cols[ j ] ) * w_inf-> recip_area, \ + ++j ){ \ + \ + double num_sum= 0.0; \ + double den_sum= 0.0; \ + TYPE *w_data= (TYPE*) w_inf-> w-> data; \ + TYPE *w_end= w_data + w_inf-> area; \ + TYPE *w_stop; \ + TYPE *f_data= f_start; \ + \ + for( ; w_data < w_end; f_data+= f_win_skip ) \ + for( w_stop= w_data + w_inf-> w-> Xsize; w_data < w_stop; ++w_data, ++f_data ){ \ + \ + double f_term= *f_data - f_mean; \ + \ + num_sum+= f_term * ( *w_data - w_inf-> mean ); \ + den_sum+= f_term * f_term; \ + } \ + \ + *r_data= num_sum * pow( den_sum * w_inf-> n_var, -0.5 ); \ + } \ + \ + if( r_data + r_skip < r_end ) \ + for( j= 0; j < need. width; ++j ) \ + seq-> f_cols[ j ]+= f_win_end[ j ] - f_start[ j ]; \ + } \ + } + + switch( w_inf-> w-> BandFmt ){ + case IM_BANDFMT_UCHAR: LOOPS( guint8 ) break; + case IM_BANDFMT_CHAR: LOOPS( gint8 ) break; + case IM_BANDFMT_USHORT: LOOPS( guint16 ) break; + case IM_BANDFMT_SHORT: LOOPS( gint16 ) break; + } + +#undef LOOPS + return 0; +} + +static int +spcor2_stop( spcor2_seq *seq ){ + + im_region_free( seq-> f ); + im_free( seq-> f_cols ); + im_free( seq ); + + return 0; +} + +int +im_spcor2( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_spcor2 intermediate", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + ref->Xsize / 2, ref->Ysize / 2, + in->Xsize + ref->Xsize - 1, + in->Ysize + ref->Ysize - 1 ) || + im_spcor2_raw( t1, ref, out ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/convolution/im_stretch3.c b/libsrc/convolution/im_stretch3.c new file mode 100644 index 00000000..3eec8f93 --- /dev/null +++ b/libsrc/convolution/im_stretch3.c @@ -0,0 +1,323 @@ +/* Function to stretch an image by 3%, and displace in x and y. Cubic + * interpolation with a seperable mask. Displacements are: + * + * 0 <= xdisp < 1.0. + * 0 <= ydisp < 1.0. + * + * Each horizontal block of 33 pixels is stretched to 34. + * + * Written by Ahmed Abbood + * August-1994 + * + * Any unsigned short image. Output image is 3 pixels smaller because of + * convolution, but x is larger by 3%: + * + * out->Xsize = 34*(in->Xsize / 33) + in->Xsize%33 - 3; + * out->Ysize = in->Ysize - 3; + * + * 20/10/95 JC + * - was not freeing regions correctly + * - tidied up + * 29/3/96 JC + * - completely rewritten ... now produces correct result, and is 2x + * faster + * 18/9/97 JC + * - added to VIPS library as im_stretch3 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Data for the cubic interpolation function. + */ +typedef struct { + IMAGE *in; + double dx, dy; + + int xoff, yoff; /* Mask we start with for this disp. */ + int mask[34][4]; /* Fixed-point masks for each output pixel */ +} StretchInfo; + +/* Per-thread info. + */ +typedef struct seq_info { + StretchInfo *sin; + REGION *ir; + unsigned short *buf; + int lsk; +} SeqInfo; + +static int +stop_stretch( SeqInfo *seq ) +{ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +static void * +start_stretch( IMAGE *out, IMAGE *in, StretchInfo *sin ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + + if( !seq ) + return( NULL ); + + seq->sin = sin; + seq->ir = im_region_create( in ); + seq->lsk = IM_IMAGE_N_ELEMENTS( out ); + seq->buf = IM_ARRAY( out, 4*seq->lsk, unsigned short ); + + if( !seq->buf || !seq->ir ) { + stop_stretch( seq ); + return( NULL ); + } + + return( (void *)seq ); +} + +/* Stretch a line of pels into a line in the buffer. + */ +static void +make_xline( StretchInfo *sin, + unsigned short *p, unsigned short *q, int w, int m ) +{ + int bands = sin->in->Bands; + int tot; + int x, b; + + /* Offsets for subsequent pixels. + */ + int o1 = 1*bands; + int o2 = 2*bands; + int o3 = 3*bands; + + for( x = 0; x < w; x++ ) { + int *mask = &sin->mask[m][0]; + unsigned short *p1 = p; + + /* Loop for this pel. + */ + for( b = 0; b < bands; b++ ) { + tot = p1[0]*mask[0] + p1[o1]*mask[1] + + p1[o2]*mask[2] + p1[o3]*mask[3]; + tot = IM_MAX( 0, tot ); + p1++; + *q++ = (tot + 16384) >> 15; + } + + /* Move to next mask. + */ + m++; + if( m == 34 ) + /* Back to mask 0, reuse this input pel. + */ + m = 0; + else + /* Move to next input pel. + */ + p += bands; + } +} + +/* As above, but do the vertical resample. lsk is how much we add to move down + * a line in p, boff is [0,1,2,3] for which buffer line is mask[3]. + */ +static void +make_yline( StretchInfo *sin, int lsk, int boff, + unsigned short *p, unsigned short *q, int w, int m ) +{ + int bands = sin->in->Bands; + int we = w * bands; + int *mask = &sin->mask[m][0]; + int tot; + int x; + + /* Offsets for subsequent pixels. Down a line each time. + */ + int o0 = lsk*boff; + int o1 = lsk*((boff + 1) % 4); + int o2 = lsk*((boff + 2) % 4); + int o3 = lsk*((boff + 3) % 4); + + for( x = 0; x < we; x++ ) { + tot = p[o0]*mask[0] + p[o1]*mask[1] + + p[o2]*mask[2] + p[o3]*mask[3]; + tot = IM_MAX( 0, tot ); + p++; + *q++ = (tot + 16384) >> 15; + } +} + +static int +stretch_gen( REGION *or, SeqInfo *seq, IMAGE *in, StretchInfo *sin ) +{ + REGION *ir = seq->ir; + Rect *r = &or->valid; + Rect r1; + int x, y; + + /* What mask do we start with? + */ + int xstart = (r->left + sin->xoff) % 34; + + /* What part of input do we need for this output? + */ + r1.left = r->left - (r->left + sin->xoff) / 34; + r1.top = r->top; + x = IM_RECT_RIGHT( r ); + x = x - (x + sin->xoff) / 34 + 3; + r1.width = x - r1.left; + r1.height = r->height + 3; + if( im_prepare( ir, &r1 ) ) + return( -1 ); + + /* Fill the first three lines of the buffer. + */ + for( y = 0; y < 3; y++ ) { + unsigned short *p = (unsigned short *) + IM_REGION_ADDR( ir, r1.left, y + r1.top ); + unsigned short *q = seq->buf + seq->lsk*y; + + make_xline( sin, p, q, r->width, xstart ); + } + + /* Loop for subsequent lines: stretch a new line of x pels, and + * interpolate a line of output from the 3 previous xes plus this new + * one. + */ + for( y = 0; y < r->height; y++ ) { + /* Next line of fresh input pels. + */ + unsigned short *p = (unsigned short *) + IM_REGION_ADDR( ir, r1.left, y + r1.top + 3 ); + + /* Next line we fill in the buffer. + */ + int boff = (y + 3)%4; + unsigned short *q = seq->buf + boff*seq->lsk; + + /* Line we write in output. + */ + unsigned short *q1 = (unsigned short *) + IM_REGION_ADDR( or, r->left, y + r->top ); + + /* Process this new xline. + */ + make_xline( sin, p, q, r->width, xstart ); + + /* Generate new output line. + */ + make_yline( sin, seq->lsk, boff, + seq->buf, q1, r->width, sin->yoff ); + } + + return( 0 ); +} + +int +im_stretch3( IMAGE *in, IMAGE *out, double dx, double dy ) +{ + StretchInfo *sin; + int i; + + /* Check our args. + */ + if( in->Coding != IM_CODING_NONE || in->BandFmt != IM_BANDFMT_USHORT ) { + im_errormsg( "im_stretch3: not uncoded unsigned short" ); + return( -1 ); + } + if( dx < 0 || dx >= 1.0 || dy < 0 || dy >= 1.0 ) { + im_errormsg( "im_stretch3: displacements out of range [0,1)" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Prepare the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = 34*(in->Xsize / 33) + in->Xsize%33 - 3; + out->Ysize = in->Ysize - 3; + + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + if( !(sin = IM_NEW( out, StretchInfo )) ) + return( -1 ); + + /* Save parameters. + */ + sin->in = in; + sin->dx = dx; + sin->dy = dy; + + /* Generate masks. + */ + for( i = 0; i < 34; i++ ) { + double d = (34.0 - i)/34.0; + + double y0 = 2.0*d*d - d - d*d*d; + double y1 = 1.0 - 2.0*d*d + d*d*d; + double y2 = d + d*d - d*d*d; + double y3 = -d*d + d*d*d; + + sin->mask[i][0] = IM_RINT( y0 * 32768 ); + sin->mask[i][1] = IM_RINT( y1 * 32768 ); + sin->mask[i][2] = IM_RINT( y2 * 32768 ); + sin->mask[i][3] = IM_RINT( y3 * 32768 ); + } + + /* Which mask do we start with to apply these offsets? + */ + sin->xoff = (dx * 33.0) + 0.5; + sin->yoff = (dy * 33.0) + 0.5; + + if( im_generate( out, + start_stretch, stretch_gen, stop_stretch, in, sin ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/im_zerox.c b/libsrc/convolution/im_zerox.c new file mode 100644 index 00000000..a76adbcb --- /dev/null +++ b/libsrc/convolution/im_zerox.c @@ -0,0 +1,178 @@ +/* @(#) Functions which detects the +ve and -ve edges of + * @(#) zero crossings of an image depending on the flag + * @(#) Function im_zerox() assumes that the imin file is an integer image + * @(#) either memory mapped or in a buffer. + * @(#) The output image is byte with + * @(#) zero crossing set to 255 and all othre values set to zero + * @(#) + * @(#) int im_zerox(pimin, pimout, flag) + * @(#) IMAGE *pimin, *pimout; + * @(#) int flag; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : + * 1/2/95 JC + * - rewritten for PIO + * - some bugs removed + * 11/5/06 + * - small clean ups + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define loop( TYPE ) \ + for( i = 0; i < ne; i++ ) { \ + TYPE p1 = ((TYPE *)p)[i]; \ + TYPE p2 = ((TYPE *)p)[i + b]; \ + \ + if( flag == 1 && p1 > 0 && p2 <= 0 ) \ + q[i] = 255; \ + else if( flag == -1 && p1 < 0 && p2 >= 0 ) \ + q[i] = 255; \ + else \ + q[i] = 0; \ + } + +/* Zerox generate function. + */ +static int +zerox_gen( REGION *or, REGION *ir, IMAGE *in, int flag ) +{ + Rect irect; + Rect *r = &or->valid; + + /* Range of pixels we loop over. + */ + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM( r ); + int b = in->Bands; + int ne = b * r->width; + + int i, y; + + /* We need to be able to see one pixel to the right. + */ + irect.top = r->top; + irect.left = r->left; + irect.width = r->width + 1; + irect.height = r->height; + if( im_prepare( ir, &irect ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: loop( signed char ); break; + case IM_BANDFMT_SHORT: loop( signed short ); break; + case IM_BANDFMT_INT: loop( signed int ); break; + case IM_BANDFMT_FLOAT: loop( float ); break; + case IM_BANDFMT_DOUBLE: loop( double ); break; + + default: + error_exit( "im_zerox: internal error" ); + /*NOTREACHED*/ + } + } + + return( 0 ); +} + +int +im_zerox( IMAGE *in, IMAGE *out, int flag ) +{ + IMAGE *t1 = im_open_local( out, "im_zerox#1" , "p" ); + + if( !t1 ) + return( -1 ); + if( flag != -1 && flag != 1 ) { + im_error( "im_zerox", _( "flag not -1 ot 1" ) ); + return( -1 ); + } + if( im_piocheck( in, t1 ) ) + return( -1 ); + if( im_iscomplex( in ) || in->Coding != IM_CODING_NONE ) { + im_error( "im_zerox", _( "non-complex uncoded only" ) ); + return( -1 ); + } + if( in->Xsize < 2 ) { + im_error( "im_zerox", _( "image too narrow" ) ); + return( -1 ); + } + if( im_isuint( in ) ) + /* Unsigned type, therefore there will be no zero-crossings. + */ + return( im_black( out, in->Xsize, in->Ysize, in->Bands ) ); + + /* Force output to be BYTE. Output is narrower than input by 1 pixel. + */ + if( im_cp_desc( t1, in ) ) + return( -1 ); + t1->Bbits = IM_BBITS_BYTE; + t1->BandFmt = IM_BANDFMT_UCHAR; + t1->Xsize -= 1; + + /* Set hints - THINSTRIP is ok with us. + */ + if( im_demand_hint( t1, IM_THINSTRIP, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( im_generate( t1, im_start_one, zerox_gen, im_stop_one, + in, GINT_TO_POINTER( flag ) ) ) + return( -1 ); + + /* Now embed it in a larger image. + */ + if( im_embed( t1, out, 0, 0, 0, in->Xsize, in->Ysize ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/convolution/man3/Makefile.am b/libsrc/convolution/man3/Makefile.am new file mode 100644 index 00000000..d66f0ec7 --- /dev/null +++ b/libsrc/convolution/man3/Makefile.am @@ -0,0 +1,58 @@ +man_MANS = \ + im_addgnoise.3 \ + im_compass.3 \ + im_conv.3 \ + im_conv_raw.3 \ + im_convf.3 \ + im_convf_raw.3 \ + im_convsep.3 \ + im_convsep_raw.3 \ + im_convsepf.3 \ + im_convsepf_raw.3 \ + im_convsub.3 \ + im_contrast_surface.3 \ + im_contrast_surface_raw.3 \ + im_create_dmask.3 \ + im_create_imask.3 \ + im_dup_dmask.3 \ + im_dup_imask.3 \ + im_embed.3 \ + im_fastcor.3 \ + im_free_dmask.3 \ + im_free_imask.3 \ + im_gauss_dmask.3 \ + im_gauss_imask.3 \ + im_gaussnoise.3 \ + im_gradient.3 \ + im_lindetect.3 \ + im_log_dmask.3 \ + im_log_imask.3 \ + im_lowpass.3 \ + im_maxvalue.3 \ + im_mpercent.3 \ + im_offsets45.3 \ + im_offsets90.3 \ + im_print_dmask.3 \ + im_print_imask.3 \ + im_rank.3 \ + im_rank_image.3 \ + im_read_dmask.3 \ + im_norm_dmask.3 \ + im_read_imask.3 \ + im_rotate_dmask45.3 \ + im_rotate_dmask90.3 \ + im_rotate_imask45.3 \ + im_rotate_imask90.3 \ + im_scale_dmask.3 \ + im_sharpen.3 \ + im_shrink.3 \ + im_spcor.3 \ + im_stretch3.3 \ + im_write_dmask.3 \ + im_write_dmask_name.3 \ + im_write_imask.3 \ + im_write_imask_name.3 \ + im_zerox.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/convolution/man3/im_addgnoise.3 b/libsrc/convolution/man3/im_addgnoise.3 new file mode 100644 index 00000000..f41ed39f --- /dev/null +++ b/libsrc/convolution/man3/im_addgnoise.3 @@ -0,0 +1,27 @@ +.TH IM_ADDGNOISE 3 "14 May 1991" +.SH NAME +im_addgnoise \- add gaussian noise to an image +.SH SYNOPSIS +#include + +int im_addgnoise(in, out, sigma) +.br +IMAGE *in, *out; +.br +double sigma; + +.SH DESCRIPTION +im_addgnoise() adds gaussian noise with mean 0 and standard deviation sigma to +the image held by the image descriptor in and writes the result on the image +descriptor out. The function works on any non-complex input image. +Input can have any no of bands. The noise is generated by adding +12 random numbers. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_add(3), im_lintra(3), im_multiply(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 25/04/1991 diff --git a/libsrc/convolution/man3/im_compass.3 b/libsrc/convolution/man3/im_compass.3 new file mode 100644 index 00000000..5679d2bc --- /dev/null +++ b/libsrc/convolution/man3/im_compass.3 @@ -0,0 +1,106 @@ +.TH IM_COMPASS 3 "2 May 1991" +.SH NAME +im_compass, im_rank_image, im_maxvalue, im_lindetect, im_gradient \- extract features from an image with +a rotating input mask +.SH SYNOPSIS +#include + +int im_rank_image(in, out, n, index) +.br +IMAGE **in; +.br +IMAGE *out; +.br +int n; +.br +int index; + +int im_maxvalue(in, out, n) +.br +IMAGE **in; +.br +IMAGE *out; +.br +int n; + +int im_compass(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_lindetect(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_gradient(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; +.SH DESCRIPTION +.B im_rank_image() +sorts the input images pixel-wise, then outputs an image in which each pixel +is selected from the sorted list by the +.B index +parameter. For example, if +.B index +is zero, then each output pixel will be the minimum of all the corresponding +input pixels. +It works for any uncoded, non-complex +image type. All input images must match in size, format, and number of bands. + +These functions convolve the image pointed by the image descriptor in with the +mask pointed by mask and put the result in the image pointed by out. + +The mask structure INTMASK is returned by the function im_read_imask(3), for +integer mask only. Input should be unsigned char; processing is carried out +using look-up tables. The output is integer. coefficients. The size of the +output image and the number of channels are the same as the corresponding of +the input. + +If the sizes of the mask are xm and ym and the sizes of the image are xs and ys, +then there is a black border at the output image as follows: + +The top ym/2 lines black, bottom ys-ym/2-ym lines black; each of the remaining +lines has the initial xm/2 pels blank, and the final +xs-xm/2-xm pels black (division over 2 is integer division). +The output at each point is divided by scale and then the offset is added. +Both offset and scale are held in mask. + +.B im_maxvalue() +is a convenience function: it is simply: + + im_rank_image( in, out, n, n - 1 ) + +.B im_compass() +convolves each point of the input byte image pointed by in with 8 masks. +The first mask is the entered mask and the seven remaining masks are produced +by successive rotations of the entered mask by 45 degrees. The maximum output +of all masks at each point is written to the output integer IMAGE descriptor. +The function expects as input a square mask of odd size. + +.B im_lindetect() +convolves each point of the input byte image pointed by in with 4 masks. +The first mask is the entered mask and the three remaining masks are produced +by successive rotations of the entered mask by 45 degrees. The maximum output +of all masks at each point is written to the output integer IMAGE descriptor. +The function expects as input a square mask of odd size. + +.B im_gradient() +convolves each point of the input byte image pointed by in with 2 masks; +the entered mask and with a 90 degrees rotation of the +entered mask. If g1, g2 are the result of each convolution then for each point +abs(g1) + abs(g2) is written to the output integer IMAGE descriptor. +The function expects as input a square mask of any size. +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_readmask(3), im_conv(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 02/05/1991 diff --git a/libsrc/convolution/man3/im_contrast_surface.3 b/libsrc/convolution/man3/im_contrast_surface.3 new file mode 100644 index 00000000..20d8fe8b --- /dev/null +++ b/libsrc/convolution/man3/im_contrast_surface.3 @@ -0,0 +1,40 @@ +.TH IM_CONTRAST_SURFACE 3 "01 May 2006" +.SH NAME + im_contrast_surface, im_contrast_surface_raw \- Generate contrast surface +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "int im_contrast_surface( IMAGE " "*in" ", IMAGE " "*out" ", int " "half_win_size" ", int " "spacing" " ); +.br + +.BI "int im_contrast_surface_raw( IMAGE " "*in" ", IMAGE " "*out" ", int " "half_win_size" ", int " "spacing" " ); +.fi +.SH DESCRIPTION +These functions generate an image where the value of each pixel represents the +contrast within a square window of size 2 * half_win_size + 1 centred on the +corresponsing point in the input image. +.PP +The output is sub-sampled by a factor of spacing. +.PP +Input must be single-band uncoded uchar, WIO or PIO. +.PP +Output is single-band uncoded uint, WIO or PIO. +.PP +In +.BR im_contrast_surface(3) , +pixels beyond the edges of the input image are considered to be have the value +of the nearest pixel which is in the image. +.PP +Alternatively, in +.BR im_contrast_surface_raw(3) , +pixels within half_win_size of the edge are not calculated, and output is +smaller accordingly. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH COPYRIGHT +.br +Copyright 2006, The Nottingham Trent University. +.SH AUTHOR +Tom Vajzovic + diff --git a/libsrc/convolution/man3/im_contrast_surface_raw.3 b/libsrc/convolution/man3/im_contrast_surface_raw.3 new file mode 100644 index 00000000..bf67695a --- /dev/null +++ b/libsrc/convolution/man3/im_contrast_surface_raw.3 @@ -0,0 +1 @@ +.so man3/im_contrast_surface.3 diff --git a/libsrc/convolution/man3/im_conv.3 b/libsrc/convolution/man3/im_conv.3 new file mode 100644 index 00000000..02839b24 --- /dev/null +++ b/libsrc/convolution/man3/im_conv.3 @@ -0,0 +1,149 @@ +.TH CONVOLUTION 3 "2 May 1991" +.SH NAME +im_conv, im_conv_raw, im_convf, im_convf_raw, +im_convsep, im_convsep_raw, im_convsepf, im_convsepf_raw, +im_convsub, im_shrink \- convolves an image with a generalised mask +.SH SYNOPSIS +#include + +int im_conv(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_conv_raw(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convf(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convf_raw(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsep(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convsep_raw(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convsepf(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsepf_raw(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsub(in, out, mask, xskip, yskip) +.br +IMAGE *in, *out; +.br +INTMASK *mask; +.br +int xskip, yskip; + +int im_shrink(in, out, xfactor, yfactor) +.br +IMAGE *in, *out; +.br +double xfactor, yfactor; + +.SH DESCRIPTION +These functions +convolve the image +.B in +with the matrix +.B mask +and put the result in the image +.B out. + +Input should be non-complex. The size and type of the output image are +the same as the size of the input. To output a larger type (for example, +to output an int image from convolution of a byte image, avoiding clipping), +cast the input image up with +.B im_clip2fmt(3). + +The output image is the same size as the input. The edge pixels are calculated +by expanding the input image using +.B im_embed(3) +in mode 1 (replicating edge pixels) just enough so that the output can match +the input. + +The output at each point is divided by scale and then the offset is added. +Both offset and scale are held in mask. + +The +.B im_conv*(3) +functions have 'raw' versions which do not add the border: instead the +output image is smaller than the input. + +.B im_conv(3) +and +.B im_conv_raw(3) +convolve any non-complex input image to make an output image of the same +type. Rounding is appropriate to the image type. + +.B im_convf(3) +and +.B im_convf_raw(3) +convolves to float (double if the input is double). +The function expects a double mask. + +.B im_convsep(3) +and +.B im_convsep_raw(3) +carry out convolution using an 1xN or Nx1 separable mask. + +The function scales the output result by dividing it with scale*scale. The +scale factor should therefore be the sqrt of the scale of the square NxN mask. + +Rounding is appropriate to the image type. It works on any non-complex image, +and writes the output in the same format as the input. +The function expects integer mask. + +.B im_convsepf(3) +and +.B im_convsepf_raw(3) +convolves to float (double if the input is double). +The function expects a double mask. + +.B im_convsub(3) +convolves the byte image pointed by in and writes the result as a byte output. +Using this function the input image is subsampled on +both directions by an integer factor of xskip horizontally and +an integer factor of yskip vertically. +During the covolution, values are rounded before division. +Both input and output are bytes. Output is clipped between 0 and 255. +The function expects an integer mask. + +.B im_shrink(3) +shrink the input image file by xfactor along the horizontal and +yfactor along the vertical direction. The function does not perform subpixel +interpolation and therefore the resultant image can present aliasing especially +for small x and y factors. Any size image, any non-complex type, any number of +bands. +.SH RETURN VALUE +The functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_read_imask(3), im_compass(3), im_fastcor(3), im_log_dmask(3), +im_clip2fmt(3) diff --git a/libsrc/convolution/man3/im_conv_raw.3 b/libsrc/convolution/man3/im_conv_raw.3 new file mode 100644 index 00000000..da5db97a --- /dev/null +++ b/libsrc/convolution/man3/im_conv_raw.3 @@ -0,0 +1,123 @@ +.TH CONVOLUTION 3 "2 May 1991" +.SH NAME +im_conv, im_convf, im_convsep, im_convsepf, +im_convsub, im_shrink \- convolves an image with a generalised +mask +.SH SYNOPSIS +#include + +int im_conv(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convf(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsep(in, out, mask) +.br +IMAGE *in, *out; +.br +INTMASK *mask; + +int im_convsepf(in, out, mask) +.br +IMAGE *in, *out; +.br +DOUBLEMASK *mask; + +int im_convsub(in, out, mask, xskip, yskip) +.br +IMAGE *in, *out; +.br +INTMASK *mask; +.br +int xskip, yskip; + +int im_shrink(in, out, xfactor, yfactor) +.br +IMAGE *in, *out; +.br +double xfactor, yfactor; + +.SH DESCRIPTION +These functions +convolve the image pointed by the image descriptor in with the file +pointed by mask and puts the result in the image pointed by out. +The mask structures INTMASK and DOUBLEMASK are +returned by the function im_read_imask(3) or im_read_dmask(3), +for integer and double masks respectively. + +Input should be non-complex. +The size and type of the output image are the same as the size of the input. +To +output a larger type (for example, to output an int image from convolution +of a byte image, avoiding clipping), cast the input image up with one of +the im_clip2*() functions. + +If the sizes of the mask are xm and ym and the sizes of the image are xs and ys, +then there is a black border at the output image as follows: + +The top ym/2 lines black, bottom ys-ym/2-ym lines black; each of the remaining +lines has the initial xm/2 pels blank, and the final +xs-xm/2-xm pels (division over 2 is integer division). + +The output at each point is divided by scale and then the offset is added. +Both offset and scale are held in mask. + +.B im_conv() +and +.B im_convsep() +have 'raw' versions which do not add the black border: instead the output +image is smaller than the input. + +.B im_conv() +and +.B im_conv_raw() +convolve any non-complex input image to make an output image of the same +type. Rounding is appropriate to the image type. + +.B im_convf() +convolves to float (double if the input is double). +The function expects a double mask. + +.B im_convsep() +and +.B im_convsep_raw() +carry out convolution using an 1xN or Nx1 separable mask. + +The function +scales the output result by dividing it with scale*scale. The scale +factor should therefore be the sqrt of the scale of the square NxN mask. + +Rounding is appropriate to the image type. It works on any non-complex image, +and writes the output in the same format as the input. +The function expects integer mask. + +.B im_convsepf() +convolves to float (double if the input is double). +The function expects a double mask. + +.B im_convsub() +convolves the byte image pointed by in and writes the result as a byte output. +Using this function the input image is subsampled on +both directions by an integer factor of xskip horizontally and +an integer factor of yskip vertically. +During the covolution, values are rounded before division. +Both input and output are bytes. Output is clipped between 0 and 255. +The function expects an integer mask. + +.B im_shrink() +shrink the input image file by xfactor along the horizontal and +yfactor along the vertical direction. The function doesnot perform subpixel +interpolation and therefore the resultant image can present aliasing especially +for small x and y factors. Any size image, any non-complex type, any number of +bands. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_read_imask(3), im_compass(3), im_fastcor(3). diff --git a/libsrc/convolution/man3/im_convf.3 b/libsrc/convolution/man3/im_convf.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convf.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convf_raw.3 b/libsrc/convolution/man3/im_convf_raw.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convf_raw.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsep.3 b/libsrc/convolution/man3/im_convsep.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsep.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsep_raw.3 b/libsrc/convolution/man3/im_convsep_raw.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsep_raw.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsepf.3 b/libsrc/convolution/man3/im_convsepf.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsepf.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsepf_raw.3 b/libsrc/convolution/man3/im_convsepf_raw.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsepf_raw.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_convsub.3 b/libsrc/convolution/man3/im_convsub.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_convsub.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_create_dmask.3 b/libsrc/convolution/man3/im_create_dmask.3 new file mode 100644 index 00000000..df8ebcfd --- /dev/null +++ b/libsrc/convolution/man3/im_create_dmask.3 @@ -0,0 +1,224 @@ +.TH IM_READMASK 3 "2 May 1991" +.SH NAME +im_create_dmask, im_create_imask, im_create_dmaskv, im_create_imaskv, +im_dup_dmask, im_dup_imask, im_free_dmask, +im_free_imask, im_print_imask, im_print_dmask, im_read_dmask, im_read_imask, +im_scale_dmask, im_write_dmask, im_write_imask \- operations on double or int +masks +.SH SYNOPSIS +#include + +DOUBLEMASK *im_create_dmask( name, xs, ys ) +.br +char *name; +.br +int xs, ys; + +INTMASK *im_create_imask( name, xs, ys ) +.br +char *name; +.br +int xs, ys; + +DOUBLEMASK *im_create_dmaskv( name, xs, ys, ... ) +.br +char *name; +.br +int xs, ys; + +INTMASK *im_create_imaskv( name, xs, ys, ... ) +.br +char *name; +.br +int xs, ys; + +DOUBLEMASK *im_dup_dmask( m, name ) +.br +DOUBLEMASK *m; +.br +char *name; + +INTMASK *im_dup_imask( m, name ) +.br +INTMASK *m; +.br +char *name; + +void im_free_dmask( mask ) +.br +DOUBLEMASK *mask; + +void im_free_imask( mask ) +.br +INTMASK *mask; + +DOUBLEMASK *im_read_dmask( name ) +.br +char *name; + +INTMASK *im_read_imask( name ) +.br +char *name; + +void im_print_dmask( mask ) +.br +DOUBLEMASK *mask; + +void im_print_imask( mask ) +.br +INTMASK *mask; + +INTMASK im_scale_dmask( mask, name ) +.br +DOUBLEMASK *mask; +.br +char *name + +void im_norm_dmask( DOUBLEMASK *mask ); + +int im_write_imask( m ) +.br +INTMASK *m; + +int im_write_imask_name( m, name ) +.br +INTMASK *m; +.br +char *name; + +int im_write_dmask_name( m, name ) +.br +DOUBLEMASK *m; +.br +char *name; + +.SH DESCRIPTION +Masks are (see ): + + typedef struct { typedef struct { + int xsize; int xsize; + int ysize; int ysize; + int scale; double scale; + int offset; double offset; + int *coeff; double *coeff; + char *filename; char *filename; + } INTMASK ; } DOUBLEMASK ; + +By mask files have the following structure: + + x y [scale] [offset] + a11 a12 ... a1y + a21 a22 ... a2y] + ... ... ... ... + ax1 ax2 ... axy + +The mask sizes x and y are always integer whereas all the remaining coefficients +are either int for INTMASK and double for the DOUBLEMASK. All numbers are +written in ASCII, for convenience. scale and offset are +always optional. If missing, they default to 1 and 0 respectively. This laxity +makes mask files useful for matrix operations, see im_matinv(3), etc. + +im_create_dmask() +returns an DOUBLEMASK with the filename xsize and ysize set to name xs and ys +respectively. The function allocates memory for the coefficients which are +all initialised to zero. In case of error the function returns NULL. + +im_create_dmaskv() operates exactly as im_create_dmask(), except that the mask +is initialised by the parameters following the width and height. These +parameters must all be doubles. For example: + + DOUBLEMASK *m = im_create_dmaskv( "untitled", 3, 3, + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 ); + +creates an identity matrix. + +im_create_imask() +returns an INTMASK with the filename xsize and ysize set to name xs and ys +respectively. The function allocates memory for the coefficients which are +all initialised to zero. In case of error the function returns NULL. + +im_create_imaskv() operates exactly as im_create_imask(), except that the mask +is initialised by the parameters following the width and height. These +parameters must all be ints. For example: + + INTMASK *m = im_create_imaskv( "untitled", 3, 3, + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 ); + +creates an identity matrix. + +im_dup_dmask() +duplicates the DOUBLEMASK mask pointed by m and returns a pointer to the +new mask. The filename member of the returned mask is set to the entered +name. The function returns either a DOUBLEMASK pointer or NULL on error. + +im_dup_imask() +duplicates the INTMASK mask pointed by m and returns a pointer to the +new mask. The filename member of the returned mask is set to the entered +name. The function returns either an INTMASK pointer or NULL on error. + +im_free_dmask() +frees the memory allocated for the DOUBLEMASK mask. + +im_free_imask() +frees the memory allocated for the INTMASK mask. + +im_print_dmask() +prints at the sterr the DOUBLEMASK pointed by mask. + +im_print_imask() +prints at the sterr the INTMASK pointed by mask. + +im_read_dmask() +opens the argument name and reads the mask into a DOUBLEMASK structure. +The function returns a pointer to an DOUBLEMASK or NULL on error. + +im_read_imask() +opens the argument name and reads the mask into a INTMASK structure. +If the input +file has non-integer coefficients then the function im_read_dmask() should +be used instead. There is a built-in checking for all mask members. +If the mask has coefficients which are integers or they are floats +with the decimal part equal to zero, the function reads the mask +properly and puts the resultant coefficients in a valid INTMASK. +The function returns a pointer to an INTMASK or NULL on error. + +im_scale_dmask() +returns an INTMASK of the DOUBLE MASK m. The maximum value of the DOUBLEMASK +is scaled to 100 in the returned mask. The scale member of the INTMASK is +scaled accordingly to the scale member of m. +The function returns a pointer to an INTMASK or NULL on error. + +.B im_norm_dmask(3) +applies the scale and offset members of +.I mask +to each of its coefficients, and then sets them to 1.0 and zero respectively. + +im_write_imask() +writes the INTMASK pointed by m to the filename member of m. The function +checks whether the filename is valid and it expects only an INTMASK. +The function returns 0 on success and -1 on error. +For writing a DOUBLEMASK use im_write_dmask() instead. + +im_write_imask_name() +operates as im_write_imask(), but the mask is written to the named file. + +im_write_dmask() +writes the DOUBLEMASK pointed by m to the filename member of m. The function +checks whether the filename is valid and it expects only an DOUBLEMASK. +The mask is written in a file with the format given above. The function uses +fprintf with the conversion character %g, to write the non integer +mask coefficients. It returns 0 on success and -1 on error. + +im_write_dmask_name() +operates as im_write_dmask(), but the mask is written to the named file. + +.SH SEE ALSO +im_lindetect(3), im_compass(3), im_conv(3), im_matinv(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 02/05/1991 diff --git a/libsrc/convolution/man3/im_create_imask.3 b/libsrc/convolution/man3/im_create_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_create_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_dup_dmask.3 b/libsrc/convolution/man3/im_dup_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_dup_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_dup_imask.3 b/libsrc/convolution/man3/im_dup_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_dup_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_embed.3 b/libsrc/convolution/man3/im_embed.3 new file mode 100644 index 00000000..d09de3bf --- /dev/null +++ b/libsrc/convolution/man3/im_embed.3 @@ -0,0 +1,36 @@ +.TH IM_EMBED 3 "11 April 1995" +.SH NAME +im_embed \- extract a portion of an image +.SH SYNOPSIS +.B #include + +int im_embed( in, out, type, x, y, w, h ) +.br +IMAGE *in, *out; +.br +int type; +.br +int x, y, w, h; + +.SH DESCRIPTION +im_embed() embeds in within a larger image (size w by h), with in's top +left-hand corner at position (x,y) within the output image. The value of type +controls what appears in the new pels. + + 0 - black pels (all bytes 0) + 1 - extend pels from image to edge + 2 - tile pels from image + 3 - mirror pels from image + 4 - white pels (all bytes 255) + +Works for any size image, any number of bands, any type. Works for LABPACK +coded images too. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_insert(3), im_extract(3), im_region_region(3), im_fill_copy(3). +.SH COPYRIGHT +National Gallery, 1995. +.SH AUTHOR +J. Cupitt \- 11/04/1995 diff --git a/libsrc/convolution/man3/im_fastcor.3 b/libsrc/convolution/man3/im_fastcor.3 new file mode 100644 index 00000000..6d02e7ad --- /dev/null +++ b/libsrc/convolution/man3/im_fastcor.3 @@ -0,0 +1,67 @@ +.TH IM_FASTCOR 3 "14 May 1991" +.SH NAME +im_fastcor, im_spcor \- correlate two images +.SH SYNOPSIS +.B #include + +.B int im_fastcor(in, ref, out) +.br +.B IMAGE *in, *ref, *out; + +.B int im_spcor(in, ref, out) +.br +.B IMAGE *in, *ref, *out; +.SH DESCRIPTION +These functions calculate spatial correlation between two +one-band images held +by the image descriptors +.B in +and +.B ref. +The sizes of +.B ref +should be smaller than +the sizes of +.B in. +The correlation is carried out by overlapping +.B ref +on the top +left corner of +.B in +and moving it over +.B in. + +The output image is the same size as the input. The edge pixels are calculated +by expanding the input image using +.B im_embed(3) +in mode 1 (replicating edge pixels) just enough so that the output can match +the input. + +.B im_spcor(3) +calculates the spatial correlation between +.B in +and +.B ref +using the +correlation coefficient from Niblack "An Introduction to Digital Image +Processing,", Prentice/Hall, pp 138. The resultant coefficients are written +as float numbers in +.B out. +The images must be char, short or ushort. + +.B im_fastcor(3) +simply returns the sum of squares of differences between +.B in +and +.B ref. +This is much faster, but less useful. The resultant coefficients are written +as unsigned int numbers in out which has a size of in. + +.SH BUGS +The functions do not check for integer overflow. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_lrmosaic(3). +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1990-1997. diff --git a/libsrc/convolution/man3/im_free_dmask.3 b/libsrc/convolution/man3/im_free_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_free_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_free_imask.3 b/libsrc/convolution/man3/im_free_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_free_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_gauss_dmask.3 b/libsrc/convolution/man3/im_gauss_dmask.3 new file mode 100644 index 00000000..37c3a33f --- /dev/null +++ b/libsrc/convolution/man3/im_gauss_dmask.3 @@ -0,0 +1,48 @@ +.TH GAUSS_MASKS 3 "6 December 1991" +.SH NAME +im_gauss_dmask, im_gauss_imask \- create a gaussian DOUBLEMASK or INTMASK +.SH SYNOPSIS +.B #include + +.B DOUBLEMASK im_gauss_dmask( name, sigma, min_amplitude ) +.br +char *name; +.br +double sigma, min_amplitude; + +.B INTMASK im_gauss_imask( name, sigma, min_amplitude ) +.br +char *name; +.br +double sigma, min_amplitude; +.SH DESCRIPTION +Both functions create a circularly symmetric Gaussian mask of sigma. The size +of the mask is determined by the variable min_amplitude; if for instance the +value .1 is entered this means that the produced mask is clipped at values +less than 10 percent of the maximum amplitude. +The mask can be directly used with the vasari convolution +programs, the default offset set is 0. + +The program uses the following equation: + + H(r) = exp( -(r * r) / (2 * sigma * sigma) ). + +The generated mask has odd size and its maximum value is normalised to +either 100 (gauss_imask) or to 1.0 (gauss_dmask). + +.B im_gauss_dmask() +creates a DOUBLEMASK laplacian of Gaussian mask with maximum value normalised +to 1.0. + +.B im_gauss_imask() +creates a INTMASK laplacian of Gaussian mask with maximum value normalised +to 100. +.SH RETURNED VALUE: +The functions return NULL on erorr. +.SH SEE ALSO +im_log_dmask(3), im_conv(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 06/12/1991 diff --git a/libsrc/convolution/man3/im_gauss_imask.3 b/libsrc/convolution/man3/im_gauss_imask.3 new file mode 100644 index 00000000..dca741eb --- /dev/null +++ b/libsrc/convolution/man3/im_gauss_imask.3 @@ -0,0 +1 @@ +.so man3/im_gauss_dmask.3 diff --git a/libsrc/convolution/man3/im_gaussnoise.3 b/libsrc/convolution/man3/im_gaussnoise.3 new file mode 100644 index 00000000..a7b05c24 --- /dev/null +++ b/libsrc/convolution/man3/im_gaussnoise.3 @@ -0,0 +1,27 @@ +.TH IM_GAUSNOISE 3 "10 May 1991" +.SH NAME +im_gaussnoise \- creates a gaussian noisy picture +.SH SYNOPSIS +#include + +int im_gaussnoise(image, xsize, ysize, mean, sigma) +.br +IMAGE *image; +.br +int xsize, ysize; +.br +double mean, sigma; + +.SH DESCRIPTION +im_gaussnoise() creates a float one band gaussian noise picture of size xsize +by ysize. The created image has mean mean and square root of variance equal +to sigma. The noise is generated by averaging 12 random numbers. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_grey(3), im_addgnoise(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/convolution/man3/im_gradient.3 b/libsrc/convolution/man3/im_gradient.3 new file mode 100644 index 00000000..5f146a90 --- /dev/null +++ b/libsrc/convolution/man3/im_gradient.3 @@ -0,0 +1,2 @@ +.so man3/im_compass.3 + diff --git a/libsrc/convolution/man3/im_lindetect.3 b/libsrc/convolution/man3/im_lindetect.3 new file mode 100644 index 00000000..34274649 --- /dev/null +++ b/libsrc/convolution/man3/im_lindetect.3 @@ -0,0 +1 @@ +.so man3/im_compass.3 diff --git a/libsrc/convolution/man3/im_log_dmask.3 b/libsrc/convolution/man3/im_log_dmask.3 new file mode 100644 index 00000000..3bc62f48 --- /dev/null +++ b/libsrc/convolution/man3/im_log_dmask.3 @@ -0,0 +1 @@ +.so man3/im_log_imask.3 diff --git a/libsrc/convolution/man3/im_log_imask.3 b/libsrc/convolution/man3/im_log_imask.3 new file mode 100644 index 00000000..15e4cb2d --- /dev/null +++ b/libsrc/convolution/man3/im_log_imask.3 @@ -0,0 +1,56 @@ +.TH LOG_MASKS 3 "6 December 1991" +.SH NAME +im_log_dmask, im_log_imask \- create a laplacian of gaussian (log) +DOUBLEMASK or INTMASK +.SH SYNOPSIS +.B +#include + + +.B DOUBLEMASK *im_log_dmask( name, sigma, min_amplitude ) +.br +char *name; +.br +double sigma, min_amplitude; + +.B INTMASK *im_log_imask( name, sigma, min_amplitude ) +.br +char *name; +.br +double sigma, min_amplitude; +.SH DESCRIPTION +Both functions create a circularly symmetric laplacian of Gaussian mask. The +size of the mask is determined by the variable min_amplitude; if for instance +the value .1 is entered this means that the produced mask is clipped at values +within 10 persent of zero, and where the change between mask elements is less +than 10%. +The mask can be directly used with the vasari convolution +programs, the default offset set is 0. + +The program uses the following equation: (from Handbook of Pattern Recognition +and image processing by Young and Fu, AP 1986 pages 220-221): + + H(r) = (1 / (2 * M_PI * s4)) * + (2 - (r2 / s2)) * + exp(-r2 / (2 * s2)) + +where s2 = sigma * sigma, s4=s2 * s2, r2 = r * r. The generated mask has odd +size and its +maximum value is normalised to either 100 (log_imask) or to 1.0 (log_dmask) + +.B im_log_dmask() +creates a DOUBLEMASK laplacian of Gaussian mask with maximum value normalised +to 1.0. + +.B im_log_imask() +creates a INTMASK laplacian of Gaussian mask with maximum value normalised +to 100. +.SH RETURNED VALUE: +The functions return NULL on erorr. +.SH SEE ALSO +im_gauss_dmask(3), im_conv(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 06/12/1991 diff --git a/libsrc/convolution/man3/im_lowpass.3 b/libsrc/convolution/man3/im_lowpass.3 new file mode 100644 index 00000000..38a25465 --- /dev/null +++ b/libsrc/convolution/man3/im_lowpass.3 @@ -0,0 +1,26 @@ +.TH IM_CONV 3 "2 May 1991" +.SH NAME +im_lowpass \- remove high frequencies quickly +.SH SYNOPSIS +#include + +int im_lowpass(in, out, x, y) +.br +IMAGE *in, *out; +.br +int x, y; +.SH DESCRIPTION +Expand image up to X by Y pixels. Linear interpolation. Used by lowpass(1X) +for simple and nasty low-pass filtering. The result is very poor, but good +enough for smoothing out whites. Any number of bands, output size == input +size, any non-complex type. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_shrink(3), im_similarity_area(3). +.SH COPYRIGHT +.br +National Gallery +.SH AUTHOR +Too embarrassed to say diff --git a/libsrc/convolution/man3/im_maxvalue.3 b/libsrc/convolution/man3/im_maxvalue.3 new file mode 100644 index 00000000..34274649 --- /dev/null +++ b/libsrc/convolution/man3/im_maxvalue.3 @@ -0,0 +1 @@ +.so man3/im_compass.3 diff --git a/libsrc/convolution/man3/im_mpercent.3 b/libsrc/convolution/man3/im_mpercent.3 new file mode 100644 index 00000000..1f5ec9dc --- /dev/null +++ b/libsrc/convolution/man3/im_mpercent.3 @@ -0,0 +1,30 @@ +.TH IM_MPERCENT 3 "14 May 1991" +.SH NAME +im_mpercent \- find threshold corresponding to a percentage of an image values +.SH SYNOPSIS +#include + +int im_mpercent( in, percent, thresh ) +.br +IMAGE *in; +.br +int percent; +.br +int *thresh; + +.SH DESCRIPTION +im_mpercent() returns (through the thresh parameter) the threshold above which +there are percent values of the input image. If for example percent=.1, the +number of pels of the input image with values greater than the returned int +will correspond to 10% of all pels of the image. + +The function is applied on one band FMTUCHAR images only. It can be +used in order to threshold the scaled result of a filtering operation. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_zerox(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 14/05/1991 diff --git a/libsrc/convolution/man3/im_norm_dmask.3 b/libsrc/convolution/man3/im_norm_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_norm_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_offsets45.3 b/libsrc/convolution/man3/im_offsets45.3 new file mode 100644 index 00000000..5504772e --- /dev/null +++ b/libsrc/convolution/man3/im_offsets45.3 @@ -0,0 +1,99 @@ +.TH IM_OFFSETS45 3 "28 May 1991" +.SH NAME +im_offsets45, im_offsets90, im_rotate_imask45, im_rotate_dmask45, +im_rotate_imask90, im_rotate_dmask90 \- rotate square masks +.SH SYNOPSIS + +.B int *im_offsets45( size ) +.br +.B int size; + +.B int *im_offsets90( size ) +.br +.B int size; + +.B INTMASK *im_rotate_imask45( m, name ) +.br +.B INTMASK *m; +.br +.B char *name; + +.B DOUBLEMASK *im_rotate_dmask45( m, name ) +.br +.B DOUBLEMASK *m; +.br +.B char *name; + +.B INTMASK *im_rotate_imask90( m, name ) +.br +.B INTMASK *m; +.br +.B char *name; + +.B DOUBLEMASK *im_rotate_dmask90( m, name ) +.br +.B DOUBLEMASK *m; +.br +.B char *name; + +.SH DESCRIPTION +These functions can be used to produce the integer offsets needed to rotate +masks by 45 or 90 degrees clockwise. + +.B im_offsets45() +accepts only even size and returns a pointer to an int buffer of size*size. +The program mallocs the buffer and puts into it the integer offsets needed to +rotate a mask of even size size by 45 degrees. For instance if size is 3 the +program returns the buffer 3 0 1 6 4 2 7 8 5 which is the offsets needed to +rotate a mask with offsets 0 1 2 3 4 5 6 7 8. The function return a +pointer to valid data on success or NULL on error. Since the program mallocs +a buffer, the user should free the returned pointer accordingly. + +.B im_offsets90() +accepts any size and returns a pointer to an int buffer of size*size. The +program mallocs the buffer and puts into it the integer offsets needed to +rotate a mask of size size by 90 degrees. For instance if size is 3 the +program returns the buffer 6 3 0 7 4 1 8 5 2 which is the offsets needed to +rotate a mask with offsets 0 1 2 3 4 5 6 7 8. The function return a +pointers to valid data on success or NULL on error. Since the program mallocs +a buffer, the user should free the returned pointer accordingly. + +.B im_rotate_imask45() +rotates the INTMASK m by 45 degrees and returns the rotated mask. The filename +member of the returned structure is set to name. The program allocates memory +for the new mask and therefore the user must free the returned mask by a call +to im_free_imask(3). The returned mask is rotated by 45 degrees clockwise. +The function return a valid INTMASK pointer on success or NULL on error. + +.B im_rotate_dmask45() +rotates the DOUBLEMASK m by 45 degrees +and returns the rotated mask. The filename +member of the returned structure is set to name. The program allocates memory +for the new mask and therefore the user must free the returned mask by a call +to im_free_dmask(3). The returned mask is rotated by 45 degrees clockwise. +The function return a valid DOUBLEMASK pointer on success or NULL on error. + +.B im_rotate_imask90() +rotates the INTMASK m by 90 degrees and returns the rotated mask. The filename +member of the returned structure is set to name. The program allocates memory +for the new mask and therefore the user must free the returned mask by a call +to im_free_imask(3). The returned mask is rotated by 90 degrees clockwise. +The function return a valid INTMASK pointer on success or NULL on error. + +.B im_rotate_dmask90() +rotates the DOUBLEMASK m by 90 degrees +and returns the rotated mask. The filename +member of the returned structure is set to name. The program allocates memory +for the new mask and therefore the user must free the returned mask by a call +to im_free_dmask(3). The returned mask is rotated by 90 degrees clockwise. +The function return a valid DOUBLEMASK pointer on success or NULL on error. + +.SH RETURN VALUE +.SH SEE ALSO +im_read_imask(3), im_read_dmask(3), im_free_imask(3), im_free_dmask(3), +im_gradient(3), im_compass(3), im_conv(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \-28/05/1991 diff --git a/libsrc/convolution/man3/im_offsets90.3 b/libsrc/convolution/man3/im_offsets90.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_offsets90.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_print_dmask.3 b/libsrc/convolution/man3/im_print_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_print_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_print_imask.3 b/libsrc/convolution/man3/im_print_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_print_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_rank.3 b/libsrc/convolution/man3/im_rank.3 new file mode 100644 index 00000000..ea870068 --- /dev/null +++ b/libsrc/convolution/man3/im_rank.3 @@ -0,0 +1,45 @@ +.TH IM_RANK 3 "19 Aug 1996" +.SH NAME +im_rank, im_rank_raw \- rank filter +.SH SYNOPSIS +#include + +int im_rank(in, out, xsize, ysize, n) +.br +IMAGE *in, *out; +.br +int xsize, ysize, n; + +int im_rank_raw(in, out, xsize, ysize, n) +.br +IMAGE *in, *out; +.br +int xsize, ysize, n; + +.SH DESCRIPTION +.B im_rank() +does rank filtering on an image. A window of size xsize by ysize +is passed over the image. At each position, the pixels inside the window are +sorted into ascending order and the pixel at the nth position is output. n +numbers from 0. + +It works for any non-complex image type, with any number of bands. A black +border is added to the output image to make it the same size as the +input. + +.B im_rank_raw() +works just as im_rank(), but does not add the border. +.SH EXAMPLES +For a median filter with mask size m (3 for 3x3, 5 for 5x5, etc.) use + + im_rank( in, out, m, m, m * m / 2 ); + +The special cases n == 0 and n == m * m - 1 are useful dilate and expand +operators. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_fastcor(3). +.SH COPYRIGHT +1989-1996 The National Gallery and Birkbeck College diff --git a/libsrc/convolution/man3/im_rank_image.3 b/libsrc/convolution/man3/im_rank_image.3 new file mode 100644 index 00000000..34274649 --- /dev/null +++ b/libsrc/convolution/man3/im_rank_image.3 @@ -0,0 +1 @@ +.so man3/im_compass.3 diff --git a/libsrc/convolution/man3/im_read_dmask.3 b/libsrc/convolution/man3/im_read_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_read_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_read_imask.3 b/libsrc/convolution/man3/im_read_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_read_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_rotate_dmask45.3 b/libsrc/convolution/man3/im_rotate_dmask45.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_rotate_dmask45.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_rotate_dmask90.3 b/libsrc/convolution/man3/im_rotate_dmask90.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_rotate_dmask90.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_rotate_imask45.3 b/libsrc/convolution/man3/im_rotate_imask45.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_rotate_imask45.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_rotate_imask90.3 b/libsrc/convolution/man3/im_rotate_imask90.3 new file mode 100644 index 00000000..bf93b39a --- /dev/null +++ b/libsrc/convolution/man3/im_rotate_imask90.3 @@ -0,0 +1 @@ +.so man3/im_offsets45.3 diff --git a/libsrc/convolution/man3/im_scale_dmask.3 b/libsrc/convolution/man3/im_scale_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_scale_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_sharpen.3 b/libsrc/convolution/man3/im_sharpen.3 new file mode 100644 index 00000000..a6bd295d --- /dev/null +++ b/libsrc/convolution/man3/im_sharpen.3 @@ -0,0 +1,163 @@ +.TH IM_SHARPEN 3 "16 May 1995" +.SH NAME +im_sharpen \- simple coring edge enhancement +.SH SYNOPSIS +#include + +int +.br +im_sharpen( in, out, mask_radius, x1, y2, y3, m1, m2 ) +.br +IMAGE *in, *out; +.br +int mask_radius; +.br +double x1, y2, y3; +.br +double m1, m2; + +.SH DESCRIPTION +This function selectively sharpens the L* channel of a LABPACK coded image. It +is suitable for preparing an image for printing, where edges need to be +boosted to compensate for blurring introduced by the half-tone process, and +also for giving any additional `crispening' required. + +The function operates as: + + in gaussian out + --+-> blur with ----> subtract --> LUT --> add ----> + | mask_radius ^ ^ + | | | + +----------------------+ | + | | + +-----------------------------------------+ + +In other words, the L* channel is smoothed with a gaussian average function of +radius mask_radius and this smoothed image is subtracted from the +original L* to generate a high-frequency signal. + +This high-frequency signal is passed through a look-up table specified by the +x1, y2, y3, m1 and m2 parameters and added back to the original L* channel to +generate the sharpened image. + +The LUT is shaped as: + + ^ + y2 |- - - - - ----------- + | / + | / slope m2 + | .../ + -x1 | ... | + -------------------...----------------------> + | ... | x1 + |... slope m1 + / | + / m2 | + / | + / | + / | + / | + ______/ _ _ _ _ _ _ | -y3 + | + +When trying to understand the meaning of these parameters, it is helpful to +imagine a cross-section through an image. Sharpening filters boost apparent +sharpness by performing the following transformation. Consider a cross-section +through a soft edge: + + ^ + | .......... + | .... + | ... + | .. + |. + --------------------+-------------------> + .| + .. | + ... | + .... | + .......... | + | + +This becomes an enhanced edge, perhaps: + + ^ + | ... overshoot + | . . + | . ............ + | . + |. + . + . + --------------------+-------------------> + . + .| + .| + . | + ............. . | + . . | + undershoot ... | + | + +There are two features: the transition from black to white has become +steeper, and there are positive and negative undershoots and overshoots. + +As a general guide, some overshoot is good for printing (it helps exaggerate +the edge), but not too much, as you will start to see strong white fringes. +Undershoot introduces black lines, which are less intrusive, so you can allow +more under- than over-shoot. + +im_sharpen uses the x1 parameter to distinguish between low and high frequency +areas. Pixels which differ by less than x1 L* units from their local average +are sharpened by factor 1+m1, and pixels which differ by more than x1 are +sharpened by factor 1+m2. y2 and y3 set limits on the amount of positive and +negative sharpening we allow. + +For printing, we recommend the following settings: + + mask_radius == 7 + x1 == 1.5 + y2 == 20 (don't brighten by more than 20 L*) + y3 == 50 (can darken by up to 50 L*) + + m1 == 1 (some sharpening in flat areas) + m2 == 2 (more sharpening in jaggy areas) + +If you want more or less sharpening, we suggest you just change the m1 and m2 +parameters. For an extreme sharpen, you might try: + + m1 == 2 + m2 == 4 + +And for a relatively gentle sharpen, perhaps: + + m1 == 0.5 + m2 == 1.5 + +If you want to adjust the x1 parameter, it can be helpful visualise its +effect by setting: + + m1 == -2 + m2 == 4 + +In other words, heavily blur flat areas, and heavily sharpen elsewhere. This +creates a terrible-looking image, but you will be able to see clearly what +parts of your image are being classified as flat. + +The y2 and y3 parameters need not usually be adjusted, unless you wish to +reduce the strength of the finges. + +The mask_radius parameter changes the width of the fringe and can be adjusted +according to the output printing resolution. As an approximate guideline, use +3 for 4 pixels/mm (CRT display resolution), 5 for 8 pixels/mm, 7 for 12 +pixels/mm and 9 for 16 pixels/mm (300 dpi == 12 pixels/mm). These figures +refer to the image raster, not the half-tone resolution. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_compass(3), im_fastcor(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1995 +.SH THANKS TO +Lindsay! diff --git a/libsrc/convolution/man3/im_shrink.3 b/libsrc/convolution/man3/im_shrink.3 new file mode 100644 index 00000000..1a31740e --- /dev/null +++ b/libsrc/convolution/man3/im_shrink.3 @@ -0,0 +1 @@ +.so man3/im_conv.3 diff --git a/libsrc/convolution/man3/im_spcor.3 b/libsrc/convolution/man3/im_spcor.3 new file mode 100644 index 00000000..e22eb0f2 --- /dev/null +++ b/libsrc/convolution/man3/im_spcor.3 @@ -0,0 +1 @@ +.so man3/im_fastcor.3 diff --git a/libsrc/convolution/man3/im_stretch3.3 b/libsrc/convolution/man3/im_stretch3.3 new file mode 100644 index 00000000..3b08c3e4 --- /dev/null +++ b/libsrc/convolution/man3/im_stretch3.3 @@ -0,0 +1,28 @@ +.TH IM_STRETCH3 3 "Sep 18 1997" +.SH NAME +im_stretch3 \- stretch horizontally by 3%, displace sub-pixel +mask +.SH SYNOPSIS +#include + +int im_stretch3( in, out, xdisp, ydisp ) +.br +IMAGE *in, *out; +.br +double xdisp, ydisp; + +.SH DESCRIPTION +im_stretch3() stretches the input image by 3% horizontally, and displaces it +by xdisp/ydisp. It uses bi-cubic interpolation, but runs quickly. It works +only for unsigned short images. + +This function is part of the MARC acquisition software, but is generally +useful for squaring up the pixels in images from the Kontron ProgRes camera +range. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3), im_shrink(3). +.SH COPYRIGHT +The National Gallery, 1997 diff --git a/libsrc/convolution/man3/im_write_dmask.3 b/libsrc/convolution/man3/im_write_dmask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_write_dmask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_write_dmask_name.3 b/libsrc/convolution/man3/im_write_dmask_name.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_write_dmask_name.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_write_imask.3 b/libsrc/convolution/man3/im_write_imask.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_write_imask.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_write_imask_name.3 b/libsrc/convolution/man3/im_write_imask_name.3 new file mode 100644 index 00000000..3b08512d --- /dev/null +++ b/libsrc/convolution/man3/im_write_imask_name.3 @@ -0,0 +1 @@ +.so man3/im_create_dmask.3 diff --git a/libsrc/convolution/man3/im_zerox.3 b/libsrc/convolution/man3/im_zerox.3 new file mode 100644 index 00000000..5678af66 --- /dev/null +++ b/libsrc/convolution/man3/im_zerox.3 @@ -0,0 +1,30 @@ +.TH IM_ZEROX 3 "14 May 1991" +.SH NAME +im_zerox \- find the zero crossings of an image +.SH SYNOPSIS +.B #include + +int im_zerox(in, out, flag) +.br +IMAGE *in, *out; +.br +int flag; + +.SH DESCRIPTION +im_zerox() detects the positive and negative edges of zero crossings of an +image held by the image descriptor in, depending on the flag. + +If flag is -1 the negative zero crossings are returned. +If flag is 1 the positive zero crossings are returned. + +The output image is byte with zero crossing set to 255 and all other values +set to zero. Input can have any number of channels, and be any non-complex type. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_conv(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 14/05/1991 diff --git a/libsrc/convolution/rotmask.c b/libsrc/convolution/rotmask.c new file mode 100644 index 00000000..85c1057e --- /dev/null +++ b/libsrc/convolution/rotmask.c @@ -0,0 +1,356 @@ +/* @(#) Functions to create offsets for rotating square masks. + * @(#) Usage + * @(#) int *im_offsets45( size ) + * @(#) int size; + * @(#) + * @(#) Returns an int pointer to valid offsets on sucess and -1 on error + * @(#) + * + * @(#) Usage + * @(#) int *im_offsets90( size ) + * @(#) int size; + * @(#) + * @(#) Returns an int pointer to valid offsets on sucess and -1 on error + * @(#) + * + * @(#) Functions to rotate square masks + * @(#) + * @(#) Usage + * @(#) INTMASK *im_rotate_imask45( mask, name ) + * @(#) INTMASK *mask; + * @(#) + * @(#) Returns an pointer to INTMASK which keeps the original mask rotated + * @(#) by 45 degrees clockwise. + * @(#) The filename member of the returned mask is set to name + * @(#) + * @(#) Usage + * @(#) DOUBLEMASK *im_rotate_dmask45( mask, name ) + * @(#) DOUBLEMASK *mask; + * @(#) + * @(#) Returns an pointer to INTMASK which keeps the original mask rotated + * @(#) by 45 degrees clockwise. + * @(#) The filename member of the returned mask is set to name + * @(#) + * @(#) Usage + * @(#) INTMASK *im_rotate_imask90( mask, name ) + * @(#) INTMASK *mask; + * @(#) + * @(#) Returns an pointer to INTMASK which keeps the original mask rotated + * @(#) by 90 degrees clockwise. + * @(#) The filename member of the returned mask is set to name + * @(#) + * @(#) Usage + * @(#) DOUBLEMASK *im_rotate_dmask90( mask, name ) + * @(#) DOUBLEMASK *mask; + * @(#) + * @(#) Returns an pointer to DOUBLEMASK which keeps the original mask rotated + * @(#) by 90 degrees clockwise. + * @(#) The filename member of the returned mask is set to name + * @(#) + * @(#) Prints a mask. Used mainly for debugging purposes + * @(#) + * @(#) Usage + * @(#) void im_print_dmask( m ) + * @(#) DOUBLEMASK *m; + * @(#) + * @(#) Usage + * @(#) void im_print_imask( m ) + * @(#) INTMASK *m; + * @(#) + * + * + * Author: N. Dessipris (Copyright, N. Dessipris 1991) + * Written on: 08/05/1991 + * Modified on: 28/05/1991 + * 12/10/95 JC + * - small revisions, needs rewriting really + * 7/8/96 JC + * - absolutely foul desp code revised + * - many bugs and mem leaks fixed + * 1/3/99 JC + * - oops, fns were not preserving scale and offset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define PIM_RINT 1 +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Creates the offsets to rotate by 45 degrees an odd size square mask + */ +int * +im_offsets45( int size ) +{ + int temp; + int x, y; + int size2 = size * size; + int size_2 = size / 2; + int *pnt, *cpnt1, *cpnt2; + + if( size%2 == 0 ) { + im_errormsg( "im_offsets45: size not odd" ); + return( NULL ); + } + if( !(pnt = IM_ARRAY( NULL, size2, int )) ) + return( NULL ); + + /* point at the beginning and end of the buffer + */ + cpnt1 = pnt; cpnt2 = pnt + size2 - 1; + + for( y = 0; y < size_2; y++ ) { + temp = (size_2 + y) * size; + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + + for( x = 0; x < y; x++ ) { + temp -= (size-1); + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + + for( x = 0; x < size_2 - y; x++ ) { + temp -= size; + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + + for( x = 0; x < size_2 - y; x++ ) { + temp++; + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + + for( x = 0; x < y; x++ ) { + temp -= ( size - 1 ); + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + } + + /* the diagonal now + */ + temp = size * (size - 1); + cpnt1 = pnt + size_2 * size; + for( x = 0; x < size; x++ ) { + *cpnt1++ = temp; + temp -= (size-1); + } + +#ifdef PIM_RINT + temp = 0; + for( y = 0; y < size; y++ ) { + for( x = 0; x < size; x++ ) { + fprintf( stderr, "%4d", *(pnt+temp) ); + temp++; + } + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); +#endif + + return( pnt ); +} + + +/* Creates the offsets to rotate any mask by 90 degrees. + */ +int * +im_offsets90( int size ) +{ + int temp; + int x, y, k; + int *offsets; + + if( !(offsets = IM_ARRAY( NULL, size*size, int )) ) + return( NULL ); + + for( k = 0, y = 0; y < size; y++ ) { + temp = size * (size - 1) + y; + + for( x = 0; x < size; x++, k++ ) { + offsets[k] = temp; + temp -= size; + } + } + + return( offsets ); +} + +/* Tye pf offset-generating function. + */ +typedef int *(*offset_fn)( int ); + +/* Rotate a dmask with a set of offsets. + */ +static DOUBLEMASK * +rotdmask( offset_fn fn, DOUBLEMASK *m, const char *name ) +{ + DOUBLEMASK *out; + int size = m->xsize * m->ysize; + int *offsets; + int i; + + if( m->xsize != m->ysize || (m->xsize % 2) == 0 ) { + im_errormsg( "im_rotate_*mask*: mask should " + "be square of even size" ); + return( NULL ); + } + if( !(offsets = fn( m->xsize )) ) + return( NULL ); + if( !(out = im_create_dmask( name, m->xsize, m->ysize )) ) { + im_free( offsets ); + return( NULL ); + } + out->scale = m->scale; + out->offset = m->offset; + + for( i = 0; i < size; i++ ) + out->coeff[i] = m->coeff[offsets[i]]; + + im_free( offsets ); + + return( out ); +} + +/* Rotate an imask with a set of offsets. + */ +static INTMASK * +rotimask( offset_fn fn, INTMASK *m, const char *name ) +{ + INTMASK *out; + int size = m->xsize * m->ysize; + int *offsets; + int i; + + if( m->xsize != m->ysize || (m->xsize % 2) == 0 ) { + im_errormsg( "im_rotate_*mask*: mask should " + "be square of even size" ); + return( NULL ); + } + if( !(offsets = fn( m->xsize )) ) + return( NULL ); + if( !(out = im_create_imask( name, m->xsize, m->ysize )) ) { + im_free( offsets ); + return( NULL ); + } + out->scale = m->scale; + out->offset = m->offset; + + for( i = 0; i < size; i++ ) + out->coeff[i] = m->coeff[offsets[i]]; + + im_free( offsets ); + + return( out ); +} + +/* Returns a mask which is the argument mask rotated by 90 degrees. Filename + * of the returned mask is name. + */ +DOUBLEMASK * +im_rotate_dmask90( DOUBLEMASK *m, const char *name ) +{ + return( rotdmask( im_offsets90, m, name ) ); +} + +/* Returns a mask which is the argument mask rotated by 45 degrees. Filename + * of the returned mask is name. + */ +DOUBLEMASK * +im_rotate_dmask45( DOUBLEMASK *m, const char *name ) +{ + return( rotdmask( im_offsets45, m, name ) ); +} + +/* Returns a mask which is the argument mask rotated by 90 degrees. Filename + * of the returned mask is name. + */ +INTMASK * +im_rotate_imask90( INTMASK *m, const char *name ) +{ + return( rotimask( im_offsets90, m, name ) ); +} + +/* Returns a mask which is the argument mask rotated by 45 degrees. Filename + * of the returned mask is name. + */ +INTMASK * +im_rotate_imask45( INTMASK *m, const char *name ) +{ + return( rotimask( im_offsets45, m, name ) ); +} + +void +im_print_imask( INTMASK *m ) +{ + int i, j, k; + int *pm = m->coeff; + + fprintf( stderr, " %s: %d %d %d %d\n", + m->filename, m->xsize, m->ysize, m->scale, m->offset ); + + for( k = 0, j = 0; j < m->ysize; j++ ) { + for( i = 0; i < m->xsize; i++, k++ ) + fprintf( stderr, "%d\t", pm[k] ); + + fprintf( stderr, "\n" ); + } +} + +void +im_print_dmask( DOUBLEMASK *m ) +{ + int i, j, k; + double *pm = m->coeff; + + fprintf( stderr, " %s: %d %d %f %f\n", + m->filename, m->xsize, m->ysize, m->scale, m->offset ); + + for( k = 0, j = 0; j < m->ysize; j++ ) { + for( i = 0; i < m->xsize; i++, k++ ) + fprintf( stderr, "%f\t", pm[k] ); + + fprintf( stderr, "\n" ); + } +} diff --git a/libsrc/convolution/rw_mask.c b/libsrc/convolution/rw_mask.c new file mode 100644 index 00000000..7abffbbc --- /dev/null +++ b/libsrc/convolution/rw_mask.c @@ -0,0 +1,725 @@ +/* @(#) Function which read a mask from a file. + * @(#) Result is written in the structure IMASK or DMASK depending on whether + * @(#) the input mask is integer or double. The structure of the mask is + * @(#) given in mask.h + * @(#) The mask coefficients can be either int (INTMASK) + * @(#) or double (DOUBLEMASK). + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 29/04/1991 + * Modified on: 10/8/1992, J.Cupitt + * - Mask reading routines no longer fail if scale and offset are missing. + * Instead, they set default values of scale=1, offset=0. + * - Code tidied up, better error recovery. + * - Bugs fixed in im_dup_*mask. No longer coredump. + * - Bugs fixed in im_write_*mask. Now work for non-square matricies. + * - im_copy_*mask_matrix, im_copy_matrix_*mask added: copy VIPS mask + * structures into Numerical Recipies in C style matricies and vice + * versa. Both structures should have been built before copy attempted. + * See im_create_*mask, im_*mat_alloc. The matrix should be indexed by 0 + * to size-1. + * 9/7/93 JC + * - some ANSIfication and tidies + * - im_free_*mask() now return zero, so they can be used as close + * callbacks. + * 7/10/94 JC + * - new IM_NEW(), IM_ARRAY() macros added + * 27/4/95 JC + * - oops! forgot to init IM_ARRAY() memory to zero + * 7/8/96 JC + * - im_scale_dmask rewritten + * 7/5/98 JC + * - im_read_*mask() rewritten, now more robust + * - im_write_*mask() rewritten + * - new functions im_write_*mask_name() + * 28/7/99 JC + * - im_create_imaskv(), im_create_dmaskv() make masks and init from + * varargs + * - tabs allowed as column separators + * 9/2/05 + * - "," allowed as column separator ... helps CSV read + * 31/5/06 + * - use g_ascii_strtod() and friends + * 2006-09-08 tcv + * - add im_norm_dmask() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Size of line buffer for reading. + */ +#define IM_MAX_LINE (4096) + +/* Free mask structure and any attached arrays. Return zero, so we can use + * these functions as close callbacks. + */ +int +im_free_imask( INTMASK *m ) +{ + if( ! m ) + return 0; + + if( m->coeff ) + im_free( m->coeff ); + if( m->filename ) + im_free( m->filename ); + im_free( m ); + + return( 0 ); +} + +int +im_free_dmask( DOUBLEMASK *m ) +{ + if( ! m ) + return 0; + + if( m->coeff ) + im_free( m->coeff ); + if( m->filename ) + im_free( m->filename ); + im_free( m ); + + return( 0 ); +} + +/* Create structures. + */ +INTMASK * +im_create_imask( const char *filename, int xs, int ys ) +{ + INTMASK *m; + int size = xs * ys; + + /* Check args. + */ + if( xs <= 0 || ys <= 0 || filename == NULL ) { + im_error( "im_create_imask", _( "bad arguments" ) ); + return( NULL ); + } + + /* Allocate and initialise structure. + */ + if( !(m = IM_NEW( NULL, INTMASK )) ) + return( NULL ); + m->coeff = NULL; + m->filename = NULL; + m->scale = 1; + m->offset = 0; + m->xsize = 0; + m->ysize = 0; + + if( !(m->coeff = IM_ARRAY( NULL, size, int )) ) { + im_free_imask( m ); + return( NULL ); + } + (void) memset( (char *) m->coeff, 0, size * sizeof( int ) ); + if( !(m->filename = im_strdup( NULL, filename )) ) { + im_free_imask( m ); + return( NULL ); + } + m->xsize = xs; m->ysize = ys; + + return( m ); +} + +INTMASK * +im_create_imaskv( const char *filename, int xs, int ys, ... ) +{ + va_list ap; + + INTMASK *m; + int i; + + if( !(m = im_create_imask( filename, xs, ys )) ) + return( NULL ); + + va_start( ap, ys ); + for( i = 0; i < xs * ys; i++ ) + m->coeff[i] = va_arg( ap, int ); + va_end( ap ); + + return( m ); +} + +DOUBLEMASK * +im_create_dmask( const char *filename, int xs, int ys ) +{ + DOUBLEMASK *m; + int size = xs * ys; + + /* Check args. + */ + if( xs <= 0 || ys <= 0 || filename == NULL ) { + im_error( "im_create_dmask", _( "bad arguments" ) ); + return( NULL ); + } + + /* Allocate and initialise structure. + */ + if( !(m = IM_NEW( NULL, DOUBLEMASK )) ) + return( NULL ); + m->coeff = NULL; + m->filename = NULL; + m->scale = 1.0; + m->offset = 0.0; + m->xsize = 0; + m->ysize = 0; + + if( !(m->coeff = IM_ARRAY( NULL, size, double )) ) { + im_free_dmask( m ); + return( NULL ); + } + (void) memset( (char *) m->coeff, 0, size * sizeof( double ) ); + if( !(m->filename = im_strdup( NULL, filename )) ) { + im_free_dmask( m ); + return( NULL ); + } + m->xsize = xs; m->ysize = ys; + + return( m ); +} + +DOUBLEMASK * +im_create_dmaskv( const char *filename, int xs, int ys, ... ) +{ + va_list ap; + + DOUBLEMASK *m; + int i; + + if( !(m = im_create_dmask( filename, xs, ys )) ) + return( NULL ); + + va_start( ap, ys ); + for( i = 0; i < xs * ys; i++ ) + m->coeff[i] = va_arg( ap, double ); + va_end( ap ); + + return( m ); +} + +/* Open for read. + */ +static FILE * +open_read( const char *name ) +{ + FILE *fp; + + if( !(fp = fopen( name, "r" )) ) { + im_error( "read_mask", _( "Unable to open \"%s\" for input" ), + name ); + return( NULL ); + } + + return( fp ); +} + +/* Read a line from a file! + */ +static int +get_line( FILE *fp, char *buf ) +{ + if( !fgets( buf, IM_MAX_LINE, fp ) ) { + im_error( "read_mask", _( "unexpected EOF" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* width, height, optional scale, optional offset. + */ +static int +read_header( FILE *fp, int *xs, int *ys, double *scale, double *offset ) +{ + char buf[IM_MAX_LINE]; + char *p, *q; + double v[4]; + int i; + + /* Read the first line: should contain size and optional + * scale + offset. + */ + if( get_line( fp, buf ) ) + return( -1 ); + + /* Read as space separated doubles. \n is in the break list because + * our line will (usually) have a trailing \n which we want to count + * as whitespace. + */ + p = buf; + for( i = 0, p = buf; + i < 4 && (q = im_break_token( p, " \t\n" )); + i++, p = q ) + v[i] = g_ascii_strtod( p, NULL ); + + if( (i != 2 && i != 4) || + ceil( v[0] ) != v[0] || + ceil( v[1] ) != v[1] || + v[0] <= 0 || + v[1] <= 0 ) { + im_error( "read_header", _( "error reading matrix header" ) ); + return( -1 ); + } + if( i == 4 && v[2] == 0 ) { + im_error( "read_header", _( "scale should be non-zero" ) ); + return( -1 ); + } + + *xs = v[0]; + *ys = v[1]; + if( i == 2 ) { + *scale = 1.0; + *offset = 0.0; + } + else { + *scale = v[2]; + *offset = v[3]; + } + + return( 0 ); +} + +/* Read matrix files. + */ +DOUBLEMASK * +im_read_dmask( const char *maskfile ) +{ + FILE *fp; + double sc, off; + int xs, ys; + DOUBLEMASK *m; + int x, y, i, size; + char buf[IM_MAX_LINE]; + + if( !(fp = open_read( maskfile )) ) + return( NULL ); + + if( read_header( fp, &xs, &ys, &sc, &off ) ) { + fclose( fp ); + return( NULL ); + } + + if( !(m = im_create_dmask( maskfile, xs, ys )) ) { + fclose( fp ); + return( NULL ); + } + m->scale = sc; + m->offset = off; + size = xs * ys; + + for( i = 0, y = 0; y < ys; y++ ) { + char *p; + + if( get_line( fp, buf ) ) { + im_free_dmask( m ); + fclose( fp ); + return( NULL ); + } + + for( p = buf, x = 0; p && x < xs; + x++, i++, p = im_break_token( p, " \t,\";" ) ) + m->coeff[i] = g_ascii_strtod( p, NULL ); + } + fclose( fp ); + + return( m ); +} + +/* INTMASK ... read as double, check for intness. + */ +INTMASK * +im_read_imask( const char *maskfile ) +{ + DOUBLEMASK *dmask; + INTMASK *imask; + int i; + + if( !(dmask = im_read_dmask( maskfile )) ) + return( NULL ); + + if( ceil( dmask->scale ) != dmask->scale || + ceil( dmask->offset ) != dmask->offset ) { + im_free_dmask( dmask ); + im_error( "im_read_imask", + _( "scale and offset should be int" ) ); + + return( NULL ); + } + + for( i = 0; i < dmask->xsize * dmask->ysize; i++ ) + if( ceil( dmask->coeff[i] ) != dmask->coeff[i] ) { + im_free_dmask( dmask ); + im_error( "im_read_imask", _( "cofficient at " + "position (%d, %d) is not int" ), + i % dmask->xsize, + i / dmask->xsize ); + + return( NULL ); + } + + if( !(imask = im_create_imask( maskfile, + dmask->xsize, dmask->ysize )) ) { + im_free_dmask( dmask ); + return( NULL ); + } + imask->scale = dmask->scale; + imask->offset = dmask->offset; + for( i = 0; i < dmask->xsize * dmask->ysize; i++ ) + imask->coeff[i] = dmask->coeff[i]; + + im_free_dmask( dmask ); + + return( imask ); +} + +INTMASK * +im_scale_dmask( DOUBLEMASK *m, const char *name ) +{ + const int size = m->xsize * m->ysize; + + INTMASK *out; + double maxval, dsum; + int i; + int isum; + + if( !name || m->xsize <= 0 || m->ysize <= 0 ) { + im_error( "im_scale_dmask", _( "bad args" ) ); + return( NULL ); + } + if( !(out = im_create_imask( name, m->xsize, m->ysize )) ) + return( NULL ); + + /* Find mask max. + */ + maxval = m->coeff[0]; + for( i = 0; i < size; i++ ) + if( m->coeff[i] > maxval ) + maxval = m->coeff[i]; + + /* Copy and scale, setting max to 100. + */ + for( i = 0; i < size; i++ ) + out->coeff[i] = IM_RINT( m->coeff[i] * 100.0 / maxval ); + out->offset = m->offset; + + /* Set the scale to match the adjustment to max. + */ + isum = 0; + dsum = 0.0; + for( i = 0; i < size; i++ ) { + isum += out->coeff[i]; + dsum += m->coeff[i]; + } + + if( dsum == m->scale ) + out->scale = isum; + else + out->scale = IM_RINT( m->scale * isum / dsum ); + + return( out ); +} + +void +im_norm_dmask( DOUBLEMASK *mask ) +{ + const int n = mask->xsize * mask->ysize; + const double scale = 1.0 / mask->scale; + + int i; + + if( 1.0 == scale && 0.0 == mask->offset ) + return; + + for( i = 0; i < n; i++ ) + mask->coeff[i] = mask->coeff[i] * scale + mask->offset; + + mask->scale = 1.0; + mask->offset = 0.0; +} + +INTMASK * +im_dup_imask( INTMASK *m, const char *name ) +{ + const int xs = m->xsize; + const int ys = m->ysize; + const int size = xs * ys; + + INTMASK *new; + int i; + + if( !(new = im_create_imask( name, xs, ys )) ) + return( NULL ); + + new->offset = m->offset; + new->scale = m->scale; + + for( i = 0; i < size; i++ ) + new->coeff[i] = m->coeff[i]; + + return( new ); +} + +DOUBLEMASK * +im_dup_dmask( DOUBLEMASK *m, const char *name ) +{ + DOUBLEMASK *new; + int xs = m->xsize; + int ys = m->ysize; + int size = xs * ys; + int i; + double *pnt1, *pnt2; + + if( !(new = im_create_dmask( name, xs, ys )) ) + return( NULL ); + + new->offset = m->offset; + new->scale = m->scale; + + pnt1 = m->coeff; + pnt2 = new->coeff; + for( i = 0; i < size; i++ ) + *pnt2++ = *pnt1++; + + return( new ); +} + +/* Open for write. + */ +static FILE * +open_write( const char *name ) +{ + FILE *fp; + + if( !(fp = fopen( name, "w" )) ) { + im_error( "write_mask", _( "unable to open \"%s\" for output" ), + name ); + return( NULL ); + } + + return( fp ); +} + +/* Write to file. + */ +static int +write_line( FILE *fp, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + if( !vfprintf( fp, fmt, ap ) ) { + im_error( "write_mask", _( "write error, disc full?" ) ); + return( -1 ); + } + va_end( ap ); + + return( 0 ); +} + +static int +write_double( FILE *fp, double d ) +{ + char buf[G_ASCII_DTOSTR_BUF_SIZE]; + + fprintf( fp, "%s", g_ascii_dtostr( buf, sizeof( buf ), d ) ); + + return( 0 ); +} + +/* Write the INTMASK m into name. + */ +int +im_write_imask_name( INTMASK *m, const char *name ) +{ + FILE *fp; + int x, y, i; + + if( !(fp = open_write( name )) ) + return( -1 ); + + if( write_line( fp, "%d %d %d %d\n", + m->xsize, m->ysize, m->scale, m->offset ) ) { + fclose( fp ); + return( -1 ); + } + + for( i = 0, y = 0; y < m->ysize; y++ ) { + for( x = 0; x < m->xsize; x++, i++ ) + if( write_line( fp, "%d ", m->coeff[i] ) ) { + fclose( fp ); + return( -1 ); + } + + if( write_line( fp, "\n" ) ) { + fclose( fp ); + return( -1 ); + } + } + fclose( fp ); + + return( 0 ); +} + +/* Write the INTMASK m into m->filename + */ +int +im_write_imask( INTMASK *m ) +{ + if( !m->filename ) { + im_error( "im_write_imask", _( "filename not set" ) ); + return( -1 ); + } + + return( im_write_imask_name( m, m->filename ) ); +} + +/* Write the DOUBLEMASK m into name. + */ +int +im_write_dmask_name( DOUBLEMASK *m, const char *name ) +{ + FILE *fp; + int x, y, i; + + if( !(fp = open_write( name )) ) + return( -1 ); + + if( write_line( fp, "%d %d ", m->xsize, m->ysize ) || + write_double( fp, m->scale ) || + write_line( fp, " " ) || + write_double( fp, m->offset ) || + write_line( fp, "\n" ) ) { + fclose( fp ); + return( -1 ); + } + + for( i = 0, y = 0; y < m->ysize; y++ ) { + for( x = 0; x < m->xsize; x++, i++ ) + if( write_double( fp, m->coeff[i] ) || + write_line( fp, " " ) ) { + fclose( fp ); + return( -1 ); + } + + if( write_line( fp, "\n" ) ) { + fclose( fp ); + return( -1 ); + } + } + fclose( fp ); + + return( 0 ); +} + +/* Write the DOUBLEMASK m into m->filename + */ +int +im_write_dmask( DOUBLEMASK *m ) +{ + if( !m->filename ) { + im_error( "im_write_dmask", _( "filename not set" ) ); + return( -1 ); + } + + return( im_write_dmask_name( m, m->filename ) ); +} + +/* Copy an imask into a matrix. Only used internally by matrix package for + * invert. + */ +void +im_copy_imask_matrix( INTMASK *mask, int **matrix ) +{ + int x, y; + int *p = mask->coeff; + + for( y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++ ) + matrix[x][y] = *p++; +} + + +/* Copy a matrix into an imask. + */ +void +im_copy_matrix_imask( int **matrix, INTMASK *mask ) +{ + int x, y; + int *p = mask->coeff; + + for( y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++ ) + *p++ = matrix[x][y]; +} + +/* Copy a dmask into a matrix. + */ +void +im_copy_dmask_matrix( DOUBLEMASK *mask, double **matrix ) +{ + int x, y; + double *p = mask->coeff; + + for( y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++ ) + matrix[x][y] = *p++; +} + +/* Copy a matrix to a dmask. + */ +void +im_copy_matrix_dmask( double **matrix, DOUBLEMASK *mask ) +{ + int x, y; + double *p = mask->coeff; + + for( y = 0; y < mask->ysize; y++ ) + for( x = 0; x < mask->xsize; x++ ) + *p++ = matrix[x][y]; +} diff --git a/libsrc/dummy.c b/libsrc/dummy.c new file mode 100644 index 00000000..066b323d --- /dev/null +++ b/libsrc/dummy.c @@ -0,0 +1,3 @@ +/* mac os x libtool hates empty link sections in convenience libraries + */ +const int im__dummy_value = 42; diff --git a/libsrc/freq_filt/Makefile.am b/libsrc/freq_filt/Makefile.am new file mode 100644 index 00000000..d8bb54a2 --- /dev/null +++ b/libsrc/freq_filt/Makefile.am @@ -0,0 +1,19 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libfreq_filt.la + +libfreq_filt_la_SOURCES = \ + fft_sp.c \ + fmask4th.c \ + fmaskcir.c \ + freq_dispatch.c \ + im_disp_ps.c \ + im_fractsurf.c \ + im_freq_mask.c \ + im_freqflt.c \ + im_fwfft.c \ + im_invfft.c \ + im_invfftr.c \ + im_rotquad.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/freq_filt/fft_sp.c b/libsrc/freq_filt/fft_sp.c new file mode 100644 index 00000000..7cda5267 --- /dev/null +++ b/libsrc/freq_filt/fft_sp.c @@ -0,0 +1,213 @@ +/* Copyright (c) 1982 Michael Landy, Yoav Cohen, and George Sperling + +Disclaimer: No guarantees of performance accompany this software, +nor is any responsibility assumed on the part of the authors. All the +software has been tested extensively and every effort has been made to +insure its reliability. */ + +/* 1991 Modified by N. Dessipris to return a valid code on error */ + +/* fft -- fast fourier transform, adapted from +** Gonzalez & Wintz p.87. +** +** No division by N is performed. +** +** Timing: rough estimates: +** two-dimensional arrays, (including copying columns +** back and forth): +** for arrays up to 16X16: less than 1 sec. +** for 32X32 arrays: about 3.0 sec. +** for 64X64 arrays: about 9.0 sec. +** for 128X128 arrays: about 31.0 sec. +** +** Calling sequence: +** +** float *rvec,*ivec; +** int loglen,skip; +** +** fft_2d(rvec,ivec,loglen) +** performs a 2-dimensional fft where loglen is the log of the length of +** a side of the array +** +** fft_2dgen(rvec,ivec,logrows,logcols) +** performs a 2-dimensional fft where logrows is the log of the number of +** rows, and logcols is the log of the number of columns +** +** fftn(rvec,ivec,loglen,skip) +** performs a 1-dimensional fft on every skip-th entry +*/ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +/* Only compile this if we're missing the fftw library. + */ +#if !HAVE_FFTW && !HAVE_FFTW3 + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static float *Const = NULL, *iConst = NULL; +static int storesize = 0, Constsize = 0; + +static int +fftn(rvec,ivec,loglen,nskip) + +float *rvec,*ivec; +int loglen,nskip; + +{ + int n,nv2,nm1,i,j,k,l,le,le1,c,nle; + float *rveci , *rvecj , *iveci , *ivecj ; + float t,wr,wi,tr,ti ; + + if(loglen==0) + return(-1); + n=1<> 1 ; nm1=n-1 ; j=0 ; + if (storesize>=1 ; + } + j+=k ; + } + le=1 ; + for (l=0;l=n) { + im_warning("index=%d\n",i+le1); + return(-1); + } + rveci=rvec+i*nskip ; rvecj=rvec+(i+le1)*nskip; + iveci=ivec+i*nskip ; ivecj=ivec+(i+le1)*nskip; + + if (c==0) { + tr = *rvecj; + ti = *ivecj; + } + else { + tr = *rvecj*Const[c] - *ivecj*iConst[c]; + ti = *rvecj*iConst[c] + *ivecj*Const[c]; + } + *rvecj = *rveci - tr; + *ivecj = *iveci - ti; + + *rveci += tr; + *iveci += ti; + } + c += nle; + } + } +/** Division by n + for(i=0;i idealhpf, parameters: frequency cutoff + * @(#) 1 -\> ideallpf, parameters: frequency cutoff + * @(#) 2 -\> buthpf, parameters: order, frequency cutoff, amplitude cutoff + * @(#) 3 -\> butlpf, parameters: order, frequency cutoff, amplitude cutoff + * @(#) 4 -\> gaussianlpf, parameters: frequency cutoff, amplitude cutoff + * @(#) 5 -\> gaussianhpf, parameters: frequency cutoff, amplitude cutoff + * @(#) ring pass ring reject filters + * @(#) 6 -\> idealrpf, parameters: frequency cutoff, width + * @(#) 7 -\> idealrrf, parameters: frequency cutoff, width + * @(#) 8 -\> butrpf, parameters: order, freq cutoff, width, ampl cutoff + * @(#) 9 -\> butrrf, parameters: order, freq cutoff, width, ampl cutoff + * @(#) 10 -\> gaussianrpf, parameters: frequency cutoff, width, ampl cutoff + * @(#) 11 -\> gaussianrrf, parameters: frequency cutoff, width, ampl cutoff + * @(#) fractal filters (for filtering gaussian noises only) + * @(#) 18 -> fractal, parameters: fractal dimension + * @(#) + * @(#) Initially one forth of the coefficients is created and it is copied over + * @(#) the four quadrants for faster processing + * @(#) + * @(#) Functions in this file; for explanations see each function + * @(#) + * @(#) float * + * @(#) im__create_quarter( out, xs, ys, flag, ap ) + * @(#) IMAGE *out; + * @(#) int xs, ys; + * @(#) enum mask_type flag; + * @(#) va_list ap; + * @(#) + * + * Written on: Nov 1991 + * Updated on: Dec 1991 + * 20/9/95 JC + * - modernised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/************************************************************************/ +/* malloc space and create normalised coefficients accross */ +/* the x (horizontal) and y (vertical) direction. */ +/************************************************************************/ +static int +alloc( IMAGE *out, int xs, int ys, double **xd, double **yd, float **coeff ) +{ + int i; + double *x, *y; + float *c; + + x = IM_ARRAY( out, xs/2 + 1, double ); + y = IM_ARRAY( out, ys/2 + 1, double ); + c = IM_ARRAY( out, (xs/2 + 1)*(ys/2 + 1), float ); + if( !x || !y || !c ) + return( -1 ); + + for( i = 0; i < ys/2 + 1; i++ ) + y[i] = (i * i) / ((double) (ys*ys/4)); + for( i = 0; i < xs/2 + 1; i++ ) + x[i] = (i * i) / ((double) (xs*xs/4)); + *xd = x; *yd = y; *coeff = c; + + return( 0 ); +} + +/* xs and ys are the sizes of the final mask; all functions returns + * the coefficients for one forth of the final mask + */ + +/************************************************************************/ +/* FLAG = 0 */ +/* Creates an ideal high pass filter mask */ +/************************************************************************/ +static float * +ideal_hpf( IMAGE *out, int xs, int ys, double fc ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2; + + if( xs != ys || fc < 0.0 ) { + im_errormsg( "ideal_hpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "ideal_hpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 > fc2 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = 0.0; + } + + *coeff = 1.0; + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 1 */ +/* Creates an ideal low pass filter mask */ +/************************************************************************/ +static float * +ideal_lpf( IMAGE *out, int xs, int ys, double fc ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2; + + if( xs != ys || fc <= 0.0 ) { + im_errormsg( "ideal_lpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "ideal_lpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 <= fc2 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = 0.0; + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 2 */ +/* Creates an Butterworth high pass filter mask */ +/************************************************************************/ +static float * +butterworth_hpf( IMAGE *out, int xs, int ys, + double order, double fc, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2, cnst; + + if( xs != ys || fc < 0.0 || order < 1.0 || ac <= 0.0 || ac >= 1.0 ) { + im_errormsg( "butterworth_hpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "butterworth_hpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (1.0 / ac) - 1.0; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + /* Leave the dc component unaltered + */ + if( x == 0 && y == 0 ) + *cpcoeff++ = 1.0; + else { + distance2 = fc2 / (xd[x] + yd[y]); + *cpcoeff++ = 1.0 / + (1.0 + cnst * pow( distance2, order )); + } + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 3 */ +/* Creates an Butterworth low pass filter mask */ +/************************************************************************/ +static float * +butterworth_lpf( IMAGE *out, int xs, int ys, + double order, double fc, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2, cnst; + + if( xs != ys || fc <= 0.0 || order < 1.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "butterworth_lpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "butterworth_lpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (1.0/ac) - 1.0; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = (xd[x] + yd[y])/fc2; + *cpcoeff++ = 1.0 / + (1.0 + cnst * pow( distance2, order )); + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 4 */ +/* Creates a gaussian high pass filter mask */ +/************************************************************************/ +static float * +gaussian_hpf( IMAGE *out, int xs, int ys, double fc, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2, cnst; + + if( xs != ys || fc <= 0.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "gaussian_hpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "gaussian_hpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = -log( ac ); + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = (xd[x] + yd[y])/fc2; + *cpcoeff++ = 1.0 - exp( -cnst * distance2 ); + } + + *coeff = 1.0; + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 5 */ +/* Creates a gaussian low pass filter mask */ +/************************************************************************/ +static float * +gaussian_lpf( IMAGE *out, int xs, int ys, double fc, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, fc2, distance2, cnst; + + if( xs != ys || fc < 0.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "gaussian_lpf: bad args" ); + return( NULL ); + } + + if( fc > 1.0 && fc <= xs/2 ) + fc2 = fc * fc * 4.0 / (double)(xs * ys); + else if( fc <= 1.0 && fc > 0.0 ) + fc2 = fc * fc; + else { + im_errormsg( "gaussian_lpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = -log( ac ); + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = (xd[x] + yd[y])/fc2; + *cpcoeff++ = exp( - cnst * distance2 ); + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 6 */ +/* Creates an ideal ring pass filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +ideal_rpf( IMAGE *out, int xs, int ys, double fc, double width ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, df, distance2, radius1_2, radius2_2; + + if( xs != ys || fc <= 0 || width <= 0 ) { + im_errormsg( "ideal_rpf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc - df > 0.0 ) { + radius1_2 = (fc-df)*(fc-df); + radius2_2 = (fc+df)*(fc+df); + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + radius1_2 = (fc - df) * (fc - df) * 4.0 / ((double)(xs * xs)); + radius2_2 = (fc + df) * (fc + df) * 4.0 / ((double)(xs * xs)); + } + else { + im_errormsg( "ideal_rpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 < radius2_2 && distance2 > radius1_2 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = 0.0; + } + + *coeff = 1.0; + + return( coeff ); +} + + +/************************************************************************/ +/* FLAG = 7 */ +/* Creates an ideal band reject filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +ideal_rrf( IMAGE *out, int xs, int ys, double fc, double width ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, df, distance2, radius1_2, radius2_2; + + if( xs != ys || fc < 0.0 || width <= 0.0 ) { + im_errormsg( "ideal_rrf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc - df > 0.0 ) { + radius1_2 = (fc-df)*(fc-df); + radius2_2 = (fc+df)*(fc+df); + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + radius1_2 = (fc - df) * (fc - df) * 4.0 / ((double)(xs * xs)); + radius2_2 = (fc + df) * (fc + df) * 4.0 / ((double)(xs * xs)); + } + else { + im_errormsg( "ideal_rrf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 < radius2_2 && distance2 > radius1_2 ) + *cpcoeff++ = 0.0; + else + *cpcoeff++ = 1.0; + } + + return( coeff ); +} + + +/************************************************************************/ +/* FLAG = 8 */ +/* Creates a butterworth band pass filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +butterworth_rpf( IMAGE *out, int xs, int ys, + double order, double fc, double width, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, d, df, ndf, ndf2, nfc, cnst; + + if( xs != ys || fc <= 0.0 || width <= 0.0 || + order < 1.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "butterworth_rpf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc-df > 0.0 ) { + nfc = fc; + ndf = width/2.0; + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + nfc = fc * 2.0 /(double)xs; + ndf = width /(double)ys; + } + else { + im_errormsg( "butterworth_rpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (1.0/ac) - 1.0; + ndf2 = ndf * ndf; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + d = sqrt( xd[x] + yd[y] ); + *cpcoeff++ = 1.0 / + (1.0 + cnst * + pow( (d-nfc)*(d-nfc)/ndf2, order )); + } + + *coeff = 1.0; + + return( coeff ); +} + + + +/************************************************************************/ +/* FLAG = 9 */ +/* Creates a butterworth ring reject filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +butterworth_rrf( IMAGE *out, int xs, int ys, + double order, double fc, double width, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, d, df, ndf, ndf2, nfc, cnst; + + if( xs != ys || fc <= 0.0 || width <= 0.0 || + order < 1.0 || ac >= 1.0 || ac <= 0.0 ) { + im_errormsg( "butterworth_rrf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc-df > 0.0 ) { + nfc = fc; + ndf = width/2.0; + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + nfc = fc * 2.0 /(double)xs; + ndf = width /(double)ys; + } + else { + im_errormsg( "butterworth_rrf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (1.0/ac) - 1.0; + ndf2 = ndf * ndf; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + d = sqrt( xd[x] + yd[y] ); + if( d == 0.0 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = 1.0 / + (1.0 + cnst * pow( + ndf2/((d-nfc)*(d-nfc)), order )); + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 10 */ +/* Creates a gaussian band pass filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +gaussian_rpf( IMAGE *out, int xs, int ys, double fc, double width, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, d, df, ndf, ndf2, nfc, cnst; + + if( xs != ys || fc < 0.0 || width <= 0.0 || ac <= 0.0 || ac > 1.0 ) { + im_errormsg( "gaussian_rpf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc - df > 0.0 ) { + nfc = fc; + ndf = width/2.0; + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + nfc = fc * 2.0 /(double) xs; + ndf = width /(double)ys; + } + else { + im_errormsg( "gaussian_rpf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = -log( ac ); + ndf2 = ndf * ndf; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + d = sqrt( xd[x] + yd[y] ); + *cpcoeff++ = exp( -cnst * (d-nfc) * (d-nfc)/ndf2 ); + } + + *coeff = 1.0; + + return( coeff ); +} + + + + +/************************************************************************/ +/* FLAG = 11 */ +/* Creates a gaussian band reject filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static float * +gaussian_rrf( IMAGE *out, int xs, int ys, double fc, double width, double ac ) +{ + int x, y; + float *coeff, *cpcoeff; + double *xd, *yd, d, df, ndf, ndf2, nfc, cnst; + + if( xs != ys || fc < 0.0 || width <= 0.0 || ac <= 0.0 || ac > 1.0 ) { + im_errormsg( "gaussian_rrf: bad args" ); + return( NULL ); + } + + df = width/2.0; + if( fc <= 1.0 && df < 1.0 && fc - df > 0.0 ) { + nfc = fc; + ndf = width/2.0; + } + else if( fc - df > 1.0 && df >= 1.0 && fc <= xs/2 ) { + nfc = fc * 2.0 /(double) xs; + ndf = width / (double)ys; + } + else { + im_errormsg( "gaussian_rrf: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = -log( ac ); + ndf2 = ndf * ndf; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + d = sqrt( xd[x] + yd[y] ); + *cpcoeff++ = 1.0 - + exp( -cnst * (d-nfc) * (d-nfc) / ndf2 ); + } + + return( coeff ); +} + +/************************************************************************/ +/* FLAG = 18 */ +/* Theoretically the power spectrum of a fractal surface should decay + * according to its fractal dimension + * This program should be used to create fractal images by filtering the + * power spectrum of Gaussian white noise + * More specifically according to PIET: + * since the coefficients of fractal noise + * < |vsubk|^2 > decay as 1/( |f|^(beta+1) ) + * or since beta=2*H + 1, beta= 7-2*D + * < |vsubk|^2 > decay as 1/( |f|^(8-2*D) ) + * and the fractal filter which should produce vsubk + * should have transfer function decaying as 1/( |f|^((beta+1)/2) ) + * where f = sqrt(fsubx * fsubx + fsuby *fsuby) + * Finally the filter has transfer function decaying as + * sqrt(fsubx*fsubx+fsuby*fsuby)^(D-4) or + * (fsubx*fsubx+fsuby*fsuby)^((D-4)/2) <--- This relation is used. + * On the other hand if D=3-H, the filtermask should decay as + * (fsubx*fsubx+fsuby*fsuby)^(-(H+1)/2) , 0= 3.0 ) { + im_errormsg( "fractal_flt: bad args" ); + return( NULL ); + } + + if( alloc( out, xs, ys, &xd, &yd, &coeff ) ) + return( NULL ); + + cpcoeff = coeff; + cnst = (frdim - 4.0)/2.0; + for( y = 0; y < ys/2 + 1; y++ ) + for( x = 0; x < xs/2 + 1; x++ ) { + distance2 = xd[x] + yd[y]; + if( distance2 == 0.0 ) + *cpcoeff++ = 1.0; + else + *cpcoeff++ = pow( distance2, cnst ); + } + + return( coeff ); +} + +/* Creates one forth of the mask coefficients. If the final mask is + * xsize by xsize, one forth should have sizes (xsize/2 + 1) by (ysize/2 + 1) + * This happens because the horizontal spatial frequencies extend + * from -xsize/2 up to (xsize/2 - 1) inclusive and + * the vertical spatial frequencies + * from -ysize/2 up to (ysize/2 - 1) inclusive + * In order to calculate the spatial frequencies at location (x, y) + * the maximum spatial frequency at the horizontal direction xsize/2 and + * the maximum spatial frequency at the vertical direction ysize/2 have + * been normalised to 1.0. + * All arithmetic internally has been carried out in double precision; + * however all masks are written as floats with maximum value normalised to 1.0 + */ + +float * +im__create_quarter( IMAGE *out, int xs, int ys, MaskType flag, va_list ap ) +{ + /* May be fewer than 4 args ... but extract them all anyway. Should be + * safe. + */ + double p0 = va_arg( ap, double ); + double p1 = va_arg( ap, double ); + double p2 = va_arg( ap, double ); + double p3 = va_arg( ap, double ); + + switch( flag ) { + /* High pass - low pass + */ + case MASK_IDEAL_HIGHPASS: + return( ideal_hpf( out, xs, ys, p0 ) ); + + case MASK_IDEAL_LOWPASS: + return( ideal_lpf( out, xs, ys, p0 ) ); + + case MASK_BUTTERWORTH_HIGHPASS: + return( butterworth_hpf( out, xs, ys, p0, p1, p2 ) ); + + case MASK_BUTTERWORTH_LOWPASS: + return( butterworth_lpf( out, xs, ys, p0, p1, p2 ) ); + + case MASK_GAUSS_HIGHPASS: + return( gaussian_hpf( out, xs, ys, p0, p1 ) ); + + case MASK_GAUSS_LOWPASS: + return( gaussian_lpf( out, xs, ys, p0, p1 ) ); + + /* Ring pass - ring reject. + */ + case MASK_IDEAL_RINGPASS: + return( ideal_rpf( out, xs, ys, p0, p1 ) ); + + case MASK_IDEAL_RINGREJECT: + return( ideal_rrf( out, xs, ys, p0, p1 ) ); + + case MASK_BUTTERWORTH_RINGPASS: + return( butterworth_rpf( out, + xs, ys, p0, p1, p2, p3 ) ); + + case MASK_BUTTERWORTH_RINGREJECT: + return( butterworth_rrf( out, + xs, ys, p0, p1, p2, p3 ) ); + + case MASK_GAUSS_RINGPASS: + return( gaussian_rpf( out, xs, ys, p0, p1, p2 ) ); + + case MASK_GAUSS_RINGREJECT: + return( gaussian_rrf( out, xs, ys, p0, p1, p2 ) ); + + case MASK_FRACTAL_FLT: + return( fractal_flt( out, xs, ys, p0 ) ); + + default: + im_errormsg( "create_quarter: unimplemented mask" ); + return( NULL ); + } + + /*NOTREACHED*/ +} diff --git a/libsrc/freq_filt/fmaskcir.c b/libsrc/freq_filt/fmaskcir.c new file mode 100644 index 00000000..98b7fc27 --- /dev/null +++ b/libsrc/freq_filt/fmaskcir.c @@ -0,0 +1,664 @@ +/* @(#) Typical filter function + * @(#) va_list is flag, filter parameters + * @(#) + * @(#) The following masks are implemented in this file + * @(#) flag, filter shape, parameters + * @(#) band pass ring reject filters + * @(#) 12 -\> idealbpf, parameters: frequency cutoff, width + * @(#) 13 -\> idealbrf, parameters: frequency cutoff, width + * @(#) 14 -\> butbpf, parameters: order, freq cutoff, width, ampl cutoff + * @(#) 15 -\> butbrf, parameters: order, freq cutoff, width, ampl cutoff + * @(#) 16 -\> gaussianbpf, parameters: frequency cutoff, width, ampl cutoff + * @(#) 17 -\> gaussianbrf, parameters: frequency cutoff, width, ampl cutoff + * @(#) + * @(#) The whole mask is created at once and written into the image file + * @(#) + * @(#) The following functions are contained within this file: + * @(#) Details are preceding the source code of each function + * @(#) + * @(#) int im__fmaskcir( out, flag, ap) + * @(#) IMAGE *out; + * @(#) enum mask_type flag; + * @(#) va_list ap; + * @(#) + * + * Copyright: N. Dessipris, 1991 + * Written on: Nov 1991 + * Updated on: Dec 1991 + * 20/9/95 JC + * - modernised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + + +/************************************************************************ + * malloc space and create normalised coefficients accross + * the x (horizontal) and y (vertical) direction. + * xs, ys are the image sizes + * xd and yd are the scrambled distributions of x and y in the rotated + * Fourier transform + * xplusd is the non scrambled distribution of (x+x0)*(x+x0) centred at 0 + * xminus is the non scrambled distribution of (x-x0)*(x-x0) centred at 0 + * similar for yplusd and yminusd + ************************************************************************/ +static int +alloc( IMAGE *out, + int xs, int ys, + int **xd, int **yd, + int **xplusd, int **xminusd, int **yplusd, int **yminusd, + int x0, int y0, + float **line ) +{ + int i; + int *x, *y, *xp, *xm, *yp, *ym; + int *pp, *pm; + float *l; + + x = IM_ARRAY( out, xs, int ); + y = IM_ARRAY( out, ys, int ); + xp = IM_ARRAY( out, xs, int ); + xm = IM_ARRAY( out, xs, int ); + yp = IM_ARRAY( out, ys, int ); + ym = IM_ARRAY( out, ys, int ); + l = IM_ARRAY( out, xs, float ); + + if( !x || !y || !xp || !xm || !yp || !ym || !l ) + return( -1 ); + + /* if ys = 8 then y = {0,1,2,3,-4,-3,-2,-1}. + */ + for( i = 0; i < ys/2; i++ ) { + y[i] = i; + y[i+ys/2] = -ys/2 + i; + } + for( i = 0; i < xs/2; i++ ) { + x[i] = i; + x[i+xs/2] = -xs/2 + i; + } + *xd = x; + *yd = y; + + pp = yp + ys/2; + pm = ym + ys/2; + for( i = -ys/2; i < ys/2; i++ ) { + pp[i] = (i + y0)*(i + y0); + pm[i] = (i - y0)*(i - y0); + } + *yplusd = yp + ys/2; + *yminusd = ym + ys/2; + + pp = xp + xs/2; + pm = xm + xs/2; + for( i = -xs/2; i < xs/2; i++ ) { + pp[i] = (i+x0)*(i+x0); + pm[i] = (i-x0)*(i-x0); + } + *xplusd = xp + xs/2; + *xminusd = xm + xs/2; + + *line = l; + + return( 0 ); +} + +/************************************************************************/ +/* FLAG = 12 */ +/* Creates an ideal band pass filter mask */ +/* The band is two CIRCLEs of radius r centred */ +/* at (fcx, fcy) and (-fcx, -fcy) */ +/************************************************************************/ +static int +ideal_bpf( IMAGE *out, double fcx, double fcy, double r ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0, d1_2, d2_2, r2; + int y2plus, y2minus; + + if( xs != ys ) { + im_errormsg( "ideal_bpf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) < 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + r2 = r*r*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + r2 = r*r; + } + else { + im_errormsg( "ideal_bpf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + if( d1_2 <= r2 ) + *cpline = 1.0; + else if( d2_2 <= r2 ) + *cpline = 1.0; + else + *cpline = 0.0; + + if( x == 0 && y == 0 ) + *cpline = 1.0; /* allow the dc component */ + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + +/************************************************************************/ +/* FLAG = 13 */ +/* Creates an ideal band reject filter mask */ +/* The band is a CIRCLE of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static int +ideal_brf( IMAGE *out, double fcx, double fcy, double r ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0, d1_2, d2_2, r2; + int y2plus, y2minus; + + if( xs != ys ) { + im_errormsg( "ideal_brf: bad args" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + r2 = r*r*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + r2 = r*r; + } + else { + im_errormsg( "ideal_brf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + if( d1_2 <= r2 ) + *cpline = 0.0; + else if( d2_2 <= r2 ) + *cpline = 0.0; + else + *cpline = 1.0; + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + +/************************************************************************/ +/* FLAG = 14 */ +/* Creates a butterworth band pass filter mask */ +/* The band is two CIRCLES centred at (fcx, fcy) and (-fcx, -fcy) */ +/* The program assummes that the peaks of the 2d mask are at the */ +/* centres above and are set to 1.0. The amplitude of both circle mask */ +/* are added and the cuttof frequency is calculated on the plane */ +/* which passes though the centre of the circles and the 0 point */ +/************************************************************************/ +static int +butterworth_bpf( IMAGE *out, + double order, double fcx, double fcy, double r, double ac ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0; + double cnst, cnsta, d1_2, d2_2, nr2; /* nr2 is new r squared */ + int y2plus, y2minus; + + if( xs != ys || order < 1.0 ) { + im_errormsg( "butterworth_bpf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + nr2 = r*r*xs*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + nr2 = r*r; + } + else { + im_errormsg( "butterworth_bpf: bad args" ); + return( -1 ); + } + if( ac >= 1.0 || ac < 0.0) { + im_errormsg( "butterworth_bpf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + /* Filter shape: radius d0, centres at (x0, y0), (-x0,-y0) + * H(d) = H1(d) + H2(d) + * H(d) = cnst1/(1 + cnst2 * pow((d-d0)/d0, 2*order)) + + * cnst1/(1 + cnst2 * pow((d+d0)/d0, 2*order)); + * for d=+d0 H(+d0) = 1.0; for d=-d0 H(-d0) = 1.0; + * for d=+da H(+da) = ampl_cutof; for d=-da H1(-da) = ampl_cutof; + * da = (xa, ya) + * xa = x0*(1 - radius/sqrt(x0*x0+y0*y0)) + * ya = y0*(1 - radius/sqrt(x0*x0+y0*y0)) + */ + cnst = (1.0/ac) - 1.0; + + /* normalise the amplitude at (x0,y0) to 1.0 + */ + cnsta = 1.0 / (1.0 + 1.0 / + (1.0 + cnst*pow( 4.0*(x0*x0 + y0*y0)/nr2, order ))); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + *cpline = cnsta * ( + 1.0 / (1.0 + cnst * pow( d1_2/nr2, order )) + + 1.0 / (1.0 + cnst * pow( d2_2/nr2, order )) ); + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + + +/************************************************************************/ +/* FLAG = 15 */ +/* Creates a butterworth band pass filter mask */ +/* The band is a the 1-H(f) of above */ +/************************************************************************/ +static int +butterworth_brf( IMAGE *out, + double order, double fcx, double fcy, double r, double ac ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0; + double cnst, cnsta, d1_2, d2_2, nr2; /* nr2 is new r squared */ + int y2plus, y2minus; + + if( xs != ys || order < 1.0 ) { + im_errormsg( "butterworth_brf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx * xs / 2.0; + y0 = fcy * ys / 2.0; + nr2 = r*r*xs*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + nr2 = r*r; + } + else { + im_errormsg( "butterworth_brf: bad args" ); + return( -1 ); + } + if( ac >= 1.0 || ac < 0.0) { + im_errormsg( "butterworth_brf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + cnst = (1.0/ac) - 1.0; + + /* normalise the amplitude at (x0,y0) to 1.0 + */ + cnsta = 1.0 / (1.0 + 1.0 / (1.0 + + cnst * pow( 4.0*(x0*x0 + y0*y0)/nr2, order ))); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + if( d1_2 == 0.0 || d2_2 == 0.0 ) + *cpline = 0; + else + *cpline = 1.0 - cnsta * + ( 1.0/(1.0 + cnst*pow( d1_2/nr2, order )) + + 1.0/(1.0 + cnst*pow( d2_2/nr2, order )) ); + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + +/************************************************************************/ +/* FLAG = 16 */ +/* Creates a gaussian band pass filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static int +gaussian_bpf( IMAGE *out, double fcx, double fcy, double r, double ac ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0; + double cnst, cnsta, d1_2, d2_2, nr2; /* nr2 is new r squared */ + int y2plus, y2minus; + + if( xs != ys ) { + im_errormsg( "gauss_bpf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + nr2 = r*r*xs*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + nr2 = r*r; + } + else { + im_errormsg( "gauss_bpf: bad args (f)" ); + return( -1 ); + } + if( ac >= 1.0 || ac < 0.0 ) { + im_errormsg( "gauss_bpf: bad args (ac)" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + cnst = -log( ac ); + + /* normalise the amplitude at (x0,y0) to 1.0 + */ + cnsta = 1.0/(1.0 + exp( - cnst * 4.0 * (x0*x0+y0*y0) / nr2 )); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + *cpline = cnsta * + (exp( -cnst * d1_2/nr2 ) + + exp( -cnst * d2_2/nr2 )); + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + + + +/************************************************************************/ +/* FLAG = 17 */ +/* Creates a gaussian band reject filter mask */ +/* The band is a RING of internal radius fc-df and external radius fc+df*/ +/************************************************************************/ +static int +gaussian_brf( IMAGE *out, double fcx, double fcy, double r, double ac ) +{ + int x, y; + int xs = out->Xsize; + int ys = out->Ysize; + float *line, *cpline; + int *xd, *yd, *xplusx0d, *xminusx0d, *yplusy0d, *yminusy0d; + int x0, y0; + double cnst, cnsta, d1_2, d2_2, nr2; /* nr2 is new r squared */ + int y2plus, y2minus; + + if( xs != ys ) { + im_errormsg( "gauss_brf: bad sizes" ); + return( -1 ); + } + if( fabs(fcx) <= 1.0 && fabs(fcy) <= 1.0 && r > 0.0 && r < 1.0 ) { + x0 = fcx*xs / 2.0; + y0 = fcy*ys / 2.0; + nr2 = r*r*xs*xs / 4.0; + } + else if( fabs(fcx) < xs/2 && fabs(fcy) < ys/2 && r >= 1.0 ) { + x0 = fcx; + y0 = fcy; + nr2 = r * r; + } + else { + im_errormsg( "gauss_brf: bad args" ); + return( -1 ); + } + if( ac >= 1.0 || ac < 0.0 ) { + im_errormsg( "gauss_brf: bad args" ); + return( -1 ); + } + + if( alloc( out, xs, ys, + &xd, &yd, &xplusx0d, &xminusx0d, + &yplusy0d, &yminusy0d, x0, y0, &line ) ) + return( -1 ); + + cnst = -log( ac ); + + /* normalise the amplitude at (x0,y0) to 1.0 + */ + cnsta = 1.0/(1.0 + exp( - cnst * 4.0 * (x0*x0+y0*y0) / nr2 )); + + for( y = 0; y < ys; y++ ) { + cpline = line; + y2plus = yplusy0d[yd[y]]; + y2minus = yminusy0d[yd[y]]; + + for( x = 0; x < xs; x++ ) { + d1_2 = xminusx0d[xd[x]] + y2minus; + d2_2 = xplusx0d[xd[x]] + y2plus; + + *cpline = 1.0 - cnsta * + (exp( -cnst * d1_2/nr2 ) + + exp( -cnst * d2_2/nr2 )); + + if( x == 0 && y == 0 ) + *cpline = 1.0; + + cpline++; + } + + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + + +/* Creates bandpass filter masks + * All arithmetic internally has been carried out in double precision; + * however all masks are written as floats with maximum value normalised to 1.0 + */ +int +im__fmaskcir( IMAGE *out, MaskType flag, va_list ap ) +{ + /* May be fewer than 5 args ... but extract them all anyway. Should be + * safe. + */ + double p0 = va_arg( ap, double ); + double p1 = va_arg( ap, double ); + double p2 = va_arg( ap, double ); + double p3 = va_arg( ap, double ); + double p4 = va_arg( ap, double ); + + switch( flag ) { + /* Band pass - band reject. + */ + case MASK_IDEAL_BANDPASS: + return( ideal_bpf( out, p0, p1, p2 ) ); + + case MASK_IDEAL_BANDREJECT: + return( ideal_brf( out, p0, p1, p2 ) ); + + case MASK_BUTTERWORTH_BANDPASS: + return( butterworth_bpf( out, p0, p1, p2, p3, p4 ) ); + + case MASK_BUTTERWORTH_BANDREJECT: + return( butterworth_brf( out, p0, p1, p2, p3, p4 ) ); + + case MASK_GAUSS_BANDPASS: + return( gaussian_bpf( out, p0, p1, p2, p3 ) ); + + case MASK_GAUSS_BANDREJECT: + return( gaussian_brf( out, p0, p1, p2, p3 ) ); + + default: + im_errormsg( "im__fmaskcir: unimplemented mask" ); + return( -1 ); + } + + /*NOTREACHED*/ +} diff --git a/libsrc/freq_filt/freq_dispatch.c b/libsrc/freq_filt/freq_dispatch.c new file mode 100644 index 00000000..bf6bb73c --- /dev/null +++ b/libsrc/freq_filt/freq_dispatch.c @@ -0,0 +1,311 @@ +/* Function dispatch tables for freq_filt. + * + * J. Cupitt, 23/2/95 + * 22/4/97 JC + * - oops, im_freqflt() was wrong + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Args to im_create_fmask(). + */ +static im_arg_desc create_fmask_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "type" ), + IM_INPUT_DOUBLE( "p1" ), + IM_INPUT_DOUBLE( "p2" ), + IM_INPUT_DOUBLE( "p3" ), + IM_INPUT_DOUBLE( "p4" ), + IM_INPUT_DOUBLE( "p5" ) +}; + +/* Call im_create_fmask via arg vector. + */ +static int +create_fmask_vec( im_object *argv ) +{ + int width = *((int *) argv[1]); + int height = *((int *) argv[2]); + int type = *((int *) argv[3]); + double p1 = *((double *) argv[4]); + double p2 = *((double *) argv[5]); + double p3 = *((double *) argv[6]); + double p4 = *((double *) argv[7]); + double p5 = *((double *) argv[8]); + + return( im_create_fmask( argv[0], width, height, + type, p1, p2, p3, p4, p5 ) ); +} + +/* Description of im_create_fmask. + */ +static im_function create_fmask_desc = { + "im_create_fmask", /* Name */ + "create frequency domain filter mask", + 0, /* Flags */ + create_fmask_vec, /* Dispatch function */ + IM_NUMBER( create_fmask_args ), /* Size of arg list */ + create_fmask_args /* Arg list */ +}; + +/* Args to im_flt_image_freq(). + */ +static im_arg_desc flt_image_freq_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "type" ), + IM_INPUT_DOUBLE( "p1" ), + IM_INPUT_DOUBLE( "p2" ), + IM_INPUT_DOUBLE( "p3" ), + IM_INPUT_DOUBLE( "p4" ), + IM_INPUT_DOUBLE( "p5" ) +}; + +/* Call im_flt_image_freq via arg vector. + */ +static int +flt_image_freq_vec( im_object *argv ) +{ + int type = *((int *) argv[2]); + double p1 = *((double *) argv[3]); + double p2 = *((double *) argv[4]); + double p3 = *((double *) argv[5]); + double p4 = *((double *) argv[6]); + double p5 = *((double *) argv[7]); + + return( im_flt_image_freq( argv[0], argv[1], + type, p1, p2, p3, p4, p5 ) ); +} + +/* Description of im_flt_image_freq. + */ +static im_function flt_image_freq_desc = { + "im_flt_image_freq", /* Name */ + "frequency domain filter image", + 0, /* Flags */ + flt_image_freq_vec, /* Dispatch function */ + IM_NUMBER( flt_image_freq_args ), /* Size of arg list */ + flt_image_freq_args /* Arg list */ +}; + +/* Args to im_fractsurf(). + */ +static im_arg_desc fractsurf_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "size" ), + IM_INPUT_DOUBLE( "dimension" ) +}; + +/* Call im_fractsurf via arg vector. + */ +static int +fractsurf_vec( im_object *argv ) +{ + int size = *((int *) argv[1]); + double dim = *((double *) argv[2]); + + return( im_fractsurf( argv[0], size, dim ) ); +} + +/* Description of im_fractsurf. + */ +static im_function fractsurf_desc = { + "im_fractsurf", /* Name */ + "generate a fractal surface of given dimension", + IM_FN_TRANSFORM, /* Flags */ + fractsurf_vec, /* Dispatch function */ + IM_NUMBER( fractsurf_args ), /* Size of arg list */ + fractsurf_args /* Arg list */ +}; + +/* Args to im_freqflt(). + */ +static im_arg_desc freqflt_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_IMAGE( "mask" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_freqflt via arg vector. + */ +static int +freqflt_vec( im_object *argv ) +{ + return( im_freqflt( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_freqflt. + */ +static im_function freqflt_desc = { + "im_freqflt", /* Name */ + "frequency-domain filter of in with mask", + IM_FN_TRANSFORM, /* Flags */ + freqflt_vec, /* Dispatch function */ + IM_NUMBER( freqflt_args ), /* Size of arg list */ + freqflt_args /* Arg list */ +}; + +/* Call im_disp_ps via arg vector. + */ +static int +disp_ps_vec( im_object *argv ) +{ + return( im_disp_ps( argv[0], argv[1] ) ); +} + +/* Description of im_disp_ps. + */ +static im_function disp_ps_desc = { + "im_disp_ps", /* Name */ + "make displayable power spectrum", + IM_FN_TRANSFORM, /* Flags */ + disp_ps_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_rotquad via arg vector. + */ +static int +rotquad_vec( im_object *argv ) +{ + return( im_rotquad( argv[0], argv[1] ) ); +} + +/* Description of im_rotquad. + */ +static im_function rotquad_desc = { + "im_rotquad", /* Name */ + "rotate image quadrants to move origin to centre", + IM_FN_TRANSFORM, /* Flags */ + rotquad_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_fwfft via arg vector. + */ +static int +fwfft_vec( im_object *argv ) +{ + return( im_fwfft( argv[0], argv[1] ) ); +} + +/* Description of im_fwfft. + */ +static im_function fwfft_desc = { + "im_fwfft", /* Name */ + "forward fast-fourier transform", + IM_FN_TRANSFORM, /* Flags */ + fwfft_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_invfft via arg vector. + */ +static int +invfft_vec( im_object *argv ) +{ + return( im_invfft( argv[0], argv[1] ) ); +} + +/* Description of im_invfft. + */ +static im_function invfft_desc = { + "im_invfft", /* Name */ + "inverse fast-fourier transform", + IM_FN_TRANSFORM, /* Flags */ + invfft_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_invfftr via arg vector. + */ +static int +invfftr_vec( im_object *argv ) +{ + return( im_invfftr( argv[0], argv[1] ) ); +} + +/* Description of im_invfftr. + */ +static im_function invfftr_desc = { + "im_invfftr", /* Name */ + "real part of inverse fast-fourier transform", + IM_FN_TRANSFORM, /* Flags */ + invfftr_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *freq_list[] = { + &create_fmask_desc, + &disp_ps_desc, + &flt_image_freq_desc, + &fractsurf_desc, + &freqflt_desc, + &fwfft_desc, + &rotquad_desc, + &invfft_desc, + &invfftr_desc +}; + +/* Package of functions. + */ +im_package im__freq_filt = { + "freq_filt", + IM_NUMBER( freq_list ), + freq_list +}; diff --git a/libsrc/freq_filt/im_disp_ps.c b/libsrc/freq_filt/im_disp_ps.c new file mode 100644 index 00000000..9e278622 --- /dev/null +++ b/libsrc/freq_filt/im_disp_ps.c @@ -0,0 +1,107 @@ +/* @(#) Makes a displayable uchar power spectrum of an input one band image + * @(#) Input should be float complex + * @(#) All images are kept in RAM; so only square arrays of powers of + * @(#) 2 as inputs. + * @(#) Functions im_fwfft, im_c2ps, im_scaleps and im_rotquad are used + * @(#) Image descriptors should have been set properly by the calling program + * @(#) + * @(#) int im_disp_ps(in, out) + * @(#) IMAGE *in, *out; + * @(#) int bandno; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 27/03/1991 + * Modified on : + * 16/6/93 J.Cupitt + * - im_ioflag() changed to im_iocheck() + * 23/2/95 JC + * - rewritten for partials + * 10/9/98 JC + * - frees memory more quickly + * 2/4/02 JC + * - any number of bands + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +disp_ps( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + IMAGE *t[3]; + + if( im_open_local_array( out, t, 3, "im_disp_ps temp 1", "p" ) ) + return( -1 ); + + if( in->BandFmt == IM_BANDFMT_COMPLEX ) { + if( im_c2ps( in, t[1] ) ) + return( -1 ); + } + else { + if( im_fwfft( in, t[0] ) || im_c2ps( t[0], t[1] ) ) + return( -1 ); + } + + if( im_scaleps( t[1], t[2] ) || im_rotquad( t[2], out ) ) + return( -1 ); + + return( 0 ); +} + +int +im_disp_ps( IMAGE *in, IMAGE *out ) +{ + IMAGE *dummy = im_open( "memory:1", "p" ); + + if( !dummy ) + return( -1 ); + if( disp_ps( dummy, in, out ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_fractsurf.c b/libsrc/freq_filt/im_fractsurf.c new file mode 100644 index 00000000..fa85bd1d --- /dev/null +++ b/libsrc/freq_filt/im_fractsurf.c @@ -0,0 +1,87 @@ +/* @(#) Creates a vasari fractal surface of a given dimension by + * @(#) filtering white gaussian noise (function im_gaussnoise(3X)) + * @(#) using the function im_fltimage_freq(3X) + * @(#) + * @(#) Usage: + * @(#) int im_fractsurf(im, frd, size) + * @(#) double frd; + * @(#) int size; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 10/09/1991 + * Modified on: + * 20/9/95 JC + * - modernised, a little + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include /* for MASK_FRACTAL_FLT */ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_fractsurf( IMAGE *out, int size, double frd ) +{ + IMAGE *noise = im_open_local( out, "noise.v", "p" ); + + if( !noise ) + return( -1 ); + if( frd <= 2.0 || frd >= 3.0 ) { + im_errormsg( "im_fractsurf: dimension shuld be in (2,3)" ); + return( -1 ); + } + + if( im_gaussnoise( noise, size, size, 0.0, 1.0 ) ) + return( -1 ); + + /* create the fractal filter mask, and perform filtering on noise + * Note that the result is in im, stored as float since + * the original noise is in float. It needs scaling for display + */ + if( im_flt_image_freq( noise, out, MASK_FRACTAL_FLT, frd ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_freq_mask.c b/libsrc/freq_filt/im_freq_mask.c new file mode 100644 index 00000000..fe734bb1 --- /dev/null +++ b/libsrc/freq_filt/im_freq_mask.c @@ -0,0 +1,237 @@ +/* @(#) Filter functions + * &(#) va_alist is a series of double variables + * @(#) + * @(#) Used to filter image in in the frequency domain, writes + * @(#) the result in image out + * @(#) + * @(#) int im_flt_image_freq( in, out, flag, num_args, va_alist ) + * @(#) IMAGE *in, *out; + * @(#) enum mask_type flag; + * @(#) int num_args; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) Creates a filter mask used for filtering in the frequency domain + * @(#) The resultant mask is held by image + * @(#) + * @(#) int im_create_fmask(image, xsize, ysize, flag, num_args, va_alist) + * @(#) IMAGE *image; + * @(#) int xsize, ysize; + * @(#) enum mask_type flag; + * @(#) int num_args; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) Creates a filter mask used for filtering in the frequency domain + * @(#) The resultant mask is held by image + * @(#) Function im_freq_mask() differs from im_create_fmask() in the last + * @(#) argument only: the latter accepts a va_dcl whereas the former + * @(#) accepts a va_list pointer pointing to the read arguments of va_dcl + * @(#) + * @(#) int im_freq_mask(image, xs, ys, flag, num_args, ap) + * @(#) IMAGE *image; + * @(#) int xs, ys; + * @(#) enum mask_type flag; + * @(#) int num_args; + * @(#) va_list ap; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * Copyright: N. Dessipris 1991, + * Written on: Nov 1991 + * Updated on: Dec 1991 + * 20/9/95 JC + * - modernised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Create the final mask by copying the 1/4 of the mask held by coeff + * The final mask is written onto image on a line by line basis + * The buffer coeff should hold (xsize/2+1)*(ysize/2+1) elms + * The created mask is not rotated; so the center is at (0, 0) + */ +static int +copy_quarter( IMAGE *out, float *coeff_s ) +{ + float *line, *cpline; + float *coeff, *cpcoeff; + int x, y; + int hxsplus1; + + if( !(line = IM_ARRAY( out, out->Xsize, float )) ) + return( -1 ); + + hxsplus1 = out->Xsize/2 + 1; + coeff = coeff_s; + for( y = 0; y < out->Ysize/2; y++ ) { + cpline = line; + cpcoeff = coeff; coeff += hxsplus1; + + for( x = 0; x < out->Xsize/2; x++ ) + *cpline++ = *cpcoeff++; + for( x = out->Xsize/2; x < out->Xsize; x++ ) + *cpline++ = *cpcoeff--; + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + for( y = out->Ysize/2; y < out->Ysize; y++ ) { + cpline = line; + cpcoeff = coeff; coeff -= hxsplus1; + + for( x = 0; x < out->Xsize/2; x++ ) + *cpline++ = *cpcoeff++; + for( x = out->Xsize/2; x < out->Xsize; x++ ) + *cpline++ = *cpcoeff--; + if( im_writeline( y, out, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Make a mask image. + */ +static int +build_freq_mask( IMAGE *out, int xs, int ys, MaskType flag, va_list ap ) +{ + float *coeff; + extern float *im__create_quarter( IMAGE *, + int, int, MaskType, va_list ); + + /* Check sizes and create one quarter of the final mask + */ + if( !im_ispoweroftwo( xs ) || !im_ispoweroftwo( ys ) ) { + im_errormsg( "im_freq_mask: mask sizes power of 2 only" ); + return( -1 ); + } + + /* Create the output image. + */ + im_initdesc( out, xs, ys, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + switch( flag ) { + case MASK_IDEAL_HIGHPASS: + case MASK_IDEAL_LOWPASS: + case MASK_BUTTERWORTH_HIGHPASS: + case MASK_BUTTERWORTH_LOWPASS: + case MASK_GAUSS_HIGHPASS: + case MASK_GAUSS_LOWPASS: + + case MASK_IDEAL_RINGPASS: + case MASK_IDEAL_RINGREJECT: + case MASK_BUTTERWORTH_RINGPASS: + case MASK_BUTTERWORTH_RINGREJECT: + case MASK_GAUSS_RINGPASS: + case MASK_GAUSS_RINGREJECT: + + case MASK_FRACTAL_FLT: + /* All these are created as a quarter and duplicated. + */ + if( !(coeff = im__create_quarter( out, xs, ys, flag, ap )) || + copy_quarter( out, coeff ) ) + return( -1 ); + break; + + case MASK_IDEAL_BANDPASS: + case MASK_IDEAL_BANDREJECT: + case MASK_BUTTERWORTH_BANDPASS: + case MASK_BUTTERWORTH_BANDREJECT: + case MASK_GAUSS_BANDPASS: + case MASK_GAUSS_BANDREJECT: + /* Created all in one go. + */ + if( im__fmaskcir( out, flag, ap ) ) + return( -1 ); + break; + + default: + im_errormsg( "im_freq_mask: unimplemented mask type" ); + return( -1 ); + } + + return( 0 ); +} + +/* Create a mask, and filter an image with it. + */ +int +im_flt_image_freq( IMAGE *in, IMAGE *out, MaskType flag, ... ) +{ + IMAGE *mask = im_open_local( out, "tempmask", "p" ); + va_list ap; + + if( !mask ) + return( -1 ); + + /* Generate mask. + */ + va_start( ap, flag ); + if( build_freq_mask( mask, in->Xsize, in->Ysize, flag, ap ) ) + return( -1 ); + va_end( ap ); + + if( im_freqflt( in, mask, out ) ) + return( -1 ); + + return( 0 ); +} + +/* Create a filter mask. + */ +int +im_create_fmask( IMAGE *out, int xsize, int ysize, MaskType flag, ... ) +{ + va_list ap; + + va_start( ap, flag ); + if( build_freq_mask( out, xsize, ysize, flag, ap ) ) + return( -1 ); + va_end( ap ); + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_freqflt.c b/libsrc/freq_filt/im_freqflt.c new file mode 100644 index 00000000..45fe0e1e --- /dev/null +++ b/libsrc/freq_filt/im_freqflt.c @@ -0,0 +1,124 @@ +/* @(#) Functions which takes as input a valid image and filters it + * @(#) in the fourier domain with the filter mask + * @(#) Input can have any format ; output is the same as input + * @(#) imin can be char uchar, short, ushort, int, uint, float, double + * @(#) or complex float; result is the same as input, clipped if necessary. + * @(#) mask can have any format but the sizes of input and mask are equal + * @(#) The function performs float fft and if the input is not complex float + * @(#) the output is casted to the type of input according to im_clip2..() + * @(#) Since buffer images are involved the size, is restricted to 512x512 + * @(#) for the SUN4 SPARC workstation + * @(#) + * @(#) int im_freqflt(imin, mask, imout) + * @(#) IMAGE *imin, *mask, *imout; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : 08/03/1991 + * 16/6/93 J.Cupitt + * - im_multiply() called, rather than im_cmultim() + * 27/10/93 JC + * - im_clip2*() called, rather than im_any2*() + * 20/9/95 JC + * - rewritten + * 10/9/98 JC + * - frees memory more quickly + * 4/3/03 JC + * - use im_invfftr() to get real back for speedup + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_freqflt( IMAGE *in, IMAGE *mask, IMAGE *out ) +{ + IMAGE *dummy; + + /* Placeholder for memory free. + */ + if( !(dummy = im_open( "memory-1", "p" )) ) + return( -1 ); + + if( im_iscomplex( in ) ) { + /* Easy case! Assume it has already been transformed. + */ + IMAGE *t1 = im_open_local( dummy, "im_freqflt-1", "p" ); + + if( !t1 || + im_multiply( in, mask, t1 ) || + im_invfftr( t1, out ) ) { + im_close( dummy ); + return( -1 ); + } + } + else { + /* Harder - fft first, then mult, then force back to start + * type. + * + * Optimisation: output of im_invfft() is float buffer, we + * will usually chagetype to char, so rather than keeping a + * large float buffer and partial to char from that, do + * changetype to a memory buffer, and copy to out from that. + */ + IMAGE *t[3]; + IMAGE *t3; + + if( im_open_local_array( dummy, t, 3, "im_freqflt-1", "p" ) || + !(t3 = im_open_local( out, "im_freqflt-3", "t" )) || + im_fwfft( in, t[0] ) || + im_multiply( t[0], mask, t[1] ) || + im_invfftr( t[1], t[2] ) || + im_clip2fmt( t[2], t3, in->BandFmt ) || + im_copy( t3, out ) ) { + im_close( dummy ); + return( -1 ); + } + } + + im_close( dummy ); + + return( 0 ); +} + diff --git a/libsrc/freq_filt/im_fwfft.c b/libsrc/freq_filt/im_fwfft.c new file mode 100644 index 00000000..b828308a --- /dev/null +++ b/libsrc/freq_filt/im_fwfft.c @@ -0,0 +1,638 @@ +/* @(#) Does a forward fft on an input image descriptor + * @(#) using the function fft_sp. (float fft) + * @(#) Input can be any complex or no-complex; output is complex (two floats) + * @(#) + * @(#) Usage: + * @(#) int im_fwfft(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/04/1990 + * Modified on : 09/05/1990 to cope with float input + * Modified on : 08/03/1991 history removed + * Modified on : 03/04/1991 to cope with any input + * + * 28/6/95 JC + * - rewritten to use im_clip2f() rather than own code + * - memory leaks fixed + * 10/9/98 JC + * - frees memory more quickly + * 2/4/02 JC + * - fftw code added + * 13/7/02 JC + * - output Type set to IM_TYPE_FOURIER to help nip + * 27/2/03 JC + * - exploits real_to_complex() path in libfftw for real input (thanks + * Matt) for a 2x speed-up + * 17/11/03 JC + * - fix a segv for wider than high images in the real_to_complex() path + * (thanks Andrey) + * - fixes to real_to_complex() path to give the correct result for + * non-square images, including odd widths and heights + * 3/11/04 + * - added fftw3 support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#ifdef HAVE_FFTW +#include +#endif /*HAVE_FFTW*/ + +#ifdef HAVE_FFTW3 +#include +#endif /*HAVE_FFTW3*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_FFTW +/* Call rfftw for a 1 band real image. + */ +static int +rfwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + const int size = in->Xsize * in->Ysize; + const int half_width = in->Xsize / 2 + 1; + + /* Pack to double real here. + */ + IMAGE *real = im_open_local( dummy, "fwfft1:1", "t" ); + + /* Transform to halfcomplex here. + */ + double *half_complex = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + rfftwnd_plan plan; + double *buf, *q, *p; + int x, y; + + if( !real || !half_complex || im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_fwfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2d( in, real ) ) + return( -1 ); + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = rfftw2d_create_plan( in->Ysize, in->Xsize, + FFTW_FORWARD, FFTW_MEASURE | FFTW_USE_WISDOM )) ) { + im_error( "im_fwfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + rfftwnd_one_real_to_complex( plan, + (fftw_real *) real->data, (fftw_complex *) half_complex ); + + rfftwnd_destroy_plan( plan ); + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (double *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Copy to out and normalise. The right half is the up/down and + * left/right flip of the left, but conjugated. Do the first + * row separately, then mirror around the centre row. + */ + p = half_complex; + q = buf; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + p = half_complex + ((in->Xsize + 1) / 2 - 1) * 2; + + for( x = half_width; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = -1.0 * p[1] / size; + p -= 2; + q += 2; + } + + if( im_writeline( 0, out, (PEL *) buf ) ) + return( -1 ); + + for( y = 1; y < out->Ysize; y++ ) { + p = half_complex + y * half_width * 2; + q = buf; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + /* Good grief. + */ + p = half_complex + 2 * + ((out->Ysize - y + 1) * half_width - 2 + + (in->Xsize & 1)); + + for( x = half_width; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = -1.0 * p[1] / size; + p -= 2; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Call fftw for a 1 band complex image. + */ +static int +cfwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + fftwnd_plan plan; + double *buf, *q, *p; + int x, y; + + IMAGE *cmplx = im_open_local( dummy, "fwfft1:1", "t" ); + + /* Make dp complex image. + */ + if( !cmplx || im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_fwfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make the plan for the transform. + */ + if( !(plan = fftw2d_create_plan( in->Ysize, in->Xsize, + FFTW_FORWARD, + FFTW_MEASURE | FFTW_USE_WISDOM | FFTW_IN_PLACE )) ) { + im_error( "im_fwfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftwnd_one( plan, (fftw_complex *) cmplx->data, NULL ); + + fftwnd_destroy_plan( plan ); + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (double *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Copy to out, normalise. + */ + for( p = (double *) cmplx->data, y = 0; y < out->Ysize; y++ ) { + int size = out->Xsize * out->Ysize; + + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +fwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + if( im_iscomplex( in ) ) + return( cfwfft1( dummy, in, out ) ); + else + return( rfwfft1( dummy, in, out ) ); +} +#else /*!HAVE_FFTW*/ +#ifdef HAVE_FFTW3 +/* Real to complex forward transform. + */ +static int +rfwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + const int size = in->Xsize * in->Ysize; + const int half_width = in->Xsize / 2 + 1; + + /* Pack to double real here. + */ + IMAGE *real = im_open_local( dummy, "fwfft1:1", "t" ); + + /* Transform to halfcomplex here. + */ + double *half_complex = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + /* We have to have a separate real buffer for the planner to work on. + */ + double *planner_scratch = IM_ARRAY( dummy, + in->Xsize * in->Ysize, double ); + + fftw_plan plan; + double *buf, *q, *p; + int x, y; + + if( !real || !half_complex || im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_fwfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2d( in, real ) ) + return( -1 ); + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. Use a separate scratch buffer for the + * planner, we can't overwrite real->data + */ + if( !(plan = fftw_plan_dft_r2c_2d( in->Ysize, in->Xsize, + planner_scratch, (fftw_complex *) half_complex, + 0 )) ) { + im_error( "im_fwfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftw_execute_dft_r2c( plan, + (double *) real->data, (fftw_complex *) half_complex ); + + fftw_destroy_plan( plan ); + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (double *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Copy to out and normalise. The right half is the up/down and + * left/right flip of the left, but conjugated. Do the first + * row separately, then mirror around the centre row. + */ + p = half_complex; + q = buf; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + p = half_complex + ((in->Xsize + 1) / 2 - 1) * 2; + + for( x = half_width; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = -1.0 * p[1] / size; + p -= 2; + q += 2; + } + + if( im_writeline( 0, out, (PEL *) buf ) ) + return( -1 ); + + for( y = 1; y < out->Ysize; y++ ) { + p = half_complex + y * half_width * 2; + q = buf; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + /* Good grief. + */ + p = half_complex + 2 * + ((out->Ysize - y + 1) * half_width - 2 + + (in->Xsize & 1)); + + for( x = half_width; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = -1.0 * p[1] / size; + p -= 2; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Complex to complex forward transform. + */ +static int +cfwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + fftw_plan plan; + double *buf, *q, *p; + int x, y; + + IMAGE *cmplx = im_open_local( dummy, "fwfft1:1", "t" ); + + /* We have to have a separate buffer for the planner to work on. + */ + double *planner_scratch = IM_ARRAY( dummy, + in->Xsize * in->Ysize * 2, double ); + + /* Make dp complex image. + */ + if( !cmplx || im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_fwfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make the plan for the transform. + */ + if( !(plan = fftw_plan_dft_2d( in->Ysize, in->Xsize, + (fftw_complex *) planner_scratch, + (fftw_complex *) planner_scratch, + FFTW_FORWARD, + 0 )) ) { + im_error( "im_fwfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftw_execute_dft( plan, + (fftw_complex *) cmplx->data, (fftw_complex *) cmplx->data ); + + fftw_destroy_plan( plan ); + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_DPCOMPLEX; + out->BandFmt = IM_BANDFMT_DPCOMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (double *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Copy to out, normalise. + */ + for( p = (double *) cmplx->data, y = 0; y < out->Ysize; y++ ) { + int size = out->Xsize * out->Ysize; + + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[0] = p[0] / size; + q[1] = p[1] / size; + p += 2; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +fwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + if( im_iscomplex( in ) ) + return( cfwfft1( dummy, in, out ) ); + else + return( rfwfft1( dummy, in, out ) ); +} +#else /*!HAVE_FFTW3*/ +/* Transform a 1 band image with vips's built-in fft routine. + */ +static int +fwfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + int size = in->Xsize * in->Ysize; + int bpx = im_ispoweroftwo( in->Xsize ); + int bpy = im_ispoweroftwo( in->Ysize ); + float *buf, *q, *p1, *p2; + int x, y; + + /* Buffers for real and imaginary parts. + */ + IMAGE *real = im_open_local( dummy, "fwfft1:1", "t" ); + IMAGE *imag = im_open_local( dummy, "fwfft1:2", "t" ); + + /* Temporaries. + */ + IMAGE *t1 = im_open_local( dummy, "fwfft1:3", "p" ); + + if( !real || !imag || !t1 ) + return( -1 ); + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 || + im_iscomplex( in ) ) { + im_error( "im_fwfft", + _( "one band non-complex uncoded only" ) ); + return( -1 ); + } + if( !bpx || !bpy ) { + im_error( "im_fwfft", _( "sides must be power of 2" ) ); + return( -1 ); + } + + /* Make sure we have a float input image. + */ + if( im_clip2f( in, real ) ) + return( -1 ); + + /* Make a buffer of 0 floats of the same size for the imaginary part. + */ + if( im_black( t1, in->Xsize, in->Ysize, 1 ) ) + return( -1 ); + if( im_clip2f( t1, imag ) ) + return( -1 ); + + /* Transform! + */ + if( im__fft_sp( (float *) real->data, (float *) imag->data, + bpx - 1, bpy - 1 ) ) { + im_error( "im_fwfft", _( "fft_sp failed" ) ); + return( -1 ); + } + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_COMPLEX; + out->BandFmt = IM_BANDFMT_COMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (float *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Gather together real and imag parts. We have to normalise output! + */ + for( p1 = (float *) real->data, p2 = (float *) imag->data, + y = 0; y < out->Ysize; y++ ) { + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[0] = *p1++ / size; + q[1] = *p2++ / size; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} +#endif /*HAVE_FFTW3*/ +#endif /*HAVE_FFTW*/ + +/* Transform an n-band image with a 1-band processing function. + */ +int +im__fftproc( IMAGE *dummy, IMAGE *in, IMAGE *out, im__fftproc_fn fn ) +{ + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + + if( in->Bands == 1 ) { + if( fn( dummy, in, out ) ) + return( -1 ); + } + else { + IMAGE *acc; + int b; + + for( acc = NULL, b = 0; b < in->Bands; b++ ) { + IMAGE *t1 = im_open_local( dummy, + "fwfftn:1", "p" ); + IMAGE *t2 = im_open_local( dummy, + "fwfftn:2", "p" ); + + if( !t1 || !t2 || + im_extract_band( in, t1, b ) || + fn( dummy, t1, t2 ) ) + return( -1 ); + + if( !acc ) + acc = t2; + else { + IMAGE *t3 = im_open_local( dummy, + "fwfftn:3", "p" ); + + if( !t3 || im_bandjoin( acc, t2, t3 ) ) + return( -1 ); + + acc = t3; + } + } + + if( im_copy( acc, out ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_fwfft( IMAGE *in, IMAGE *out ) +{ + IMAGE *dummy = im_open( "im_fwfft:1", "p" ); + + if( !dummy ) + return( -1 ); + if( im__fftproc( dummy, in, out, fwfft1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + /* Set type hint. + */ + out->Type = IM_TYPE_FOURIER; + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_invfft.c b/libsrc/freq_filt/im_invfft.c new file mode 100644 index 00000000..289e7119 --- /dev/null +++ b/libsrc/freq_filt/im_invfft.c @@ -0,0 +1,280 @@ +/* @(#) Does a inverse fft on an input image descriptor + * @(#) using the function fft_sp. + * @(#) Input complex (2 floats) output complex (2 floats) + * @(#) + * @(#) Usage: + * @(#) int im_invfft(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/04/1990 + * Modified on : + * 28/6/95 JC + * - rewritten, based on new im_fwfft() code + * 10/9/98 JC + * - frees memory more quickly + * 2/4/02 JC + * - fftw code added + * 13/7/02 JC + * - Type reset + * 27/2/03 JC + * - tiny speed-up ... save 1 copy on write + * 22/1/04 JC + * - oops, fix for segv on wider than high fftw transforms + * 3/11/04 + * - added fftw3 support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#ifdef HAVE_FFTW +#include +#endif /*HAVE_FFTW*/ + +#ifdef HAVE_FFTW3 +#include +#endif /*HAVE_FFTW3*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_FFTW +/* Call fftw for a 1 band image. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + fftwnd_plan plan; + + IMAGE *cmplx = im_open_local( out, "invfft1:1", "t" ); + + /* Make dp complex image. + */ + if( !cmplx || im_pincheck( in ) || im_poutcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_invfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = fftw2d_create_plan( in->Ysize, in->Xsize, + FFTW_BACKWARD, + FFTW_MEASURE | FFTW_USE_WISDOM | FFTW_IN_PLACE )) ) { + im_error( "im_invfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftwnd_one( plan, (fftw_complex *) cmplx->data, NULL ); + + fftwnd_destroy_plan( plan ); + + /* Copy to out. + */ + if( im_copy( cmplx, out ) ) + return( -1 ); + + return( 0 ); +} +#else /*!HAVE_FFTW*/ +#ifdef HAVE_FFTW3 +/* Complex to complex inverse transform. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + fftw_plan plan; + + IMAGE *cmplx = im_open_local( out, "invfft1:1", "t" ); + + /* We have to have a separate buffer for the planner to work on. + */ + double *planner_scratch = IM_ARRAY( dummy, + in->Xsize * in->Ysize * 2, double ); + + /* Make dp complex image. + */ + if( !cmplx || im_pincheck( in ) || im_poutcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_invfft", _( "one band uncoded only" ) ); + return( -1 ); + } + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = fftw_plan_dft_2d( in->Ysize, in->Xsize, + (fftw_complex *) planner_scratch, + (fftw_complex *) planner_scratch, + FFTW_BACKWARD, + 0 )) ) { + im_error( "im_invfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftw_execute_dft( plan, + (fftw_complex *) cmplx->data, (fftw_complex *) cmplx->data ); + + fftw_destroy_plan( plan ); + + /* Copy to out. + */ + if( im_copy( cmplx, out ) ) + return( -1 ); + + return( 0 ); +} +#else /*!HAVE_FFTW3*/ +/* Fall back to VIPS's built-in fft + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + int bpx = im_ispoweroftwo( in->Xsize ); + int bpy = im_ispoweroftwo( in->Ysize ); + float *buf, *q, *p1, *p2; + int x, y; + + /* Buffers for real and imaginary parts. + */ + IMAGE *real = im_open_local( dummy, "invfft1:1", "t" ); + IMAGE *imag = im_open_local( dummy, "invfft1:2", "t" ); + + /* Temps. + */ + IMAGE *t1 = im_open_local( dummy, "invfft1:3", "p" ); + IMAGE *t2 = im_open_local( dummy, "invfft1:4", "p" ); + + if( !real || !imag || !t1 ) + return( -1 ); + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || + in->Bands != 1 || !im_iscomplex( in ) ) { + im_error( "im_invfft", _( "one band complex uncoded only" ) ); + return( -1 ); + } + if( !bpx || !bpy ) { + im_error( "im_invfft", _( "sides must be power of 2" ) ); + return( -1 ); + } + + /* Make sure we have a single-precision complex input image. + */ + if( im_clip2cm( in, t1 ) ) + return( -1 ); + + /* Extract real and imag parts. We have to complement the imaginary. + */ + if( im_c2real( t1, real ) ) + return( -1 ); + if( im_c2imag( t1, t2 ) || im_lintra( -1.0, t2, 0.0, imag ) ) + return( -1 ); + + /* Transform! + */ + if( im__fft_sp( (float *) real->data, (float *) imag->data, + bpx - 1, bpy - 1 ) ) { + im_error( "im_invfft", _( "fft_sp failed" ) ); + return( -1 ); + } + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_COMPLEX; + out->BandFmt = IM_BANDFMT_COMPLEX; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (float *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Gather together real and imag parts. + */ + for( p1 = (float *) real->data, p2 = (float *) imag->data, + y = 0; y < out->Ysize; y++ ) { + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[0] = *p1++; + q[1] = *p2++; + q += 2; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} +#endif /*HAVE_FFTW3*/ +#endif /*HAVE_FFTW*/ + +int +im_invfft( IMAGE *in, IMAGE *out ) +{ + IMAGE *dummy = im_open( "im_invfft:1", "p" ); + + if( !dummy ) + return( -1 ); + if( im__fftproc( dummy, in, out, invfft1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + if( out->Bands == 1 ) + out->Type = IM_TYPE_B_W; + else + out->Type = IM_TYPE_MULTIBAND; + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_invfftr.c b/libsrc/freq_filt/im_invfftr.c new file mode 100644 index 00000000..2d458d46 --- /dev/null +++ b/libsrc/freq_filt/im_invfftr.c @@ -0,0 +1,330 @@ +/* @(#) Does a inverse fft on an input image descriptor + * @(#) Input complex (2 floats) output real + * @(#) + * @(#) Usage: + * @(#) int im_invfftr(in, out) + * @(#) IMAGE *in, *out; + * @(#) + * + * Modified on : + * 27/2/03 JC + * - from im_invfft.c + * 22/1/04 JC + * - oops, fix for segv on wider than high fftw transforms + * 3/11/04 + * - added fftw3 support + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#ifdef HAVE_FFTW +#include +#endif /*HAVE_FFTW*/ + +#ifdef HAVE_FFTW3 +#include +#endif /*HAVE_FFTW3*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_FFTW +/* Use fftw2. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + IMAGE *cmplx = im_open_local( dummy, "invfft1-1", "t" ); + IMAGE *real = im_open_local( out, "invfft1-2", "t" ); + const int half_width = in->Xsize / 2 + 1; + + /* Transform to halfcomplex here. + */ + double *half_complex = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + rfftwnd_plan plan; + int x, y; + double *q, *p; + + if( !cmplx || !real || !half_complex || im_pincheck( in ) || + im_poutcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_invfft", _( "one band uncoded only" ) ); + return( -1 ); + } + + /* Make dp complex image for input. + */ + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make mem buffer real image for output. + */ + if( im_cp_desc( real, in ) ) + return( -1 ); + real->Bbits = IM_BBITS_DOUBLE; + real->BandFmt = IM_BANDFMT_DOUBLE; + if( im_setupout( real ) ) + return( -1 ); + + /* Build half-complex image. + */ + q = half_complex; + for( y = 0; y < cmplx->Ysize; y++ ) { + p = ((double *) cmplx->data) + y * in->Xsize * 2; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + p += 2; + q += 2; + } + } + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = rfftw2d_create_plan( in->Ysize, in->Xsize, + FFTW_BACKWARD, FFTW_MEASURE | FFTW_USE_WISDOM )) ) { + im_error( "im_invfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + rfftwnd_one_complex_to_real( plan, + (fftw_complex *) half_complex, (fftw_real *) real->data ); + + rfftwnd_destroy_plan( plan ); + + /* Copy to out. + */ + if( im_copy( real, out ) ) + return( -1 ); + + return( 0 ); +} +#else /*!HAVE_FFTW*/ +#ifdef HAVE_FFTW3 +/* Complex to real inverse transform. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + IMAGE *cmplx = im_open_local( dummy, "invfft1-1", "t" ); + IMAGE *real = im_open_local( out, "invfft1-2", "t" ); + const int half_width = in->Xsize / 2 + 1; + + /* Transform to halfcomplex here. + */ + double *half_complex = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + /* We have to have a separate real buffer for the planner to work on. + */ + double *planner_scratch = IM_ARRAY( dummy, + in->Ysize * half_width * 2, double ); + + fftw_plan plan; + int x, y; + double *q, *p; + + if( !cmplx || !real || !half_complex || im_pincheck( in ) || + im_poutcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) { + im_error( "im_invfft", _( "one band uncoded only" ) ); + return( -1 ); + } + + /* Make dp complex image for input. + */ + if( im_clip2dcm( in, cmplx ) ) + return( -1 ); + + /* Make mem buffer real image for output. + */ + if( im_cp_desc( real, in ) ) + return( -1 ); + real->Bbits = IM_BBITS_DOUBLE; + real->BandFmt = IM_BANDFMT_DOUBLE; + if( im_setupout( real ) ) + return( -1 ); + + /* Build half-complex image. + */ + q = half_complex; + for( y = 0; y < cmplx->Ysize; y++ ) { + p = ((double *) cmplx->data) + y * in->Xsize * 2; + + for( x = 0; x < half_width; x++ ) { + q[0] = p[0]; + q[1] = p[1]; + p += 2; + q += 2; + } + } + + /* Make the plan for the transform. Yes, they really do use nx for + * height and ny for width. + */ + if( !(plan = fftw_plan_dft_c2r_2d( in->Ysize, in->Xsize, + (fftw_complex *) planner_scratch, (double *) real->data, + 0 )) ) { + im_error( "im_invfft", _( "unable to create transform plan" ) ); + return( -1 ); + } + + fftw_execute_dft_c2r( plan, + (fftw_complex *) half_complex, (double *) real->data ); + + fftw_destroy_plan( plan ); + + /* Copy to out. + */ + if( im_copy( real, out ) ) + return( -1 ); + + return( 0 ); +} +#else /*!HAVE_FFTW3*/ +/* Fall back to vips's built-in fft. + */ +static int +invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out ) +{ + int bpx = im_ispoweroftwo( in->Xsize ); + int bpy = im_ispoweroftwo( in->Ysize ); + float *buf, *q, *p1; + int x, y; + + /* Buffers for real and imaginary parts. + */ + IMAGE *real = im_open_local( dummy, "invfft1:1", "t" ); + IMAGE *imag = im_open_local( dummy, "invfft1:2", "t" ); + + /* Temps. + */ + IMAGE *t1 = im_open_local( dummy, "invfft1:3", "p" ); + IMAGE *t2 = im_open_local( dummy, "invfft1:4", "p" ); + + if( !real || !imag || !t1 ) + return( -1 ); + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || + in->Bands != 1 || !im_iscomplex( in ) ) { + im_error( "im_invfft", _( "one band complex uncoded only" ) ); + return( -1 ); + } + if( !bpx || !bpy ) { + im_error( "im_invfft", _( "sides must be power of 2" ) ); + return( -1 ); + } + + /* Make sure we have a single-precision complex input image. + */ + if( im_clip2cm( in, t1 ) ) + return( -1 ); + + /* Extract real and imag parts. We have to complement the imaginary. + */ + if( im_c2real( t1, real ) ) + return( -1 ); + if( im_c2imag( t1, t2 ) || im_lintra( -1.0, t2, 0.0, imag ) ) + return( -1 ); + + /* Transform! + */ + if( im__fft_sp( (float *) real->data, (float *) imag->data, + bpx - 1, bpy - 1 ) ) { + im_error( "im_invfft", _( "fft_sp failed" ) ); + return( -1 ); + } + + /* WIO to out. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bbits = IM_BBITS_FLOAT; + out->BandFmt = IM_BANDFMT_FLOAT; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = (float *) IM_ARRAY( dummy, + IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + /* Just write real part. + */ + for( p1 = (float *) real->data, y = 0; y < out->Ysize; y++ ) { + q = buf; + + for( x = 0; x < out->Xsize; x++ ) { + q[x] = *p1++; + } + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} +#endif /*HAVE_FFTW3*/ +#endif /*HAVE_FFTW*/ + +int +im_invfftr( IMAGE *in, IMAGE *out ) +{ + IMAGE *dummy = im_open( "im_invfft:1", "p" ); + + if( !dummy ) + return( -1 ); + if( im__fftproc( dummy, in, out, invfft1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + if( out->Bands == 1 ) + out->Type = IM_TYPE_B_W; + else + out->Type = IM_TYPE_MULTIBAND; + + return( 0 ); +} diff --git a/libsrc/freq_filt/im_rotquad.c b/libsrc/freq_filt/im_rotquad.c new file mode 100644 index 00000000..d4361356 --- /dev/null +++ b/libsrc/freq_filt/im_rotquad.c @@ -0,0 +1,103 @@ +/* @(#) Shifts the four quadrants of a fourier transform for display + * @(#) Any number of bands, any coding, any band format + * @(#) Works on images with even sizes + * @(#) Output is the same as the input + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_rotquad( in, out ) + * @(#) IMAGE *in, *out; + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 12/04/1990 + * Modified on : 09/05/1991 + * Modified on : 09/06/1992, J.Cupitt. + * - now works for any type, any number of bands. + * - uses bcopy instead of a loop: mucho faster. + * now uses memcpy - for Sys5 compat K.Martinez 29/4/92 + * 5/8/93 JC + * - some ANSIfication + * 28/6/95 JC + * - some more modernisation + * 11/7/02 JC + * - redone in term of extract()/insert(), for great partialisation + * 14/4/04 + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_rotquad( IMAGE *in, IMAGE *out ) +{ + IMAGE *t[6]; + int xd = in->Xsize / 2; + int yd = in->Ysize / 2; + + if( in->Xsize < 2 || in->Ysize < 2 ) + return( im_copy( in, out ) ); + + if( im_open_local_array( out, t, 6, "im_rotquad-1", "p" ) || + /* Extract 4 areas. + */ + im_extract_area( in, t[0], 0, 0, xd, yd ) || + im_extract_area( in, t[1], xd, 0, in->Xsize - xd, yd ) || + im_extract_area( in, t[2], 0, yd, xd, in->Ysize - yd ) || + im_extract_area( in, t[3], xd, yd, + in->Xsize - xd, in->Ysize - yd ) || + + /* Reassemble, rotated. + */ + im_insert( t[3], t[2], t[4], in->Xsize - xd, 0 ) || + im_insert( t[1], t[0], t[5], in->Xsize - xd, 0 ) || + im_insert( t[4], t[5], out, 0, in->Ysize - yd ) ) + return( -1 ); + + out->Xoffset = xd; + out->Yoffset = yd; + + return( 0 ); +} diff --git a/libsrc/freq_filt/man3/Makefile.am b/libsrc/freq_filt/man3/Makefile.am new file mode 100644 index 00000000..4ed28f0b --- /dev/null +++ b/libsrc/freq_filt/man3/Makefile.am @@ -0,0 +1,13 @@ +man_MANS = \ + im_create_fmask.3 \ + im_disp_ps.3 \ + im_flt_imag_freq.3 \ + im_fractsurf.3 \ + im_freqflt.3 \ + im_fwfft.3 \ + im_invfft.3 \ + im_invfftr.3 \ + im_rotquad.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/freq_filt/man3/im_create_fmask.3 b/libsrc/freq_filt/man3/im_create_fmask.3 new file mode 100644 index 00000000..a10c37b3 --- /dev/null +++ b/libsrc/freq_filt/man3/im_create_fmask.3 @@ -0,0 +1,359 @@ +.TH IM_FMASKPROF 3 "8 Oct 1991" +.SH NAME +im_create_fmask \- create a frequency domain filter mask according to args +.SH SYNOPSIS +.B #include + +int +.br +im_create_fmask( IMAGE *out, int xs, int ys, int type, double p1, ... ) + +.SH DESCRIPTION +im_create_fmask() +creates a float one band image mask. Sizes xs and ys must be powers of 2, +and must be square. Non-square masks may be added in a future version. + +There are 18 types of filter mask supported in this VIPS - they are listed +below. For each type, you are expected to give the correct number of +additional parameters. See the table. + +.br +----------------------------------------------------------- +.br +| Filter mask; type; num_args; parameters | +.br +----------------------------------------------------------- +.br +| Ideal high pass; 0; 1; fc | +.br +| Ideal low pass; 1; 1; fc | +.br +| Butterworth high pass; 2; 3; order, fc, ac | +.br +| Butterworth low pass; 3; 3; order, fc, ac | +.br +| Gaussian low pass; 4; 2; fc, ac | +.br +| Gaussian high pass; 5; 2; fc, ac | +.br +| | +.br +| Ideal ring pass; 6; 2; fc, width | +.br +| Ideal ring reject; 7; 2; fc, width | +.br +| Butterworth ring pass; 8; 4; order, fc, width, ac | +.br +| Butterworth ring reject; 9; 4; order, fc, width, ac | +.br +| Gaussian ring pass; 10; 3; fc, width, ac | +.br +| Gaussian ring reject; 11; 3; fc, width, ac | +.br +| | +.br +| Ideal band pass; 12; 3; fcx, fcy, r | +.br +| Ideal band reject; 13; 3; fcx, fcy, r | +.br +| Butterworth band pass; 14; 5; order, fcx, fcy, r, ac | +.br +| Butterworth band reject; 15; 5; order, fcx, fcy, r, ac | +.br +| Gaussian band pass; 16; 4; fcx, fcy, r, ac | +.br +| Gaussian band reject; 17; 4; fcx, fcy, r, ac | +.br +| | +.br +| fractal filter mask; 18; 1; fractal_dimension | +.br +----------------------------------------------------------- + +All masks are created with the four quadrants rotated so the (0,0) dc component +is at the top left corner of the image. In order to view a mask, +the four quadrants must be rotated +(im_rotquad(3)) and scaled (im_scale(3)). If the masks +are used for filtering in the frequency domain, there is no need for rotation. +Function im_flt_imag_freq(3) creates a mask and filter a square image in the +frequency domain. + +As a matter of convention the positive x axis is from left to right while the +positive y axis is from top to bottom (on the image with the frequency (0,0) +close to the centre i.e the four quadrants rotated). +All produced filters are float images with the maximum value normalised to 1.0. +Ideal and Butterworth filters are given in the book by Gonzalez and Wintz. + +HIGH PASS - LOW PASS FILTER MASKS (flag: 0 to 5) + +A high pass filter mask filters the low frequencies while allowing the high +frequencies to get through. The reverse happens with a low pass +filter mask. The transition is controlled by the frequency +cutoff (fc). All masks are circularly symmetric and they are creating +by duplicating one forth of them. + +Ideal high pass/low pass (argno=1): + +The variable fc determines the frequency cutoff which can be given either as +percentage of the max spatial frequency (normalised by convention to 1.0) or +in pixels. In the latter case it is assumed that the input image is +square and that the maximum spatial frequency +corresponds to xs/2 points horizontally and and ys/2 points vertically. +The following line of code creates an ideal circularly symmetric +high pass filter mask: + +im_create_fmask(im, 128, 128, 0, .5); + +with all values above half the max spatial frequency +(corresponding to 32 pixels) set to 1.0 and the remaining set to 0.0. +The dc value (corresponding to the frequency (0,0)) is set to 1.0. +When the mask is properly scaled and has its four quadrants rotated it is a +black circle within a white square. The radius of the circle is +determined by fc which is .5*max_spatial_frequency that is, for the example +above .5*64=32. +The centre of the circle is set to 1.0 (white), in order to allow +the dc component to pass unaltered. +A circularly symmetric ideal low pass filter mask is constructed in a similar +way. + +Butterworth high pass/low pass (argno=3): + +Each mask needs three arguments: the order, fc and ac. Order corresponds to +the order of the Butterworth filter mask, fc is the frequency cutoff and +ac is the amplitude cutoff. The same conventions are valid for both fc and ac +as for the ideal high pass and low pass filter mask. +The amplitude cutoff is determined by ac and corresponds to the percentage +of the maximum amplitude at fc. The maximum amplitude is always +normalised to 1.0. +If the transfer function of the filter is H(r) then H(fc) = ac*H(0). +The transfer function at frequency (0,0) is also set to 1.0. + +The transfer function of the Butterworth high pass is: +.br +H(r)=1.0/(1.0+(1.0/ac-1.0)*pow((fc*fc)/(r*r),order)). +.br +For a Butterworth low pass: +.br +H(r)=1.0/(1.0+(1.0/ac-1.0)*pow((r*r)/(fc*fc),order)). +.br +Both masks are given in Gonzalez and Wintz (Digital Image Processing, 2nd edn, +1987). +By increasing the order, the filter becomes steeper introducing ringing. + +Gaussian high pass/low pass (argno=2): + +Each of these masks needs 2 arguments: fc and ac. For both arguments the same +conventions as for the Butterworth mask are valid. The transfer function +of a Gaussian high pass filter mask is given by the equation: +.br +H(r) = 1.0 - exp(log(ac)*r*r/(fc*fc)). +.br +The corresponding mask for a Gaussian high pass is: +.br +H(f) = exp(log(ac)*r*r/(fc*fc)). +.br +ac being the amplitude cutoff. +.br + + +RING PASS - RING REJECT FILTER MASKS (flag: 6 to 11) + +A circularly symmetric ring pass filter mask allows all +frequencies within a ring, to pass while blocking all other frequencies. +The ring is specified by its width and it radius which corresponds to fc +the frequency cutoff. The fc is centred within the width and, therefore, +the ring starts at point fc-width/2 up to fc+width/2 along the positive +horizontal x axis. The reverse happens with a low pass +filter mask. The transition is controlled by the frequency +cutoff (fc). All masks are circularly symmetric and they are creating +by duplicating one forth of them. + +Ideal ring pass/ring reject filter masks (argno=2): + +An ideal ring pass filter mask has two arguments, the width and the frequency +cutoff. The created mask when properly rotated, +is a white ring of internal radius fc-df +and external radius fc+df, on a black square. All band pass values +within the ring are set to 1.0 while the remaining band reject frequencies +are set to 0.0. The (0,0) frequency component is set to 1.0. +Both fc and width must be either between 0.0 and 1.0, or between 1.0 and +xs/2. If both are between 0.0 and 1.0 then the program normalises then to the +maximum spatial frequency which is xs/2=ys/2. + +An ideal ring reject filter mask is the reverse of the ideal ring pass filter +mask, that is it allows all frequencies to get through apart from the +frequencies within a ring specified by the args of the function, +in a similar way as the ideal ring pass filter. + +Butterworth ring pass/ring reject filter masks (argno=4): + +.br +Each of these masks has 4 arguments: the order of the filter (order), +the frequency cutoff (fc), the width (width) and the amplitude cutoff (ac). +.br +A Butterworth ring pass filter mask is a circularly symmetric ring shape mask. +The profile of the mask along the horizontal positive axis is a shifted +low pass Butterworth filter mask, with the maximum value set to 1.0. +This mask is similar to the ideal ring pass but the transition +between band pass and band reject zones instead of a sharp brick +wall, is a shifted Butterworth low pass filter +mask. The transfer function of the mask is given by the equation: +.br +H(r)=1./(1.+cnst*pow(((r-fc)*(r-fc)/(w2)),order)) +.br +where cnst=1/ac, w2 = width*width/4. +.br +Both fc and width should be either between 0.0 and 1.0 or between 1.0 and xs/2 +as in the case of the ideal ring pass or ring reject mask. The amplitude +cutoff should be always between 0.0 and 1.0. It should be noted that: +.br +H(fc+df)=H(fc-df)=ac*H(fc) +.br +The value of H(0) at frequency (0,0) has been set to 1.0 in order to allow +the dc component of the image to pass unaltered. + +For the case of the Butterworth ring reject filter mask, its transfer function +is given by the equation: +.br +H(r)=1./(1.+cnst*pow((w2/((r-fc)*(r-fc))),order)) +.br +where cnst=1/ac, w2 = width*width/4. +.br + +Gaussian ring pass/ring reject filter masks (argno=3): + +Each of these masks takes three arguments: the frequency cutoff (fc), the width +(width) and the amplitude cutoff (ac). The conventions for the arguments +are the same as for the Butterworth ring pass and ring reject masks above; +however the order is not needed. + +The transfer function of a Gaussian ring pass filter mask is: +.br +H(r)=exp(log(ac)*(r-fc) * (r-fc)/w2) +.br +where w2 = width*width/4. +.br + +BAND PASS - BAND REJECT MASKS (flag:13 to 17) + +These filter masks are used in order to eliminate spatial frequencies +around a given frequency. Since the masks must be symmetrical +with respect to the origin, they cannot be realised by creating +one forth and replicating it four times. + +Ideal band pass/band reject filter masks (argno=3): + +An ideal band reject filter mask takes three arguments: the coordinates +of the centre of the one circle (fcx,fcy) and its radius r. The produced +filter mask has all values 0.0 except two disks centred at (fcx,fcy), +(-fcx,-fcy) each one having radius r. The two disks have values of 1.0. +The value of the mask corresponding to (0,0) spatial frequency, as also +set to 1.0. + +All three arguments fcx, fcy and r should be either between 0 and 1 or +between 1 and the max spatial frequency which is xs/2=ys/2. In the +case that the arguments are between 0.0 and 1.0 they are interpreted +as percentage of the maximum spatial frequency. For the case of band +pass filter masks the value of the (0,0) frequency is set to 1.0 so that +the dc component can pass unaltered. + +Butterworth band pass/band reject filter masks (argno=4): + +A Butterworth band pass/band reject +filter mask allows/rejects spatial frequencies +around a given frequency. The mask consists of the sum of two +Butterworth shape filters centered at (fcx,fcy) and (-fcx,-fcy). +The shape of each mask is determined by the parameters of the function. +The arguments fcx, fcy and r obey the same conventions as for those +of the ideal band pass / band reject masks. The transfer function of the +filter at point (0,0) is set to 1.0. + +The function works by adding the two Butterworth masks. +As a result, if the whole mask is normalised with respect to +frequency (fcx,fcy), the cutoff frequency at (fcx+||r||,fcy+||r||) will +be different to that of (fcx-||r||,fcy-||r||), since the tail of +the mask centered at (-fcx,-fcy) will give a different contribution +to (fcx+||r||,fcy+||r||) than that to (fcx-||r||,fcy-||r||). +In order to simplify the calculations, the function estimates the +amplitude at a cutoff frequency ((fcx-||r||,fcy-||r||) as if the contribution +comes only from the mask centred at (fcx,fcy). The side effect of this +approach is that for big values of r the cutoff frequency of the filter mask +is different at frequencies (fcx+||r||,fcy+||r||) and (fcx+||r||,fcy+||r||). + +More specifically, given that each disk has a Butterworth shape of radius r +with centres at (x0, y0) and (-x0,-y0), +the transfer function of a Butterworth band pass filter +mask is given by the equation: +.br +H(d)= { H1(d) + H2(d) } +.br +H1(d) = cnst1/(1 + cnst2 * pow((d-d0)/r, 2*order)) +.br +H2(d) = cnst1/(1 + cnst2 * pow((d+d0)/r, 2*order)) +.br +where +.br +cnst1=1./(1.+1./(1.+cnst1*pow(d02/((r/2)*(r/2)),order))) +.br +cnst2=1./ac - 1., +.br +d02 = x0*x0+y0*y0. +.br +With this configuration for d=+d0, H(+d0) = 1.0; for d=-d0 H(-d0) = 1.0. +If da=(xa,ya), then for d=+da, H1(+da)=ac and for d=-da, H1(-da)=ac. In the +latter case it is assumed that xa=x0*(1-radius/sqrt(x0*x0+y0*y0)) and that +ya=y0*(1-radius/sqrt(x0*x0+y0*y0)). + +The transfer function of a Butterworth band reject filter H_bbr(d) is given +by the equation: +.br +H_bbr(d) = 1.0 - H_bbp(d), +.br +where H_bbp(d) is the transfer function of the Butterworth bandpass filter +defined above. + +Gaussian band pass/band reject filter masks (argno=3): + +For a Gaussian band pass or band reject filter mask, similar conventions +to those of the Butterworth filter masks, are valid however the order as an +argument is not needed. + +The transfer function of a Gaussian band pass filter mask is given by the +equation +.br +H(d)= { H1(d) + H2(d) } +.br +H1(d) = cnst1 * exp(-cnst2 * (d-d0)*(d-d0)/(r*r)) +.br +H1(d) = cnst1 * exp(-cnst2 * (d+d0)*(d+d0)/(r*r)) +.br +where +.br +cnst1=1/( 1+exp(-cnst*d02/((r/2)*(r/2))) ), +.br +d02 = x0*x0+y0*y0 and cnst2=-log(ac). + +The transfer function of a Gaussian band reject filter H_gbr(d) is given +by the equation: +.br +H_gbr(d) = 1.0 - H_gbp(d), +.br +where H_gbp(d) is the transfer function of the Gaussian bandpass filter +defined above. + +FRACTAL FILTER MASK (flag:18) + +The fractal filter mask should be used only to filter square images of +white Gaussian noise in order to create fractal surfaces of a given fractal +dimension. The fractal dimension should be between 2.0 and 3.0. The produced +mask has a power spectrum which decays according to the rule entered by the +parameter fractal dimension. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_flt_image_freq(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/08/1991 diff --git a/libsrc/freq_filt/man3/im_disp_ps.3 b/libsrc/freq_filt/man3/im_disp_ps.3 new file mode 100644 index 00000000..33040ded --- /dev/null +++ b/libsrc/freq_filt/man3/im_disp_ps.3 @@ -0,0 +1,32 @@ +.TH IM_DISP_PS 3 "23 May 1991" +.SH NAME +im_disp_ps \- creates a displayable power spectrum of an one band image +.SH SYNOPSIS +.B #include + +.B int im_disp_ps(in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +im_disp_ps() creates a displayable power spectrum of the image held by the +image descriptor in. The resultant unsigned char image is written on the +image descriptor out. Input should be one band image of any non-complex +type. + +The function finds the fourier transform of the input (if in is not complex) +and then the amplitude power spectrum is created (im_c2ps(3)). The power +spectrum is passed through a non-linear transformation (im_scaleps(3)). The +final image has the four quadrants rotated in such a way that the coordinate +(0,0) is near the centre of the image (im_rotquad(3)). + +Input should be a square image with size power of 2. +The maximum size of the input image is dictated by the available RAM. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_fwfft(3), im_c2ps(3), im_scaleps(3), im_rotquad(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 23/05/1991 diff --git a/libsrc/freq_filt/man3/im_flt_imag_freq.3 b/libsrc/freq_filt/man3/im_flt_imag_freq.3 new file mode 100644 index 00000000..3242a947 --- /dev/null +++ b/libsrc/freq_filt/man3/im_flt_imag_freq.3 @@ -0,0 +1,27 @@ +.TH IM_FMASKPROF 3 "8 Oct 1991" +.SH NAME +im_flt_imag_freq \- filter a square image in the frequency domain +.SH SYNOPSIS +#include + +int +.br +im_flt_imag_freq( IMAGE *in, IMAGE *out, int flag, double p1, ... ) + +.SH DESCRIPTION +im_flt_imag_freq() +filters an image in the frequency domain using a mask dictated by the flag +and the parameters p1, ... The mask is created by using the +function im_create_fmask(3). After creating the mask the program filters +the input image using the function im_freqflt(3). + +For details about the arguments refer to the function im_create_fmask(3). + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_freqflt(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/08/1991 diff --git a/libsrc/freq_filt/man3/im_fractsurf.3 b/libsrc/freq_filt/man3/im_fractsurf.3 new file mode 100644 index 00000000..3fdeca34 --- /dev/null +++ b/libsrc/freq_filt/man3/im_fractsurf.3 @@ -0,0 +1,29 @@ +.TH IM_FRACTSURF 3 "10 May 1991" +.SH NAME +im_fractsurf \- creates a fractal surface +.SH SYNOPSIS +.B #include + +.B int im_fractsurf(in, frdim, size) +.br +.B IMAGE *in; +.br +.B double frdim; +.br +.B int size; +.SH DESCRIPTION +.B im_fractsurf +creates a fracta surface. The program initially creates a gaussian +noise image (im_gausnoise(3)) of sizes size x size. The image is then +filtered (im_flt_image_freq(3)) in order to force the power spectrum to +decay according to the desired fractal dimension frdim. The result is +a one band float image and should be scaled for display. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_flt_image_freq(3), im_gausnoise(3) +.SH COPYRIGHT +.br +N. Dessipris, +.SH AUTHOR +N. Dessipris \- 06/07/1990 diff --git a/libsrc/freq_filt/man3/im_freqflt.3 b/libsrc/freq_filt/man3/im_freqflt.3 new file mode 100644 index 00000000..5f6a5f41 --- /dev/null +++ b/libsrc/freq_filt/man3/im_freqflt.3 @@ -0,0 +1,35 @@ +.TH IM_FOURFLT 3 "10 May 1991" +.SH NAME +im_freqflt \- filters an image with a float filter mask in the frequency domain +.SH SYNOPSIS +.B #include + +.B int im_freqflt(in, filtermask, out) +.br +IMAGE *in, *filtermask, *out; +.SH DESCRIPTION +im_freqflt() performs filtering in the frequency domain of the input image +held by the image descriptor in with a mask held by the descriptor filtermask +and writes the result on the image descriptor out. + +Image sizes should be power of two and less or equal to 512. All images +should be one channel square images. Image filtermask is a non-complex one +channel image created by im_create_fmask(). Input image can be any type. If +input is complex, in and filtermask are multiplied using the function +im_cmultim(3). + +If input is not complex then it is transformed into the frequency domain and +then it is multiplied with the filtermask. In the latter case the result is +inverse fourier transformed and clipped to the input image format using the +function im_clip2fmt(3). +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH BUGS +The function has not been fully tested. +.SH SEE\ ALSO +im_fwfft(3), im_invfft(3), im_create_fmask(3), im_cmultim(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/freq_filt/man3/im_fwfft.3 b/libsrc/freq_filt/man3/im_fwfft.3 new file mode 100644 index 00000000..ba26cbeb --- /dev/null +++ b/libsrc/freq_filt/man3/im_fwfft.3 @@ -0,0 +1,60 @@ +.TH IM_FWFFT 3 "14 May 1991" +.SH NAME +im_fwfft, im_invfft, im_invfftr \- forward and inverse fft on an image +.SH SYNOPSIS +.B #include + +.B int im_fwfft(in, out) +.br +.B IMAGE *in, *out; + +.B int im_invfft(in, out) +.br +.B IMAGE *in, *out; + +.B int im_invfftr(in, out) +.br +.B IMAGE *in, *out; + +.SH DESCRIPTION +.B im_fwfft() +performs a forward fast Fourier Transform on the image held by the +image descriptor in and writes the result to the image descriptor out. +The image can be in any format and have any number of bands. The output is +always complex. + +If VIPS has been built with support for libfftw, a high-speed FFT library, +then fftwnd_one() is used to compute the transform. This produces a double +precision complex result. The first transformation at a particular image +size will be very slow as libfftw optimises itself for your machine, +but subsequent transforms of images of that size are extremely fast. +Unfortunately, libfftw does not have good out-of-memory behaviour. If you +try to transform a very large image, your program will exit abruptly. + +If VIPS has not been built with libfftw support, VIPS uses its own fft +routines. These are rather slow, are single precision only, and can only +transform images whose sides are a power of two. + +.B im_invfft() +performs the reverse transform. +The input image must be complex, the output is always complex. The image may +have any number of bands. + +Again, if libfftw was present when VIPS was +compiled, that library is used to calculate the transform. + +.B im_invfftr() +performs the reverse transform. +The input image must be complex, the output is always real. The image may +have any number of bands. It is about 2 x faster than +.B im_invfft(). + +Again, if libfftw was present when VIPS was +compiled, that library is used to calculate the transform. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_rotquad(3), im_c2ps(3), im_scaleps(3), im_disp_ps(3). +.SH COPYRIGHT +1995, National Gallery and Birkbeck College diff --git a/libsrc/freq_filt/man3/im_invfft.3 b/libsrc/freq_filt/man3/im_invfft.3 new file mode 100644 index 00000000..1111213f --- /dev/null +++ b/libsrc/freq_filt/man3/im_invfft.3 @@ -0,0 +1 @@ +.so man3/im_fwfft.3 diff --git a/libsrc/freq_filt/man3/im_invfftr.3 b/libsrc/freq_filt/man3/im_invfftr.3 new file mode 100644 index 00000000..1111213f --- /dev/null +++ b/libsrc/freq_filt/man3/im_invfftr.3 @@ -0,0 +1 @@ +.so man3/im_fwfft.3 diff --git a/libsrc/freq_filt/man3/im_rotquad.3 b/libsrc/freq_filt/man3/im_rotquad.3 new file mode 100644 index 00000000..d2fc6bea --- /dev/null +++ b/libsrc/freq_filt/man3/im_rotquad.3 @@ -0,0 +1,28 @@ +.TH IM_ROTQUAD 3 "07 July 1990" +.SH NAME +im_rotquad \- rotates the four quadrants of an image +.SH SYNOPSIS +.B #include + +.B int im_rotquad(in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +im_rotquad rotates the four quadrants of the image held by the image +descriptor in and writes the result on the image descriptor out. + +The function is used primarily to rotate a fourier transform in such a way +that the coordinate (0,0) is near the centre of the image, the so-called +optical transform. + +The function operates on any input; +output has the same format, sizes and bands as input. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_fwfft(3), im_invfft(3), im_scaleps(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 06/07/1990 diff --git a/libsrc/histograms_lut/Makefile.am b/libsrc/histograms_lut/Makefile.am new file mode 100644 index 00000000..b98669f0 --- /dev/null +++ b/libsrc/histograms_lut/Makefile.am @@ -0,0 +1,25 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libhistograms_lut.la + +libhistograms_lut_la_SOURCES = \ + hist_dispatch.c \ + im_gammacorrect.c \ + im_heq.c \ + im_hist.c \ + im_histeq.c \ + im_histgr.c \ + im_histnD.c \ + im_histplot.c \ + im_histspec.c \ + im_hsp.c \ + im_identity.c \ + im_invertlut.c \ + im_lhisteq.c \ + im_maplut.c \ + im_buildlut.c \ + im_project.c \ + im_stdif.c \ + tone.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/histograms_lut/hist_dispatch.c b/libsrc/histograms_lut/hist_dispatch.c new file mode 100644 index 00000000..f49044b6 --- /dev/null +++ b/libsrc/histograms_lut/hist_dispatch.c @@ -0,0 +1,811 @@ +/* VIPS function dispatch tables for histogram_lut. + * + * J. Cupitt, 24/5/95. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One image in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Args for im_gammacorrect. + */ +static im_arg_desc gammacorrect_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "exponent" ) +}; + +/* Call im_gammacorrect via arg vector. + */ +static int +gammacorrect_vec( im_object *argv ) +{ + double exp = *((double *) argv[2]); + + return( im_gammacorrect( argv[0], argv[1], exp ) ); +} + +/* Description of im_gammacorrect. + */ +static im_function gammacorrect_desc = { + "im_gammacorrect", /* Name */ + "gamma-correct image", /* Description */ + IM_FN_PIO, /* Flags */ + gammacorrect_vec, /* Dispatch function */ + IM_NUMBER( gammacorrect_args ), /* Size of arg list */ + gammacorrect_args /* Arg list */ +}; + +/* Image plus number in, image out. + */ +static im_arg_desc heq_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "band_number" ) +}; + +/* Call im_heq via arg vector. + */ +static int +heq_vec( im_object *argv ) +{ + int bn = *((int *) argv[2]); + + return( im_heq( argv[0], argv[1], bn ) ); +} + +/* Description of im_heq. + */ +static im_function heq_desc = { + "im_heq", /* Name */ + "histogram-equalise image", /* Description */ + IM_FN_PIO, /* Flags */ + heq_vec, /* Dispatch function */ + IM_NUMBER( heq_args ), /* Size of arg list */ + heq_args /* Arg list */ +}; + +/* Call im_hist via arg vector. + */ +static int +hist_vec( im_object *argv ) +{ + int bn = *((int *) argv[2]); + + return( im_hist( argv[0], argv[1], bn ) ); +} + +/* Description of im_hist. + */ +static im_function hist_desc = { + "im_hist", /* Name */ + "find and graph histogram of image", /* Description */ + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + hist_vec, /* Dispatch function */ + IM_NUMBER( heq_args ), /* Size of arg list */ + heq_args /* Arg list */ +}; + +/* Call im_histcum via arg vector. + */ +static int +histcum_vec( im_object *argv ) +{ + return( im_histcum( argv[0], argv[1] ) ); +} + +/* Description of im_histcum. + */ +static im_function histcum_desc = { + "im_histcum", /* Name */ + "turn histogram to cumulative histogram",/* Description */ + IM_FN_PIO, /* Flags */ + histcum_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_histnorm via arg vector. + */ +static int +histnorm_vec( im_object *argv ) +{ + return( im_histnorm( argv[0], argv[1] ) ); +} + +/* Description of im_histcum. + */ +static im_function histnorm_desc = { + "im_histnorm", /* Name */ + "form normalised histogram",/* Description */ + IM_FN_PIO, /* Flags */ + histnorm_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_histeq via arg vector. + */ +static int +histeq_vec( im_object *argv ) +{ + return( im_histeq( argv[0], argv[1] ) ); +} + +/* Description of im_histeq. + */ +static im_function histeq_desc = { + "im_histeq", /* Name */ + "form histogram equalistion LUT",/* Description */ + IM_FN_PIO, /* Flags */ + histeq_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_histgr via arg vector. + */ +static int +histgr_vec( im_object *argv ) +{ + int bn = *((int *) argv[2]); + + return( im_histgr( argv[0], argv[1], bn ) ); +} + +/* Description of im_histgr. + */ +static im_function histgr_desc = { + "im_histgr", /* Name */ + "find histogram of image", /* Description */ + IM_FN_TRANSFORM, /* Flags */ + histgr_vec, /* Dispatch function */ + IM_NUMBER( heq_args ), /* Size of arg list */ + heq_args /* Arg list */ +}; + +/* Call im_histnD() via arg vector. + */ +static int +histnD_vec( im_object *argv ) +{ + int bins = *((int *) argv[2]); + + return( im_histnD( argv[0], argv[1], bins ) ); +} + +/* Args for im_histnD(). + */ +static im_arg_desc histnD_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "bins" ) +}; + +/* Description of im_histnD(). + */ +static im_function histnD_desc = { + "im_histnD", /* Name */ + "find 1D, 2D or 3D histogram of image", /* Description */ + IM_FN_TRANSFORM, /* Flags */ + histnD_vec, /* Dispatch function */ + IM_NUMBER( histnD_args ), /* Size of arg list */ + histnD_args /* Arg list */ +}; + +/* Call im_histplot via arg vector. + */ +static int +histplot_vec( im_object *argv ) +{ + return( im_histplot( argv[0], argv[1] ) ); +} + +/* Description of im_histplot. + */ +static im_function histplot_desc = { + "im_histplot", /* Name */ + "plot graph of histogram", /* Description */ + IM_FN_PIO, /* Flags */ + histplot_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Args for im_histspec. + */ +static im_arg_desc histspec_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_INPUT_IMAGE( "ref" ), + IM_OUTPUT_IMAGE( "out" ), +}; + +/* Call im_histspec via arg vector. + */ +static int +histspec_vec( im_object *argv ) +{ + return( im_histspec( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_histspec. + */ +static im_function histspec_desc = { + "im_histspec", /* Name */ + "find histogram which will make pdf of in match ref", + 0, /* Flags */ + histspec_vec, /* Dispatch function */ + IM_NUMBER( histspec_args ), /* Size of arg list */ + histspec_args /* Arg list */ +}; + +/* Call im_hsp via arg vector. + */ +static int +hsp_vec( im_object *argv ) +{ + return( im_hsp( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_hsp. + */ +static im_function hsp_desc = { + "im_hsp", /* Name */ + "match stats of in to stats of ref", + 0, /* Flags */ + hsp_vec, /* Dispatch function */ + IM_NUMBER( histspec_args ), /* Size of arg list */ + histspec_args /* Arg list */ +}; + +/* Args for im_identity. + */ +static im_arg_desc identity_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "nbands" ) +}; + +/* Call im_identity via arg vector. + */ +static int +identity_vec( im_object *argv ) +{ + int nb = *((int *) argv[1]); + + return( im_identity( argv[0], nb ) ); +} + +/* Description of im_identity. + */ +static im_function identity_desc = { + "im_identity", /* Name */ + "generate identity histogram", + 0, /* Flags */ + identity_vec, /* Dispatch function */ + IM_NUMBER( identity_args ), /* Size of arg list */ + identity_args /* Arg list */ +}; + +/* Args for im_identity_ushort. + */ +static im_arg_desc identity_ushort_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "nbands" ), + IM_INPUT_INT( "size" ) +}; + +/* Call im_identity_ushort via arg vector. + */ +static int +identity_ushort_vec( im_object *argv ) +{ + int nb = *((int *) argv[1]); + int sz = *((int *) argv[2]); + + return( im_identity_ushort( argv[0], nb, sz ) ); +} + +/* Description of im_identity_ushort. + */ +static im_function identity_ushort_desc = { + "im_identity_ushort", /* Name */ + "generate ushort identity histogram", + 0, /* Flags */ + identity_ushort_vec, /* Dispatch function */ + IM_NUMBER( identity_ushort_args ), /* Size of arg list */ + identity_ushort_args /* Arg list */ +}; + +/* Args for im_lhisteq. + */ +static im_arg_desc lhisteq_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ) +}; + +/* Call im_lhisteq via arg vector. + */ +static int +lhisteq_vec( im_object *argv ) +{ + int xw = *((int *) argv[2]); + int yw = *((int *) argv[3]); + + return( im_lhisteq( argv[0], argv[1], xw, yw ) ); +} + +/* Description of im_lhisteq. + */ +static im_function lhisteq_desc = { + "im_lhisteq", /* Name */ + "local histogram equalisation", + IM_FN_PIO, /* Flags */ + lhisteq_vec, /* Dispatch function */ + IM_NUMBER( lhisteq_args ), /* Size of arg list */ + lhisteq_args /* Arg list */ +}; + +/* Call im_lhisteq_raw via arg vector. + */ +static int +lhisteq_raw_vec( im_object *argv ) +{ + int xw = *((int *) argv[2]); + int yw = *((int *) argv[3]); + + return( im_lhisteq_raw( argv[0], argv[1], xw, yw ) ); +} + +/* Description of im_lhisteq_raw. + */ +static im_function lhisteq_raw_desc = { + "im_lhisteq_raw", /* Name */ + "local histogram equalisation, no border", + IM_FN_PIO, /* Flags */ + lhisteq_raw_vec, /* Dispatch function */ + IM_NUMBER( lhisteq_args ), /* Size of arg list */ + lhisteq_args /* Arg list */ +}; + +/* Args for im_maplut. + */ +static im_arg_desc maplut_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMAGE( "lut" ) +}; + +/* Call im_maplut via arg vector. + */ +static int +maplut_vec( im_object *argv ) +{ + return( im_maplut( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_maplut. + */ +static im_function maplut_desc = { + "im_maplut", /* Name */ + "map image through LUT", + IM_FN_PIO, /* Flags */ + maplut_vec, /* Dispatch function */ + IM_NUMBER( maplut_args ), /* Size of arg list */ + maplut_args /* Arg list */ +}; + +/* Call im_project() via arg vector. + */ +static int +project_vec( im_object *argv ) +{ + return( im_project( argv[0], argv[1], argv[2] ) ); +} + +/* Args for im_project(). + */ +static im_arg_desc project_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "hout" ), + IM_OUTPUT_IMAGE( "vout" ) +}; + +/* Description of im_project(). + */ +static im_function project_desc = { + "im_project", /* Name */ + "find horizontal and vertical projections of an image", + IM_FN_TRANSFORM, /* Flags */ + project_vec, /* Dispatch function */ + IM_NUMBER( project_args ), /* Size of arg list */ + project_args /* Arg list */ +}; + +/* Args for im_stdif. + */ +static im_arg_desc stdif_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_DOUBLE( "m0" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_DOUBLE( "s0" ), + IM_INPUT_INT( "xw" ), + IM_INPUT_INT( "yw" ) +}; + +/* Call im_stdif via arg vector. + */ +static int +stdif_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double m0 = *((double *) argv[3]); + double b = *((double *) argv[4]); + double s0 = *((double *) argv[5]); + int xw = *((int *) argv[6]); + int yw = *((int *) argv[7]); + + return( im_stdif( argv[0], argv[1], a, m0, b, s0, xw, yw ) ); +} + +/* Description of im_stdif. + */ +static im_function stdif_desc = { + "im_stdif", /* Name */ + "statistical differencing", + IM_FN_PIO, /* Flags */ + stdif_vec, /* Dispatch function */ + IM_NUMBER( stdif_args ), /* Size of arg list */ + stdif_args /* Arg list */ +}; + +/* Call im_stdif_raw via arg vector. + */ +static int +stdif_raw_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double m0 = *((double *) argv[3]); + double b = *((double *) argv[4]); + double s0 = *((double *) argv[5]); + int xw = *((int *) argv[6]); + int yw = *((int *) argv[7]); + + return( im_stdif_raw( argv[0], argv[1], a, m0, b, s0, xw, yw ) ); +} + +/* Description of im_stdif. + */ +static im_function stdif_raw_desc = { + "im_stdif_raw", /* Name */ + "statistical differencing, no border", + IM_FN_PIO, /* Flags */ + stdif_raw_vec, /* Dispatch function */ + IM_NUMBER( stdif_args ), /* Size of arg list */ + stdif_args /* Arg list */ +}; + +/* Args for im_buildlut. + */ +static im_arg_desc buildlut_args[] = { + IM_INPUT_DMASK( "xyes" ), + IM_OUTPUT_IMAGE( "lut" ) +}; + +/* Call im_buildlut via arg vector. + */ +static int +buildlut_vec( im_object *argv ) +{ + im_mask_object *mi = argv[0]; + + return( im_buildlut( mi->mask, argv[1] ) ); +} + +/* Description of im_buildlut. + */ +static im_function buildlut_desc = { + "im_buildlut", /* Name */ + "generate LUT table from set of x/y positions", + 0, /* Flags */ + buildlut_vec, /* Dispatch function */ + IM_NUMBER( buildlut_args ),/* Size of arg list */ + buildlut_args /* Arg list */ +}; + +/* Args for im_invertlut. + */ +static im_arg_desc invertlut_args[] = { + IM_INPUT_DMASK( "measures" ), + IM_OUTPUT_IMAGE( "lut" ), + IM_INPUT_INT( "lut_size" ) +}; + +/* Call im_invertlut via arg vector. + */ +static int +invertlut_vec( im_object *argv ) +{ + im_mask_object *mi = argv[0]; + int lut_size = *((int *) argv[2]); + + return( im_invertlut( mi->mask, argv[1], lut_size ) ); +} + +/* Description of im_invertlut. + */ +static im_function invertlut_desc = { + "im_invertlut", /* Name */ + "generate correction table from set of measures", + 0, /* Flags */ + invertlut_vec, /* Dispatch function */ + IM_NUMBER( invertlut_args ),/* Size of arg list */ + invertlut_args /* Arg list */ +}; + +/* Args for im_tone_build. + */ +static im_arg_desc tone_build_args[] = { + IM_OUTPUT_IMAGE( "hist" ), + IM_INPUT_DOUBLE( "Lb" ), + IM_INPUT_DOUBLE( "Lw" ), + IM_INPUT_DOUBLE( "Ps" ), + IM_INPUT_DOUBLE( "Pm" ), + IM_INPUT_DOUBLE( "Ph" ), + IM_INPUT_DOUBLE( "S" ), + IM_INPUT_DOUBLE( "M" ), + IM_INPUT_DOUBLE( "H" ) +}; + +/* Call im_tone_build via arg vector. + */ +static int +tone_build_vec( im_object *argv ) +{ + double Lb = *((double *) argv[1]); + double Lw = *((double *) argv[2]); + double Ps = *((double *) argv[3]); + double Pm = *((double *) argv[4]); + double Ph = *((double *) argv[5]); + double S = *((double *) argv[6]); + double M = *((double *) argv[7]); + double H = *((double *) argv[8]); + + return( im_tone_build( argv[0], Lb, Lw, Ps, Pm, Ph, S, M, H ) ); +} + +/* Description of im_tone_build. + */ +static im_function tone_build_desc = { + "im_tone_build", /* Name */ + "create LUT for tone adjustment of LabS images", + 0, /* Flags */ + tone_build_vec, /* Dispatch function */ + IM_NUMBER( tone_build_args ), /* Size of arg list */ + tone_build_args /* Arg list */ +}; + +/* Args for im_tone_build_range. + */ +static im_arg_desc tone_build_range_args[] = { + IM_OUTPUT_IMAGE( "hist" ), + IM_INPUT_INT( "in_max" ), + IM_INPUT_INT( "out_max" ), + IM_INPUT_DOUBLE( "Lb" ), + IM_INPUT_DOUBLE( "Lw" ), + IM_INPUT_DOUBLE( "Ps" ), + IM_INPUT_DOUBLE( "Pm" ), + IM_INPUT_DOUBLE( "Ph" ), + IM_INPUT_DOUBLE( "S" ), + IM_INPUT_DOUBLE( "M" ), + IM_INPUT_DOUBLE( "H" ) +}; + +/* Call im_tone_build_range via arg vector. + */ +static int +tone_build_range_vec( im_object *argv ) +{ + int in_max = *((int *) argv[1]); + int out_max = *((int *) argv[2]); + double Lb = *((double *) argv[3]); + double Lw = *((double *) argv[4]); + double Ps = *((double *) argv[5]); + double Pm = *((double *) argv[6]); + double Ph = *((double *) argv[7]); + double S = *((double *) argv[8]); + double M = *((double *) argv[9]); + double H = *((double *) argv[10]); + + return( im_tone_build_range( argv[0], in_max, out_max, + Lb, Lw, Ps, Pm, Ph, S, M, H ) ); +} + +/* Description of im_tone_build_range. + */ +static im_function tone_build_range_desc = { + "im_tone_build_range", /* Name */ + "create LUT for tone adjustment", + 0, /* Flags */ + tone_build_range_vec, /* Dispatch function */ + IM_NUMBER( tone_build_range_args ),/* Size of arg list */ + tone_build_range_args /* Arg list */ +}; + +/* Args for im_tone_analyse. + */ +static im_arg_desc tone_analyse_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "hist" ), + IM_INPUT_DOUBLE( "Ps" ), + IM_INPUT_DOUBLE( "Pm" ), + IM_INPUT_DOUBLE( "Ph" ), + IM_INPUT_DOUBLE( "S" ), + IM_INPUT_DOUBLE( "M" ), + IM_INPUT_DOUBLE( "H" ) +}; + +/* Call im_tone_analyse via arg vector. + */ +static int +tone_analyse_vec( im_object *argv ) +{ + double Ps = *((double *) argv[2]); + double Pm = *((double *) argv[3]); + double Ph = *((double *) argv[4]); + double S = *((double *) argv[5]); + double M = *((double *) argv[6]); + double H = *((double *) argv[7]); + + return( im_tone_analyse( argv[0], argv[1], Ps, Pm, Ph, S, M, H ) ); +} + +/* Description of im_tone_analyse. + */ +static im_function tone_analyse_desc = { + "im_tone_analyse", /* Name */ + "analyse in and create LUT for tone adjustment", + 0, /* Flags */ + tone_analyse_vec, /* Dispatch function */ + IM_NUMBER( tone_analyse_args ), /* Size of arg list */ + tone_analyse_args /* Arg list */ +}; + +/* Args for im_ismonotonic. + */ +static im_arg_desc ismonotonic_args[] = { + IM_INPUT_IMAGE( "lut" ), + IM_OUTPUT_INT( "mono" ) +}; + +/* Call im_ismonotonic via arg vector. + */ +static int +ismonotonic_vec( im_object *argv ) +{ + int *res = (int *) argv[1]; + + return( im_ismonotonic( argv[0], res ) ); +} + +/* Description of im_ismonotonic. + */ +static im_function ismonotonic_desc = { + "im_ismonotonic", /* Name */ + "test LUT for monotonicity", + 0, /* Flags */ + ismonotonic_vec, /* Dispatch function */ + IM_NUMBER( ismonotonic_args ), /* Size of arg list */ + ismonotonic_args /* Arg list */ +}; + +/* Args for im_tone_map + */ +static im_arg_desc tone_map_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMAGE( "lut" ) +}; + +/* Call im_tone_map via arg vector. + */ +static int +tone_map_vec( im_object *argv ) +{ + return( im_tone_map( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_tone_map. + */ +static im_function tone_map_desc = { + "im_tone_map", /* Name */ + "map L channel of LabS or LabQ image through LUT", + IM_FN_PIO, /* Flags */ + tone_map_vec, /* Dispatch function */ + IM_NUMBER( tone_map_args ),/* Size of arg list */ + tone_map_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *hist_list[] = { + &gammacorrect_desc, + &heq_desc, + &hist_desc, + &histcum_desc, + &histeq_desc, + &histgr_desc, + &histnD_desc, + &histnorm_desc, + &histplot_desc, + &histspec_desc, + &hsp_desc, + &identity_desc, + &identity_ushort_desc, + &ismonotonic_desc, + &lhisteq_desc, + &lhisteq_raw_desc, + &invertlut_desc, + &buildlut_desc, + &maplut_desc, + &project_desc, + &stdif_desc, + &stdif_raw_desc, + &tone_analyse_desc, + &tone_build_desc, + &tone_build_range_desc, + &tone_map_desc +}; + +/* Package of functions. + */ +im_package im__histograms_lut = { + "histograms_lut", + IM_NUMBER( hist_list ), + hist_list +}; diff --git a/libsrc/histograms_lut/im_buildlut.c b/libsrc/histograms_lut/im_buildlut.c new file mode 100644 index 00000000..5c878936 --- /dev/null +++ b/libsrc/histograms_lut/im_buildlut.c @@ -0,0 +1,241 @@ +/* @(#) Build a LUT from a set of x/y points. Eg. if input is + * @(#) + * @(#) 12 100 + * @(#) 14 110 + * @(#) 18 120 + * @(#) + * @(#) we generate + * @(#) + * @(#) 100 (12) + * @(#) 105 + * @(#) 110 + * @(#) 112.5 + * @(#) 115 + * @(#) 117.5 + * @(#) 120 (18) + * @(#) + * @(#) The x/y points don't need to be sorted: we do that. You can have + * @(#) several Ys ... each becomes a band in the output LUT. + * + * Written on: 26/9/06 + * - from im_invertlut() + * 9/10/06 + * - don't output x values + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +/* Our state. + */ +typedef struct _State { + DOUBLEMASK *input; /* Input mask */ + int lut_size; /* Number of output elements to generate */ + double **data; /* Rows of unpacked matrix */ +} State; + +/* Use this to sort our input rows by the first column. + */ +static int +compare( const void *a, const void *b ) +{ + double **r1 = (double **) a; + double **r2 = (double **) b; + + double diff = r1[0][0] - r2[0][0]; + + if( diff > 0 ) + return( 1 ); + else if( diff == 0 ) + return( 0 ); + else + return( -1 ); +} + +/* Free our state. + */ +static void +free_state( State *state ) +{ + if( state->data ) { + int i; + + for( i = 0; i < state->input->ysize; i++ ) + if( state->data[i] ) { + im_free( state->data[i] ); + state->data[i] = NULL; + } + + im_free( state->data ); + state->data = NULL; + } +} + +/* Fill our state. + */ +static int +build_state( State *state, DOUBLEMASK *input ) +{ + int x, y, i; + int xlow, xhigh; + + state->input = input; + state->data = NULL; + + /* Need xlow and xhigh to get the size of the LUT we build. + */ + xlow = xhigh = input->coeff[0]; + for( y = 0; y < input->ysize; y++ ) { + double v = input->coeff[y * input->xsize]; + + if( floor( v ) != v ) { + im_error( "im_buildlut", _( "x value not an int" ) ); + return( -1 ); + } + + if( v < xlow ) + xlow = v; + if( v > xhigh ) + xhigh = v; + } + state->lut_size = xhigh - xlow; + + if( state->lut_size < 1 ) { + im_error( "im_buildlut", _( "x range too small" ) ); + return( -1 ); + } + + if( !(state->data = IM_ARRAY( NULL, input->ysize, double * )) ) + return( -1 ); + for( y = 0; y < input->ysize; y++ ) + state->data[y] = NULL; + + for( y = 0; y < input->ysize; y++ ) + if( !(state->data[y] = IM_ARRAY( NULL, input->xsize, double )) ) + return( -1 ); + + for( i = 0, y = 0; y < input->ysize; y++ ) + for( x = 0; x < input->xsize; x++, i++ ) + state->data[y][x] = input->coeff[i]; + + /* Sort by 1st column in input. + */ + qsort( state->data, input->ysize, sizeof( double * ), compare ); + +#ifdef DEBUG + printf( "Input table, sorted by 1st column\n" ); + for( y = 0; y < input->ysize; y++ ) { + printf( "%.4d ", y ); + + for( x = 0; x < input->xsize; x++ ) + printf( "%.9f ", state->data[y][x] ); + + printf( "\n" ); + } +#endif /*DEBUG*/ + + return( 0 ); +} + +static int +buildlut( State *state, IMAGE *output ) +{ + const DOUBLEMASK *input = state->input; + const int ysize = input->ysize; + const int xsize = input->xsize; + + double *odata = (double *) output->data; + + int b, y, i, x; + + /* Do each output channel separately. + */ + for( b = 0; b < xsize - 1; b++ ) + for( x = b, y = 0; y < ysize - 1; y++ ) { + const int x1 = state->data[y][0]; + const int x2 = state->data[y + 1][0]; + const double y1 = state->data[y][b + 1]; + const double y2 = state->data[y + 1][b + 1]; + const int dx = x2 - x1; + const double dy = y2 - y1; + + for( i = 0; i < dx; i++, x += xsize - 1 ) + odata[x] = y1 + i * dy / dx; + } + + return( 0 ); +} + +int +im_buildlut( DOUBLEMASK *input, IMAGE *output ) +{ + State state; + + if( !input || input->xsize < 2 || input->ysize < 1 ) { + im_error( "im_buildlut", _( "bad input matrix size" ) ); + return( -1 ); + } + + if( build_state( &state, input ) ) { + free_state( &state ); + return( -1 ); + } + + im_initdesc( output, + state.lut_size, 1, input->xsize - 1, + IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( output ) ) + return( -1 ); + + if( buildlut( &state, output ) ) { + free_state( &state ); + return( -1 ); + } + + free_state( &state ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_gammacorrect.c b/libsrc/histograms_lut/im_gammacorrect.c new file mode 100644 index 00000000..14e4ad7a --- /dev/null +++ b/libsrc/histograms_lut/im_gammacorrect.c @@ -0,0 +1,82 @@ +/* @(#) Gamma-correct uchar image with factor gammafactor. + * @(#) + * @(#) int im_gammacorrect(in, out, exponent) + * @(#) IMAGE *in, *out; + * @(#) double exponent; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * + * Copyright: 1990, N. Dessipris. + * + * Written on: 19/07/1990 + * Modified on: + * 19/6/95 JC + * - redone as library function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_gammacorrect( IMAGE *in, IMAGE *out, double exponent ) +{ + IMAGE *t1 = im_open_local( out, "im_gammacorrect:#1", "p" ); + IMAGE *t2 = im_open_local( out, "im_gammacorrect:#2", "p" ); + IMAGE *t3 = im_open_local( out, "im_gammacorrect:#2", "p" ); + + if( !t1 || !t2 || !t3 ) + return( -1 ); + + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_gammacorrect: uchar images only" ); + return( -1 ); + } + + if( im_identity( t1, 1 ) || + im_powtra( t1, t2, exponent ) || + im_scale( t2, t3 ) || + im_maplut( in, out, t3 ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_heq.c b/libsrc/histograms_lut/im_heq.c new file mode 100644 index 00000000..aaca77f8 --- /dev/null +++ b/libsrc/histograms_lut/im_heq.c @@ -0,0 +1,79 @@ +/* @(#) Histogram equalises the input uchar image; result in uchar + * @(#) If bandno=0 all bands are equilised independantly + * @(#) else input image is equilised using the histogram of bandno only. + * @(#) Image descriptors should have been set properly by the calling program + * @(#) + * @(#) Usage: heq imagein imageout bandno + * @(#) int im_heq(in, out, bandno) + * @(#) IMAGE *in, *out; + * @(#) int bandno; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 27/03/1991 + * Modified on : + * 16/6/93 J.Cupitt + * - im_ioflag() changed to im_iocheck() + * 24/5/95 JC + * - ANSIfied and tidied up + * 3/3/01 JC + * - more cleanup + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_heq( IMAGE *in, IMAGE *out, int bandno ) +{ + IMAGE *t1 = im_open_local( out, "im_heq:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_heq:2", "p" ); + + if( !t1 || !t2 || + im_histgr( in, t1, bandno ) || + im_histeq( t1, t2 ) || + im_maplut( in, out, t2 ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_hist.c b/libsrc/histograms_lut/im_hist.c new file mode 100644 index 00000000..89d63d04 --- /dev/null +++ b/libsrc/histograms_lut/im_hist.c @@ -0,0 +1,75 @@ +/* @(#) Creates a displayable historam of in result in hist + * @(#) If bandno=0 histogram of all bands is created + * @(#) else the histogram of bandno only is created. + * @(#) + * @(#) Usage: + * @(#) int im_hist(in, out, bandno) + * @(#) IMAGE *in, *out; + * @(#) int bandno; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 27/03/1991 + * Modified on : + * 16/6/93 J.Cupitt + * - im_ioflag() call changed to im_iocheck() + * 24/5/95 JC + * - ANSIfied and tidied up + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_hist( IMAGE *in, IMAGE *out, int bandno ) +{ + IMAGE *hist = im_open_local( out, "im_hist:#1", "p" ); + + if( !hist || + im_iocheck( in, out ) || + im_histgr( in, hist, bandno ) || + im_histplot( hist, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histeq.c b/libsrc/histograms_lut/im_histeq.c new file mode 100644 index 00000000..e84ede49 --- /dev/null +++ b/libsrc/histograms_lut/im_histeq.c @@ -0,0 +1,212 @@ +/* @(#) Takes as input a histogram and creates a lut which when applied + * @(#) on the original image, histogram equilises it. + * @(#) + * @(#) Histogram equalisation is carried out for each band of hist + * @(#) individually. + * @(#) + * @(#) int im_histeq( IMAGE *hist, IMAGE *lut ) + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * + * Copyright: 1991, N. Dessipris + * + * Author: N. Dessipris + * Written on: 02/08/1990 + * 24/5/95 JC + * - tidied up and ANSIfied + * 20/7/95 JC + * - smartened up again + * - now works for hists >256 elements + * 3/3/01 JC + * - broken into cum and norm ... helps im_histspec() + * - better behaviour for >8 bit hists + * 31/10/05 JC + * - was broken for vertical histograms, gah + * - neater im_histnorm() + * 23/7/07 + * - eek, off by 1 for more than 1 band hists + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define ACCUMULATE( ITYPE, OTYPE ) { \ + for( b = 0; b < nb; b++ ) { \ + ITYPE *p = (ITYPE *) in->data; \ + OTYPE *q = (OTYPE *) outbuf; \ + OTYPE total; \ + \ + total = 0; \ + for( x = b; x < mx; x += nb ) { \ + total += p[x]; \ + q[x] = total; \ + } \ + } \ +} + +/* Form cumulative histogram. + */ +int +im_histcum( IMAGE *in, IMAGE *out ) +{ + const int px = in->Xsize * in->Ysize; + const int nb = im_iscomplex( in ) ? in->Bands * 2 : in->Bands; + const int mx = px * nb; + + PEL *outbuf; + int b, x; + + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_histcum", _( "input coded" ) ); + return( -1 ); + } + if( px > 65536 ) { + im_error( "im_histcum", _( "input too large" ) ); + return( -1 ); + } + if( im_incheck( in ) ) + return( -1 ); + + /* int types -> uint, float/double stay as they are. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = px; + out->Ysize = 1; + if( im_isint( in ) ) + out->BandFmt = IM_BANDFMT_UINT; + out->Bbits = im_bits_of_fmt( out->BandFmt ); + + if( !(outbuf = im_malloc( out, IM_IMAGE_SIZEOF_LINE( out ))) ) + return( -1 ); + + switch( in->BandFmt ) { + case IM_BANDFMT_CHAR: + ACCUMULATE( signed char, unsigned int ); break; + case IM_BANDFMT_UCHAR: + ACCUMULATE( unsigned char, unsigned int ); break; + case IM_BANDFMT_SHORT: + ACCUMULATE( signed short, unsigned int ); break; + case IM_BANDFMT_USHORT: + ACCUMULATE( unsigned short, unsigned int ); break; + case IM_BANDFMT_INT: + ACCUMULATE( signed int, unsigned int ); break; + case IM_BANDFMT_UINT: + ACCUMULATE( unsigned int, unsigned int ); break; + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_COMPLEX: + ACCUMULATE( float, float ); break; + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_DPCOMPLEX: + ACCUMULATE( double, double ); break; + + default: + assert( 0 ); + } + + if( im_setupout( out ) || im_writeline( 0, out, outbuf ) ) + return( -1 ); + + return( 0 ); +} + +/* Normalise histogram ... normalise range to make it square (ie. max == + * number of elements). Normalise each band separately. + */ +int +im_histnorm( IMAGE *in, IMAGE *out ) +{ + const int px = in->Xsize * in->Ysize; + DOUBLEMASK *stats; + double *a, *b; + int i; + IMAGE *t1; + int fmt; + + /* Need max for each channel. + */ + if( !(a = IM_ARRAY( out, in->Bands, double )) || + !(b = IM_ARRAY( out, in->Bands, double )) || + !(stats = im_stats( in )) ) + return( -1 ); + + /* Scale each channel by px / channel max + */ + for( i = 0; i < in->Bands; i++ ) { + a[i] = px / stats->coeff[6 + 1 + 6*i]; + b[i] = 0; + } + + im_free_dmask( stats ); + + if( !(t1 = im_open_local( out, "im_histnorm:2", "p" )) || + im_lintra_vec( in->Bands, a, in, b, t1 ) ) + return( -1 ); + + /* Make output format as small as we can. + */ + if( px <= 256 ) + fmt = IM_BANDFMT_UCHAR; + else if( px <= 65536 ) + fmt = IM_BANDFMT_USHORT; + else + fmt = IM_BANDFMT_UINT; + + if( im_clip2fmt( t1, out, fmt ) ) + return( -1 ); + + return( 0 ); +} + +/* Histogram equalisation. + */ +int +im_histeq( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_histeq:1", "p" ); + + /* Normalised cumulative. + */ + if( !t1 || im_histcum( in, t1 ) || im_histnorm( t1, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histgr.c b/libsrc/histograms_lut/im_histgr.c new file mode 100644 index 00000000..0faad82d --- /dev/null +++ b/libsrc/histograms_lut/im_histgr.c @@ -0,0 +1,379 @@ +/* @(#) im_histgr: make a histogram of an image and saves it into hist. + * @(#) If input is uchar, output is 256 by 1 image of uint. If input is + * @(#) ushort, output is max(image) + 1 by 1 image of uint. If bandno is + * @(#) zero, then output is has same number of bands as input, with each + * @(#) band being a separate histogram. Otherwise, bandno selects a band + * @(#) to find the histogram of. + * @(#) + * @(#) Usage: + * @(#) int im_histgr(image, hist, bandno) + * @(#) IMAGE *image, *hist; + * @(#) int bandno; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, 1991, N. Dessipris. + * + * Author: Nicos Dessipris. + * Written on: 09/07/1990 + * Modified on : 11/03/1991 + * 19/7/93 JC + * - test for Coding type added + * 26/10/94 JC + * - rewritten for ANSI + * - now does USHORT too + * - 5 x faster! + * 2/6/95 JC + * - rewritten for partials + * 3/3/01 JC + * - tiny speed ups + * 21/1/07 + * - number bands from zero + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Accumulate a histogram in one of these. + */ +typedef struct { + int bands; /* Number of bands in output */ + int which; /* If one band in out, which band of input */ + int size; /* Length of bins */ + int mx; /* Maximum value we have seen */ + unsigned int **bins; /* All the bins! */ +} Histogram; + +/* Build a Histogram. + */ +static Histogram * +build_hist( IMAGE *out, int bands, int which, int size ) +{ + int i; + Histogram *hist = IM_NEW( out, Histogram ); + + if( !hist || !(hist->bins = IM_ARRAY( out, bands, unsigned int * )) ) + return( NULL ); + for( i = 0; i < bands; i++ ) { + if( !(hist->bins[i] = IM_ARRAY( out, size, unsigned int )) ) + return( NULL ); + memset( hist->bins[i], 0, size * sizeof( unsigned int ) ); + } + + hist->bands = bands; + hist->which = which; + hist->size = size; + hist->mx = 0; + + return( hist ); +} + +/* Build a sub-hist, based on the main hist. + */ +static void * +build_subhist( IMAGE *out, Histogram *mhist ) +{ + return( (void *) + build_hist( out, mhist->bands, mhist->which, mhist->size ) ); +} + +/* Join a sub-hist onto the main hist. + */ +static int +merge_subhist( Histogram *shist, Histogram *mhist ) +{ + int i, j; + + /* Sanity! + */ + if( shist->bands != mhist->bands || shist->size != mhist->size ) + error_exit( "sanity failure in merge_subhist" ); + + /* Add on sub-data. + */ + mhist->mx = IM_MAX( mhist->mx, shist->mx ); + for( i = 0; i < mhist->bands; i++ ) + for( j = 0; j < mhist->size; j++ ) + mhist->bins[i][j] += shist->bins[i][j]; + + /* Blank out sub-hist to make sure we can't add it again. + */ + shist->mx = 0; + for( i = 0; i < shist->bands; i++ ) + shist->bins[i] = NULL; + + return( 0 ); +} + +/* Histogram of all bands of a uchar image. + */ +static int +find_uchar_hist( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int nb = im->Bands; + int x, y, z; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( reg, le, y ); + int i; + + for( i = 0, x = 0; x < r->width; x++ ) + for( z = 0; z < nb; z++, i++ ) + hist->bins[z][p[i]]++; + } + + /* Note the maximum. + */ + hist->mx = 255; + + return( 0 ); +} + +/* Histogram of a selected band of a uchar image. + */ +static int +find_uchar_hist_extract( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + unsigned int *bins = hist->bins[0]; + int nb = im->Bands; + int max = r->width * nb; + int x, y; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( reg, le, y ); + + for( x = hist->which; x < max; x += nb ) + bins[p[x]]++; + } + + /* Note the maximum. + */ + hist->mx = 255; + + return( 0 ); +} + +/* Histogram of all bands of a ushort image. + */ +static int +find_ushort_hist( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int mx = hist->mx; + int nb = im->Bands; + int x, y, z; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + unsigned short *p = (unsigned short *) + IM_REGION_ADDR( reg, le, y ); + int i; + + for( i = 0, x = 0; x < r->width; x++ ) + for( z = 0; z < nb; z++, i++ ) { + int v = p[i]; + + /* Adjust maximum. + */ + if( v > mx ) + mx = v; + + hist->bins[z][v]++; + } + } + + /* Note the maximum. + */ + hist->mx = mx; + + return( 0 ); +} + +/* Histogram of all bands of a ushort image. + */ +static int +find_ushort_hist_extract( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int mx = hist->mx; + unsigned int *bins = hist->bins[0]; + int nb = im->Bands; + int max = nb * r->width; + int x, y; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + unsigned short *p = (unsigned short *) + IM_REGION_ADDR( reg, le, y ) + hist->which; + + for( x = hist->which; x < max; x += nb ) { + int v = p[x]; + + /* Adjust maximum. + */ + if( v > mx ) + mx = v; + + bins[v]++; + } + } + + /* Note the maximum. + */ + hist->mx = mx; + + return( 0 ); +} + +int +im_histgr( IMAGE *in, IMAGE *out, int bandno ) +{ + int size; /* Length of hist */ + int bands; /* Number of bands in output */ + Histogram *mhist; + int (*scanfn)( REGION *, Histogram * ); + int i, j; + unsigned int *obuffer, *q; + + /* Check images. PIO from in, WIO to out. + */ + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_histgr", _( "uncoded images only" ) ); + return( -1 ); + } + + /* Find the range of pixel values we must handle. + */ + if( in->Bbits == IM_BBITS_BYTE && in->BandFmt == IM_BANDFMT_UCHAR ) + size = 256; + else if( in->Bbits == IM_BBITS_SHORT && + in->BandFmt == IM_BANDFMT_USHORT ) + size = 65536; + else { + im_error( "im_histgr", _( "input not uchar or ushort" ) ); + return( -1 ); + } + + /* How many output bands? + */ + if( bandno > in->Bands || bandno < -1 ) { + im_error( "im_histgr", _( "bad band parameter" ) ); + return( -1 ); + } + if( bandno == -1 ) + bands = in->Bands; + else + bands = 1; + + /* Build main hist we accumulate data in. + */ + if( !(mhist = build_hist( out, bands, bandno, size )) ) + return( -1 ); + + /* Select scan function. + */ + if( in->BandFmt == IM_BANDFMT_UCHAR && bandno == -1 ) + scanfn = find_uchar_hist; + else if( in->BandFmt == IM_BANDFMT_UCHAR ) + scanfn = find_uchar_hist_extract; + else if( in->BandFmt == IM_BANDFMT_USHORT && bandno == -1 ) + scanfn = find_ushort_hist; + else + scanfn = find_ushort_hist_extract; + + /* Accumulate data. + */ + if( im_iterate( in, + build_subhist, scanfn, merge_subhist, mhist, NULL ) ) + return( -1 ); + + /* Make the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + im_initdesc( out, + mhist->mx + 1, 1, bands, IM_BBITS_INT, IM_BANDFMT_UINT, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Interleave for output. + */ + if( !(obuffer = IM_ARRAY( out, + IM_IMAGE_N_ELEMENTS( out ), unsigned int )) ) + return( -1 ); + for( q = obuffer, j = 0; j < out->Xsize; j++ ) + for( i = 0; i < out->Bands; i++ ) + *q++ = mhist->bins[i][j]; + + /* Write interleaved buffer into hist. + */ + if( im_writeline( 0, out, (PEL *) obuffer ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histnD.c b/libsrc/histograms_lut/im_histnD.c new file mode 100644 index 00000000..6bd24bc2 --- /dev/null +++ b/libsrc/histograms_lut/im_histnD.c @@ -0,0 +1,267 @@ +/* @(#) im_histnD: make a one, two or three dimensional histogram of a 1, 2 or + * @(#) 3 band image. Divide each axis into a certain number of bins .. ie. + * @(#) output is 1 x bins, binx x bins, or bins x bins x bins bands. + * @(#) uchar and ushort only. + * @(#) + * @(#) Usage: + * @(#) int im_histnD( image, hist, bins ) + * @(#) IMAGE *image, *hist; + * @(#) int bins; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Written on: 8/7/03 + * 10/11/04 + * - oops, was not checking the bandfmt coming in + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Accumulate a histogram in one of these. + */ +typedef struct { + IMAGE *in; + IMAGE *out; + int bins; + + unsigned int ***data; /* Gather stats here */ +} Histogram; + +/* Build a Histogram. + */ +static Histogram * +build_hist( IMAGE *in, IMAGE *out, int bins ) +{ + /* How many dimensions we we need to allocate? + */ + int ilimit = in->Bands > 2 ? bins : 1; + int jlimit = in->Bands > 1 ? bins : 1; + + int i, j; + Histogram *hist; + + if( !(hist = IM_NEW( out, Histogram )) ) + return( NULL ); + + hist->in = in; + hist->out = out; + hist->bins = bins; + + if( !(hist->data = IM_ARRAY( out, bins, unsigned int ** )) ) + return( NULL ); + memset( hist->data, 0, bins * sizeof( unsigned int ** ) ); + + for( i = 0; i < ilimit; i++ ) { + if( !(hist->data[i] = IM_ARRAY( out, bins, unsigned int * )) ) + return( NULL ); + memset( hist->data[i], 0, bins * sizeof( unsigned int * ) ); + for( j = 0; j < jlimit; j++ ) { + if( !(hist->data[i][j] = IM_ARRAY( out, + bins, unsigned int )) ) + return( NULL ); + memset( hist->data[i][j], + 0, bins * sizeof( unsigned int ) ); + } + } + + return( hist ); +} + +/* Build a sub-hist, based on the main hist. + */ +static void * +build_subhist( IMAGE *out, Histogram *mhist ) +{ + return( (void *) + build_hist( mhist->in, mhist->out, mhist->bins ) ); +} + +/* Join a sub-hist onto the main hist. + */ +static int +merge_subhist( Histogram *shist, Histogram *mhist ) +{ + int i, j, k; + + /* Sanity! + */ + if( shist->in != mhist->in || shist->out != mhist->out ) + error_exit( "sanity failure in merge_subhist" ); + + /* Add on sub-data. + */ + for( i = 0; i < mhist->bins; i++ ) + for( j = 0; j < mhist->bins; j++ ) + for( k = 0; k < mhist->bins; k++ ) + if( mhist->data[i] && mhist->data[i][j] ) { + mhist->data[i][j][k] += + shist->data[i][j][k]; + + /* Zap sub-hist to make sure we + * can't add it again. + */ + shist->data[i][j][k] = 0; + } + + return( 0 ); +} + +#define LOOP( TYPE ) { \ + TYPE *p = (TYPE *) line;\ + \ + for( i = 0, x = 0; x < r->width; x++ ) { \ + for( z = 0; z < nb; z++, i++ ) \ + index[z] = p[i] / scale; \ + \ + hist->data[index[2]][index[1]][index[0]]++; \ + } \ +} + +static int +find_hist( REGION *reg, Histogram *hist ) +{ + Rect *r = ®->valid; + IMAGE *im = reg->im; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int nb = im->Bands; + int max_val = im->BandFmt == IM_BANDFMT_UCHAR ? 256 : 65536; + int scale = max_val / hist->bins; + int x, y, z, i; + int index[3]; + + /* Fill these with dimensions, backwards. + */ + index[0] = index[1] = index[2] = 0; + + /* Accumulate! + */ + for( y = to; y < bo; y++ ) { + char *line = IM_REGION_ADDR( reg, le, y ); + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + LOOP( unsigned char ); + break; + + case IM_BANDFMT_USHORT: + LOOP( unsigned char ); + break; + + default: + error_exit( "panic #34847563245" ); + } + } + + return( 0 ); +} + +int +im_histnD( IMAGE *in, IMAGE *out, int bins ) +{ + int max_val; + Histogram *mhist; + int x, y, z, i; + unsigned int *obuffer; + + /* Check images. PIO from in, WIO to out. + */ + if( im_pincheck( in ) || im_outcheck( out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_histnD", _( " uncoded images only" ) ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR && + in->BandFmt != IM_BANDFMT_USHORT ) { + im_error( "im_histnD", + _( " unsigned 8 or 16 bit images only" ) ); + return( -1 ); + } + + max_val = in->BandFmt == IM_BANDFMT_UCHAR ? 256 : 65536; + if( bins < 1 || bins > max_val ) { + im_error( "im_histnD", + _( " bins out of range [1,%d]" ), max_val ); + return( -1 ); + } + + /* Build main hist we accumulate to. + */ + if( !(mhist = build_hist( in, out, bins )) ) + return( -1 ); + + /* Accumulate data. + */ + if( im_iterate( in, + build_subhist, find_hist, merge_subhist, mhist, NULL ) ) + return( -1 ); + + /* Make the output image. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + im_initdesc( out, + bins, in->Bands > 1 ? bins : 1, in->Bands > 2 ? bins : 1, + IM_BBITS_INT, IM_BANDFMT_UINT, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + + /* Interleave to output buffer. + */ + if( !(obuffer = IM_ARRAY( out, + IM_IMAGE_N_ELEMENTS( out ), unsigned int )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + for( i = 0, x = 0; x < out->Xsize; x++ ) + for( z = 0; z < out->Bands; z++, i++ ) + obuffer[i] = mhist->data[z][y][x]; + + if( im_writeline( y, out, (PEL *) obuffer ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histplot.c b/libsrc/histograms_lut/im_histplot.c new file mode 100644 index 00000000..6592d54d --- /dev/null +++ b/libsrc/histograms_lut/im_histplot.c @@ -0,0 +1,336 @@ +/* @(#) im_histplot: plot a 1xany or anyx1 image file as a max x any or + * @(#) any x max graph using these rules: + * @(#) + * @(#) - unsigned char + * @(#) always output 256 + * @(#) - other unsigned integer types + * @(#) output 0 - max + * @(#) - signed int types + * @(#) min moved to 0, max moved to max + min. + * @(#) - float types + * @(#) min moved to 0, max moved to any (square output) + * @(#) + * @(#) usage: + * @(#) + * @(#) int + * @(#) im_histplot( hist, histplot ) + * @(#) IMAGE *hist, *histplot; + * @(#) + * @(#) Returns non-zero on error + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris. + * Written on: 09/07/1990 + * Modified on : 12/03/1991 + * 20/6/95 JC + * - rules rationalised + * - im_lineprof removed + * - rewritten + * 13/8/99 JC + * - rewritten again for partial, rules redone + * 19/9/99 JC + * - oooops, broken for >1 band + * 26/9/99 JC + * - oooops, graph float was wrong + * 17/11/99 JC + * - oops, failed for all 0's histogram + * 14/12/05 + * - redone plot function in C, also use incheck() to cache calcs + * - much, much faster! + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Normalise an image using the rules noted above. + */ +static int +normalise( IMAGE *in, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_histplot:2", "p" ); + double min, max; + + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_histplot", _( "uncoded only" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_histplot", _( "non-complex only" ) ); + return( -1 ); + } + + if( im_isuint( in ) ) { + /* Trivial case. + */ + if( im_copy( in, out ) ) + return( -1 ); + } + else if( im_isint( in ) ) { + /* Move min up to 0. incheck(), because we have to min() so we + * might as well save the calcs. + */ + if( !t1 || + im_incheck( in ) || + im_min( in, &max ) || + im_lintra( 1.0, in, -min, t1 ) ) + return( -1 ); + } + else { + /* Float image: scale min--max to 0--any. Output square + * graph. + */ + int any; + + if( in->Xsize == 1 ) + any = in->Ysize; + else + any = in->Xsize; + + /* incheck(), because we have to min()/max() so we + * might as well save the calcs. + */ + if( !t1 || + im_incheck( in ) || + im_min( in, &min ) || + im_max( in, &max ) || + im_lintra( any / (max - min), in, + -min * any / (max - min), out ) ) + return( -1 ); + } + + return( 0 ); +} + +#define VERT( TYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + \ + for( x = le; x < ri; x++ ) { \ + for( b = 0; b < nb; b++ ) \ + q[b] = p1[b] < x ? 0 : 255; \ + \ + q += nb; \ + } \ +} + + +/* Generate function. + */ +static int +make_vert_gen( REGION *or, void *dummy, IMAGE *in ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int ri = IM_RECT_RIGHT( r ); + int bo = IM_RECT_BOTTOM( r ); + int nb = in->Bands; + + int x, y, b; + + for( y = to; y < bo; y++ ) { + unsigned char *q = (unsigned char *) + IM_REGION_ADDR( or, le, y ); + unsigned char *p = (unsigned char *) + IM_IMAGE_ADDR( in, 0, y ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: VERT( unsigned char ); break; + case IM_BANDFMT_CHAR: VERT( signed char ); break; + case IM_BANDFMT_USHORT: VERT( unsigned short ); break; + case IM_BANDFMT_SHORT: VERT( signed short ); break; + case IM_BANDFMT_UINT: VERT( unsigned int ); break; + case IM_BANDFMT_INT: VERT( signed int ); break; + case IM_BANDFMT_FLOAT: VERT( float ); break; + case IM_BANDFMT_DOUBLE: VERT( double ); break; + + default: + im_error( "im_histplot", _( "internal error #8255" ) ); + return( -1 ); + } + } + + return( 0 ); +} + +#define HORZ( TYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + \ + for( y = to; y < bo; y++ ) { \ + for( b = 0; b < nb; b++ ) \ + q[b] = p1[b] < (ht - y) ? 0 : 255; \ + \ + q += lsk; \ + } \ +} + + +/* Generate function. + */ +static int +make_horz_gen( REGION *or, void *dummy, IMAGE *in ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int ri = IM_RECT_RIGHT( r ); + int bo = IM_RECT_BOTTOM( r ); + int nb = in->Bands; + int lsk = IM_REGION_LSKIP( or ); + int ht = or->im->Ysize; + + int x, y, b; + + for( x = le; x < ri; x++ ) { + unsigned char *q = (unsigned char *) + IM_REGION_ADDR( or, x, to ); + unsigned char *p = (unsigned char *) + IM_IMAGE_ADDR( in, x, 0 ); + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: HORZ( unsigned char ); break; + case IM_BANDFMT_CHAR: HORZ( signed char ); break; + case IM_BANDFMT_USHORT: HORZ( unsigned short ); break; + case IM_BANDFMT_SHORT: HORZ( signed short ); break; + case IM_BANDFMT_UINT: HORZ( unsigned int ); break; + case IM_BANDFMT_INT: HORZ( signed int ); break; + case IM_BANDFMT_FLOAT: HORZ( float ); break; + case IM_BANDFMT_DOUBLE: HORZ( double ); break; + + default: + im_error( "im_histplot", _( "internal error #8255" ) ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Plot image. + */ +static int +plot( IMAGE *in, IMAGE *out ) +{ + IMAGE *t[5]; + double max; + int tsize; + int xsize; + int ysize; + + if( im_incheck( in ) || + im_poutcheck( out ) ) + return( -1 ); + + /* Find range we will plot. + */ + if( im_open_local_array( out, t, 5, "im_histplot", "p" ) || + im_max( in, &max ) ) + return( -1 ); + if( max < 0 ) { + im_errormsg( "im_histplot: internal error #8254" ); + return( -1 ); + } + if( in->BandFmt == IM_BANDFMT_UCHAR ) + tsize = 256; + else + tsize = ceil( max ); + + /* Make sure we don't make a zero height image. + */ + if( tsize == 0 ) + tsize = 1; + + if( in->Xsize == 1 ) { + /* Vertical graph. + */ + xsize = tsize; + ysize = in->Ysize; + } + else { + /* Horizontal graph. + */ + xsize = in->Xsize; + ysize = tsize; + } + + /* Set image. + */ + im_initdesc( out, xsize, ysize, in->Bands, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( in->Xsize == 1 ) { + if( im_generate( out, NULL, make_vert_gen, NULL, in, NULL ) ) + return( -1 ); + } + else { + if( im_generate( out, NULL, make_horz_gen, NULL, in, NULL ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_histplot( IMAGE *hist, IMAGE *histplot ) +{ + IMAGE *norm = im_open_local( histplot, "im_histplot:1", "p" ); + + if( !norm ) + return( -1 ); + if( hist->Xsize != 1 && hist->Ysize != 1 ) { + im_error( "im_histplot", _( "Xsize or Ysize not 1" ) ); + return( -1 ); + } + + if( normalise( hist, norm ) || + plot( norm, histplot ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_histspec.c b/libsrc/histograms_lut/im_histspec.c new file mode 100644 index 00000000..55dce220 --- /dev/null +++ b/libsrc/histograms_lut/im_histspec.c @@ -0,0 +1,217 @@ +/* @(#) Creates a lut for transforming imagein (with histin) according to + * @(#) the pdf of imageref (with histref). The lut should have been set + * @(#) by a call to im_setbuf() or im_openout(). histin and histref + * @(#) should have been set by a call to im_mmapin() or they are buffer images + * @(#) + * @(#) Usage: int im_histspec(in, ref, out) + * @(#) IMAGE *histin, *histref, *out; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 19/07/1990 + * Modified on: 26/03/1991 + * 1/3/01 JC + * - bleurg! rewritten, now does 16 bits as well, bugs removed, faster, + * smaller + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +/* +#define PIM_RINT + */ + +/* Match two normalised cumulative histograms. + */ +static int +match( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + const int inpx = in->Xsize * in->Ysize; + const int refpx = ref->Xsize * ref->Ysize; + const int bands = in->Bands; + + unsigned int *inbuf; /* in and ref, padded to same size */ + unsigned int *refbuf; + unsigned int *outbuf; /* Always output as uint */ + + int px; /* Number of pixels */ + int max; /* px * bands */ + + int i, j; + + if( im_iocheck( in, out ) || im_iocheck( ref, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || ref->Coding != IM_CODING_NONE ) { + im_errormsg( "im_histspec: not uncoded" ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UINT || + ref->BandFmt != IM_BANDFMT_UINT ) { + im_errormsg( "im_histspec: bad band format" ); + return( -1 ); + } + if( in->Bands != ref->Bands ) { + im_errormsg( "im_histspec: input histograms differ in " + "number of bands" ); + return( -1 ); + } + + /* How big? + */ + if( inpx <= 256 && refpx <= 256 ) + px = 256; + else if( inpx <= 65536 && refpx <= 65536 ) + px = 65536; + else { + im_errormsg( "im_histspec: luts too large" ); + return( -1 ); + } + max = px * bands; + + /* Unpack to equal sized buffers. + */ + inbuf = IM_ARRAY( out, max, unsigned int ); + refbuf = IM_ARRAY( out, max, unsigned int ); + outbuf = IM_ARRAY( out, max, unsigned int ); + if( !inbuf || !refbuf || !outbuf ) + return( -1 ); + for( i = 0; i < inpx * bands; i++ ) + inbuf[i] = ((unsigned int *)in->data)[i]; + for( ; i < max; i++ ) + inbuf[i] = 0; + for( i = 0; i < refpx * bands; i++ ) + refbuf[i] = ((unsigned int *)ref->data)[i]; + for( ; i < max; i++ ) + refbuf[i] = 0; + + for( j = 0; j < bands; j++ ) { + /* Track up refbuf[] with this. + */ + int ri = j; + int limit = max - bands; + + for( i = j; i < max; i += bands ) { + unsigned int inv = inbuf[i]; + + for( ; ri < limit; ri += bands ) + if( inv <= refbuf[ri] ) + break; + + if( ri < limit ) { + /* Simple rounding. + */ + double mid = refbuf[ri] + + refbuf[ri + bands] / 2.0; + + if( inv < mid ) + outbuf[i] = ri/bands; + else + outbuf[i] = ri/bands + 1; + } + else + outbuf[i] = refbuf[ri]; + } + } + + if( im_cp_descv( out, in, ref, NULL ) ) + return( -1 ); + out->Xsize = px; + out->Ysize = 1; + out->Type = IM_TYPE_HISTOGRAM; + + if( im_setupout( out ) || im_writeline( 0, out, (PEL *) outbuf ) ) + return( -1 ); + + return( 0 ); +} + +int +im_histspec( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *t1 = im_open_local( out, "im_histspec:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_histspec:2", "p" ); + IMAGE *t3 = im_open_local( out, "im_histspec:3", "p" ); + IMAGE *t4 = im_open_local( out, "im_histspec:4", "p" ); + IMAGE *t5 = im_open_local( out, "im_histspec:5", "p" ); + + int px; + int fmt; + + if( !t1 || !t2 || !t2 || !t4 || !t5 ) + return( -1 ); + if( !im_isuint( in ) || !im_isuint( ref ) ) { + im_errormsg( "im_histspec: input luts are not some unsigned " + "integer type" ); + return( -1 ); + } + + /* Match hists. + */ + if( im_histeq( in, t1 ) || im_clip2ui( t1, t2 ) || + im_histeq( ref, t3 ) || im_clip2ui( t3, t4 ) || + match( t2, t4, t5 ) ) + return( -1 ); + + /* Clip type down. + */ + px = t5->Xsize * t5->Ysize; + if( px <= 256 ) + fmt = IM_BANDFMT_UCHAR; + else if( px <= 65536 ) + fmt = IM_BANDFMT_USHORT; + else + fmt = IM_BANDFMT_UINT; + + if( im_clip2fmt( t5, out, fmt ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/histograms_lut/im_hsp.c b/libsrc/histograms_lut/im_hsp.c new file mode 100644 index 00000000..79af9c2b --- /dev/null +++ b/libsrc/histograms_lut/im_hsp.c @@ -0,0 +1,77 @@ +/* @(#) Maps imagein to imageout with histogram specified by imageref + * @(#) Both images should have the same number of bands + * @(#) Each band of the output image is specified according to the distribution + * @(#) of grey levels of the reference image + * @(#) + * @(#) Usage: + * @(#) int im_hsp(in, ref, out) + * @(#) IMAGE *in, *ref, *out; + * @(#) + * @(#) Return 0 on success and -1 on error + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 08/05/1990 + * Modified on : + * 16/6/93 J.Cupitt + * - im_ioflag() call changed to im_iocheck() + * 25/5/95 JC + * - revised + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_hsp( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + IMAGE *histin = im_open_local( out, "im_hsp:#1", "p" ); + IMAGE *histref = im_open_local( out, "im_hsp:#2", "p" ); + IMAGE *lut = im_open_local( out, "im_hsp:#3", "p" ); + + if( !histin || !histref || !lut || + im_histgr( in, histin, -1 ) || + im_histgr( ref, histref, -1 ) || + im_histspec( histin, histref, lut ) || + im_maplut( in, out, lut ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_identity.c b/libsrc/histograms_lut/im_identity.c new file mode 100644 index 00000000..030781df --- /dev/null +++ b/libsrc/histograms_lut/im_identity.c @@ -0,0 +1,163 @@ +/* @(#) Creates a image file with Xsize=256, Ysize=1, Bands=bands, + * @(#) Bbits=IM_BBITS_BYTE, BandFmt=IM_BANDFMT_UCHAR, Type=IM_TYPE_HISTOGRAM; + * @(#) + * @(#) The created image consist a n bands linear lut and is the basis + * @(#) for building up look-up tables. + * @(#) + * @(#) int im_identity(lut, bands) + * @(#) IMAGE *lut; + * @(#) int bands; + * @(#) + * @(#) As above, but make a ushort LUT. ushort LUTs can be less than 65536 + * @(#) elements - sz is the number of elements required. + * @(#) + * @(#) int im_identity_ushort(lut, bands, sz) + * @(#) IMAGE *lut; + * @(#) int bands; + * @(#) int sz; + * @(#) + * @(#) Returns -1 on error and 0 on success + * @(#) + * + * Copyright 1991, N. Dessipris. + * + * Author N. Dessipris + * Written on 11/03/1991 + * Updated on: + * 18/6/93 JC + * - im_outcheck() call added + * - ANSIfied + * 24/8/94 JC + * - im_identity_ushort() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_identity( IMAGE *lut, int bands ) +{ + unsigned char *buf, *p; + int x, z; + + /* Check input args + */ + if( im_outcheck( lut ) ) + return( -1 ); + if( bands < 0 ) { + im_errormsg( "im_identity: bad bands" ); + return( -1 ); + } + + /* Set new image properly. + */ + im_initdesc( lut, + 256, 1, bands, IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + + /* Make output. + */ + if( im_setupout( lut ) ) + return( -1 ); + + /* Create output buffer. + */ + if( !(buf = (unsigned char *) im_malloc( lut, bands * 256 )) ) + return( -1 ); + + /* Write ramp. + */ + for( p = buf, x = 0; x < 256; x++ ) + for( z = 0; z < bands; z++ ) + *p++ = x; + + if( im_writeline( 0, lut, buf ) ) + return( -1 ); + + return( 0 ); +} + +int +im_identity_ushort( IMAGE *lut, int bands, int sz ) +{ + unsigned short *buf, *p; + int x, z; + + /* Check input args + */ + if( im_outcheck( lut ) ) + return( -1 ); + if( sz > 65536 || sz < 0 ) { + im_errormsg( "im_identity_ushort: bad size" ); + return( -1 ); + } + if( bands < 0 ) { + im_errormsg( "im_identity_ushort: bad bands" ); + return( -1 ); + } + + /* Set new image. + */ + im_initdesc( lut, + sz, 1, bands, IM_BBITS_SHORT, IM_BANDFMT_USHORT, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + + /* Make output. + */ + if( im_setupout( lut ) ) + return( -1 ); + + /* Create output buffer. + */ + if( !(buf = (unsigned short *) + im_malloc( lut, sz * bands * sizeof( unsigned short ) )) ) + return( -1 ); + + /* Write ramp. + */ + for( p = buf, x = 0; x < sz; x++ ) + for( z = 0; z < bands; z++ ) + *p++ = x; + if( im_writeline( 0, lut, (PEL *) buf ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_invertlut.c b/libsrc/histograms_lut/im_invertlut.c new file mode 100644 index 00000000..26d6ffb6 --- /dev/null +++ b/libsrc/histograms_lut/im_invertlut.c @@ -0,0 +1,268 @@ +/* @(#) Given a mask of target values and real values, generate a LUT which + * @(#) will map reals to targets. Handy for linearising images from + * @(#) measurements of a colour chart. All values in [0,1]. Piecewise linear + * @(#) interpolation, extrapolate head and tail to 0 and 1. + * @(#) + * @(#) Eg. input line like: + * @(#) + * @(#) 0.1 0.2 0.3 0.1 + * @(#) + * @(#) means a patch with 10% reflectance produces an image with 20% in + * @(#) channel 1, 30% in channel 2, and 10% in channel 3. + * @(#) + * @(#) Inputs don't need to be sorted (we do that). Generate any precision + * @(#) LUT ... typically ask for 256 elements. + * + * Written on: 5/6/01 + * Modified on : + * 7/7/03 JC + * - generate image rather than doublemask (arrg) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +/* Our state. + */ +typedef struct { + DOUBLEMASK *input; /* Input mask */ + IMAGE *output; /* Output lut */ + int lut_size; /* Number of output elements to generate */ + + double **data; /* Rows of unpacked matrix */ +} State; + +/* Use this to sort our input rows by the first column. + */ +static int +compare( const void *a, const void *b ) +{ + double **r1 = (double **) a; + double **r2 = (double **) b; + + double diff = r1[0][0] - r2[0][0]; + + if( diff > 0 ) + return( 1 ); + else if( diff == 0 ) + return( 0 ); + else + return( -1 ); +} + +/* Free our state. + */ +static void +free_state( State *state ) +{ + if( state->data ) { + int i; + + for( i = 0; i < state->input->ysize; i++ ) + if( state->data[i] ) { + im_free( state->data[i] ); + state->data[i] = NULL; + } + + im_free( state->data ); + state->data = NULL; + } +} + +/* Fill our state. + */ +static int +build_state( State *state, DOUBLEMASK *input, IMAGE *output, int lut_size ) +{ + int x, y, i; + + state->input = input; + state->output = output; + state->lut_size = lut_size; + state->data = NULL; + + if( !(state->data = IM_ARRAY( NULL, input->ysize, double * )) ) + return( -1 ); + for( y = 0; y < input->ysize; y++ ) + state->data[y] = NULL; + + for( y = 0; y < input->ysize; y++ ) + if( !(state->data[y] = IM_ARRAY( NULL, input->xsize, double )) ) + return( -1 ); + + for( i = 0, y = 0; y < input->ysize; y++ ) + for( x = 0; x < input->xsize; x++, i++ ) + state->data[y][x] = input->coeff[i]; + + /* Sanity check for data range. + */ + for( y = 0; y < input->ysize; y++ ) + for( x = 0; x < input->xsize; x++ ) + if( state->data[y][x] > 1.0 || + state->data[y][x] < 0.0 ) { + im_errormsg( "im_invertlut: element out of " + "range [0,1]" ); + return( -1 ); + } + + /* Sort by 1st column in input. + */ + qsort( state->data, input->ysize, sizeof( double * ), compare ); + +#ifdef DEBUG + printf( "Input table, sorted by 1st column\n" ); + for( y = 0; y < input->ysize; y++ ) { + printf( "%.4d ", y ); + + for( x = 0; x < input->xsize; x++ ) + printf( "%.9f ", state->data[y][x] ); + + printf( "\n" ); + } +#endif /*DEBUG*/ + + return( 0 ); +} + +static int +invertlut( State *state ) +{ + DOUBLEMASK *input = state->input; + int ysize = input->ysize; + int xsize = input->xsize; + IMAGE *output = state->output; + double *odata = (double *) output->data; + int bands = xsize - 1; + + double **data = state->data; + int lut_size = state->lut_size; + + int i; + + /* Do each output channel separately. + */ + for( i = 0; i < bands; i++ ) { + /* The first and last lut positions we know real values for. + */ + int first = data[0][i + 1] * (lut_size - 1); + int last = data[ysize - 1][i + 1] * (lut_size - 1); + + int k; + double fac; + + /* Extrapolate bottom and top segments to (0,0) and (1,1). + */ + fac = data[0][0] / first; + for( k = 0; k < first; k++ ) + odata[i + k * bands] = k * fac; + + fac = (1 - data[ysize - 1][0]) / ((lut_size - 1) - last); + for( k = last + 1; k < lut_size; k++ ) + odata[i + k * bands] = + data[ysize - 1][0] + (k - last) * fac; + + /* Interpolate the data setions. + */ + for( k = first; k <= last; k++ ) { + /* Where we're at in the [0,1] range. + */ + double ki = (double) k / (lut_size - 1); + + double irange, orange; + int j; + + /* Search for the lowest real value < ki. There may + * not be one: if not, just use 0. Tiny error. + */ + for( j = ysize - 1; j >= 0; j-- ) + if( data[j][i + 1] < ki ) + break; + if( j == -1 ) + j = 0; + + /* Interpolate k as being between row data[j] and row + * data[j + 1]. + */ + irange = data[j + 1][i + 1] - data[j][i + 1]; + orange = data[j + 1][0] - data[j][0]; + + odata[i + k * bands] = data[j][0] + + orange * ((ki - data[j][i + 1]) / irange); + } + } + + return( 0 ); +} + +int +im_invertlut( DOUBLEMASK *input, IMAGE *output, int lut_size ) +{ + State state; + + if( !input || input->xsize < 2 || input->ysize < 1 ) { + im_errormsg( "im_invertlut: bad input matrix" ); + return( -1 ); + } + if( lut_size < 1 || lut_size > 65536 ) { + im_errormsg( "im_invertlut: bad lut_size" ); + return( -1 ); + } + + im_initdesc( output, + lut_size, 1, input->xsize - 1, + IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( output ) ) + return( -1 ); + + if( build_state( &state, input, output, lut_size ) || + invertlut( &state ) ) { + free_state( &state ); + return( -1 ); + } + free_state( &state ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_lhisteq.c b/libsrc/histograms_lut/im_lhisteq.c new file mode 100644 index 00000000..8cab4c9e --- /dev/null +++ b/libsrc/histograms_lut/im_lhisteq.c @@ -0,0 +1,212 @@ +/* @(#) Performs local histogram equalisation on an image using a + * @(#) window of size xw by yw + * @(#) Works only on monochrome images + * @(#) + * @(#) int im_lhisteq(in, out, xwin, ywin) + * @(#) IMAGE *in, *out; + * @(#) int xwin, ywin; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * + * Copyright: 1991, N. Dessipris + * + * Author: N. Dessipris + * Written on: 24/10/1991 + * Modified on : + * 25/1/96 JC + * - rewritten, adapting im_spcor() + * - correct result, 2x faster, partial, simpler, better arg checking + * 8/7/04 + * - expand input rather than output with new im_embed() mode + * - _raw() output is one pixel larger + * - sets Xoffset/Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold global stuff here. + */ +typedef struct { + int xwin, ywin; /* Parameters */ + + int npels; /* Pels in window */ +} LhistInfo; + +/* lhist generate function. + */ +static int +lhist_gen( REGION *or, REGION *ir, IMAGE *in, LhistInfo *inf ) +{ + Rect irect; + + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ri = IM_RECT_RIGHT(r); + + int x, y, i, j; + int lsk; + + int coff; /* Offset to move to centre of window */ + + /* What part of ir do we need? + */ + irect.left = or->valid.left; + irect.top = or->valid.top; + irect.width = or->valid.width + inf->xwin; + irect.height = or->valid.height + inf->ywin; + + if( im_prepare( ir, &irect ) ) + return( -1 ); + lsk = IM_REGION_LSKIP( ir ); + coff = lsk * (inf->ywin/2) + inf->xwin/2; + + for( y = to; y < bo; y++ ) { + /* Get input and output pointers for this line. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + PEL *p1, *p2; + int hist[ 256 ]; + + /* Find histogram for start of this line. + */ + memset( hist, 0, 256 * sizeof(int) ); + for( p1 = p, j = 0; j < inf->ywin; j++, p1 += lsk ) + for( p2 = p1, i = 0; i < inf->xwin; i++, p2++ ) + hist[*p2]++; + + /* Loop for output pels. + */ + for( x = le; x < ri; x++, p++ ) { + /* Sum histogram up to current pel. + */ + int target = p[coff]; + int sum = 0; + + for( sum = 0, i = 0; i < target; i++ ) + sum += hist[i]; + + /* Transform. + */ + *q++ = sum * 256 / inf->npels; + + /* Adapt histogram - remove the pels from the left hand + * column, add in pels for a new right-hand column. + */ + for( p1 = p, j = 0; j < inf->ywin; j++, p1 += lsk ) { + hist[p1[0]]--; + hist[p1[inf->xwin]]++; + } + } + } + + return( 0 ); +} + +int +im_lhisteq_raw( IMAGE *in, IMAGE *out, int xwin, int ywin ) +{ + LhistInfo *inf; + + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Bbits != IM_BBITS_BYTE || in->BandFmt != IM_BANDFMT_UCHAR || + in->Bands != 1 || in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_lhisteq: one band uchar uncoded only" ); + return( -1 ); + } + if( xwin > in->Xsize || ywin > in->Ysize ) { + im_errormsg( "im_lhisteq: window too large" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= xwin - 1; + out->Ysize -= ywin - 1; + + /* Save parameters. + */ + if( !(inf = IM_NEW( out, LhistInfo )) ) + return( -1 ); + inf->xwin = xwin; + inf->ywin = ywin; + inf->npels = xwin * ywin; + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Write the hist. + */ + if( im_generate( out, + im_start_one, lhist_gen, im_stop_one, in, inf ) ) + return( -1 ); + + out->Xoffset = -xwin / 2; + out->Yoffset = -xwin / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_lhisteq( IMAGE *in, IMAGE *out, int xwin, int ywin ) +{ + IMAGE *t1 = im_open_local( out, "im_lhisteq:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, + xwin / 2, ywin / 2, + in->Xsize + xwin - 1, in->Ysize + ywin - 1 ) || + im_lhisteq_raw( t1, out, xwin, ywin ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_maplut.c b/libsrc/histograms_lut/im_maplut.c new file mode 100644 index 00000000..8cdc3d22 --- /dev/null +++ b/libsrc/histograms_lut/im_maplut.c @@ -0,0 +1,623 @@ +/* @(#) Map an image through another image, acting as a LUT (Look Up Table). + * @(#) The lut may have any type, and the output image will be that type. + * @(#) + * @(#) The input image must be an unsigned integer types, that is, it must + * @(#) be one of IM_BANDFMT_UCHAR, IM_BANDFMT_USHORT or IM_BANDFMT_UINT. + * @(#) + * @(#) If the input is IM_BANDFMT_UCHAR, then the LUT must have 256 elements, + * @(#) in other words, lut->Xsize * lut->Ysize == 256. + * @(#) + * @(#) If the input is IM_BANDFMT_USHORT or IM_BANDFMT_UINT, then the lut + * @(#) may have any number of elements, and input pels whose value is + * @(#) greater than lut->Xsize * lut->Ysize are mapped with the last LUT + * @(#) element. + * @(#) + * @(#) If LUT has one band, then all bands of input pass through it. If LUT + * @(#) has same number of bands as input, then each band is LUTed + * @(#) separately. If input has one band, then LUT may have many bands and + * @(#) the output will have the same number of bands as the LUT. + * @(#) + * @(#) int + * @(#) im_maplut( in, out, lut ) + * @(#) IMAGE *in, *out, *lut; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Modified: + * 18/6/93 JC + * - oops! im_incheck() added for LUT image + * - some ANSIfication + * 15/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * - now does complex LUTs too + * 10/3/94 JC + * - more helpful error messages, slight reformatting + * 24/8/94 JC + * - now allows non-uchar image input + * 7/10/94 JC + * - uses im_malloc(), IM_NEW() etc. + * 13/3/95 JC + * - now takes a private copy of LUT, so user can im_close() LUT image + * after im_maplut() without fear of coredumps + * 23/6/95 JC + * - lut may now have many bands if image has just one band + * 3/3/01 JC + * - small speed ups + * 30/6/04 + * - heh, 1 band image + 3 band lut + >8bit output has been broken for 9 + * years :-) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Struct we carry for LUT operations. + */ +typedef struct { + int fmt; /* LUT image BandFmt */ + int nb; /* Number of bands in lut */ + int es; /* IM_IMAGE_SIZEOF_ELEMENT() for lut image */ + int sz; /* Number of elements in minor dimension */ + int clp; /* Value we clip against */ + PEL **table; /* Lut converted to 2d array */ + int overflow; /* Number of overflows for non-uchar lut */ +} LutInfo; + +/* Print overflows, if any. + */ +static int +end_lut( LutInfo *st ) +{ + if( st->overflow ) { + im_warn( "im_maplut", _( "%d overflows detected" ), + st->overflow ); + st->overflow = 0; + } + + return( 0 ); +} + +/* Build a lut table. + */ +static LutInfo * +build_luts( IMAGE *out, IMAGE *lut ) +{ + LutInfo *st = IM_NEW( out, LutInfo ); + int i, x; + PEL *q; + + if( !st ) + return( NULL ); + + /* Make luts. We unpack the LUT image into a C 2D array to speed + * processing. + */ + st->fmt = lut->BandFmt; + st->es = IM_IMAGE_SIZEOF_ELEMENT( lut ); + st->nb = lut->Bands; + st->sz = lut->Xsize * lut->Ysize; + st->clp = st->sz - 1; + st->overflow = 0; + st->table = NULL; + if( im_add_evalend_callback( out, + (im_callback_fn) end_lut, st, NULL ) ) + return( NULL ); + + /* Attach tables. + */ + if( !(st->table = IM_ARRAY( out, lut->Bands, PEL * )) ) + return( NULL ); + for( i = 0; i < lut->Bands; i++ ) + if( !(st->table[i] = IM_ARRAY( out, st->sz * st->es, PEL )) ) + return( NULL ); + + /* Scan LUT and fill table. + */ + q = (PEL *) lut->data; + for( x = 0; x < st->sz; x++ ) + for( i = 0; i < st->nb; i++ ) { + memcpy( st->table[i] + x * st->es, q, st->es ); + q += st->es; + } + + return( st ); +} + +/* Our sequence value: the region this sequence is using, and local stats. + */ +typedef struct { + REGION *ir; /* Input region */ + int overflow; /* Number of overflows */ +} Seq; + +/* Destroy a sequence value. + */ +static int +stop_maplut( Seq *seq, IMAGE *in, LutInfo *st ) +{ + /* Add to global stats. + */ + st->overflow += seq->overflow; + + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Our start function. + */ +static void * +start_maplut( IMAGE *out, IMAGE *in, LutInfo *st ) +{ + Seq *seq = IM_NEW( out, Seq ); + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->overflow = 0; + + if( !(seq->ir = im_region_create( in )) ) + return( NULL ); + + return( (void *) seq ); +} + +/* Map through n non-complex luts. + */ +#define loop(OUT) { \ + int b = st->nb; \ + \ + for( y = to; y < bo; y++ ) { \ + for( z = 0; z < b; z++ ) { \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + OUT *tlut = (OUT *) st->table[z]; \ + \ + for( x = z; x < ne; x += b ) \ + q[x] = tlut[p[x]]; \ + } \ + } \ +} + +/* Map through n complex luts. + */ +#define loopc(OUT) { \ + int b = in->Bands; \ + \ + for( y = to; y < bo; y++ ) { \ + for( z = 0; z < b; z++ ) { \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ) + z; \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ) + z*2; \ + OUT *tlut = (OUT *) st->table[z]; \ + \ + for( x = 0; x < ne; x += b ) { \ + int n = p[x]*2; \ + \ + q[0] = tlut[n]; \ + q[1] = tlut[n + 1]; \ + q += b*2; \ + } \ + } \ + } \ +} + +#define loopg(IN,OUT) { \ + int b = st->nb; \ + \ + for( y = to; y < bo; y++ ) { \ + for( z = 0; z < b; z++ ) { \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + OUT *tlut = (OUT *) st->table[z]; \ + \ + for( x = z; x < ne; x += b ) { \ + int index = p[x]; \ + \ + if( index > st->clp ) { \ + index = st->clp; \ + seq->overflow++; \ + } \ + \ + q[x] = tlut[index]; \ + } \ + } \ + } \ +} + +#define loopcg(IN,OUT) { \ + int b = in->Bands; \ + \ + for( y = to; y < bo; y++ ) { \ + for( z = 0; z < b; z++ ) { \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ) + z; \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ) + z*2; \ + OUT *tlut = (OUT *) st->table[z]; \ + \ + for( x = 0; x < ne; x += b ) { \ + int index = p[x]; \ + \ + if( index > st->clp ) { \ + index = st->clp; \ + seq->overflow++; \ + } \ + \ + q[0] = tlut[ index*2 ]; \ + q[1] = tlut[ index*2+1 ]; \ + \ + q += b*2; \ + } \ + } \ + } \ +} + +/* Map image through one non-complex lut. + */ +#define loop1(OUT) { \ + OUT *tlut = (OUT *) st->table[0]; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < ne; x++ ) \ + q[x] = tlut[p[x]]; \ + } \ +} + +/* Map image through one complex lut. + */ +#define loop1c(OUT) { \ + OUT *tlut = (OUT *) st->table[0]; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < ne; x++ ) { \ + int n = p[x] * 2; \ + \ + q[0] = tlut[n]; \ + q[1] = tlut[n + 1]; \ + q += 2; \ + } \ + } \ +} + +/* As above, but the input image may be any unsigned integer type. We have to + * index the lut carefully, and record the number of overflows we detect. + */ +#define loop1g(IN,OUT) { \ + OUT *tlut = (OUT *) st->table[0]; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < ne; x++ ) { \ + int index = p[x]; \ + \ + if( index > st->clp ) { \ + index = st->clp; \ + seq->overflow++; \ + } \ + \ + q[x] = tlut[index]; \ + } \ + } \ +} + +#define loop1cg(IN,OUT) { \ + OUT *tlut = (OUT *) st->table[0]; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < ne; x++ ) { \ + int index = p[x]; \ + \ + if( index > st->clp ) { \ + index = st->clp; \ + seq->overflow++; \ + } \ + \ + q[0] = tlut[index*2]; \ + q[1] = tlut[index*2 + 1]; \ + q += 2; \ + } \ + } \ +} + +/* Map 1-band image through a many-band non-complex lut. + */ +#define loop1m(OUT) { \ + OUT **tlut = (OUT **) st->table; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( i = 0, x = 0; x < np; x++ ) { \ + int n = p[x]; \ + \ + for( z = 0; z < st->nb; z++, i++ ) \ + q[i] = tlut[z][n]; \ + } \ + } \ +} + +/* Map 1-band image through many-band complex lut. + */ +#define loop1cm(OUT) { \ + OUT **tlut = (OUT **) st->table; \ + \ + for( y = to; y < bo; y++ ) { \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); \ + \ + for( x = 0; x < np; x++ ) { \ + int n = p[x] * 2; \ + \ + for( z = 0; z < st->nb; z++ ) { \ + q[0] = tlut[z][n]; \ + q[1] = tlut[z][n+1]; \ + q += 2; \ + } \ + } \ + } \ +} + +/* Map 1-band uint or ushort image through a many-band non-complex LUT. + */ +#define loop1gm(IN,OUT) { \ + OUT **tlut = (OUT **) st->table; \ + \ + for( y = to; y < bo; y++ ) { \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + \ + for( i = 0, x = 0; x < np; x++ ) { \ + int n = p[x]; \ + \ + if( n > st->clp ) { \ + n = st->clp; \ + seq->overflow++; \ + } \ + \ + for( z = 0; z < st->nb; z++, i++ ) \ + q[i] = tlut[z][n]; \ + } \ + } \ +} + +/* Map 1-band uint or ushort image through a many-band complex LUT. + */ +#define loop1cgm(IN,OUT) { \ + OUT **tlut = (OUT **) st->table; \ + \ + for( y = to; y < bo; y++ ) { \ + IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ + OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < np; x++ ) { \ + int n = p[x]; \ + \ + if( n > st->clp ) { \ + n = st->clp; \ + seq->overflow++; \ + } \ + \ + for( z = 0; z < st->nb; z++ ) { \ + q[0] = tlut[z][n*2]; \ + q[1] = tlut[z][n*2 + 1]; \ + q += 2; \ + } \ + } \ + } \ +} + +/* Switch for input types. Has to be uint type! + */ +#define inner_switch( UCHAR, GEN, OUT ) \ + switch( ir->im->BandFmt ) { \ + case IM_BANDFMT_UCHAR: UCHAR( OUT ); break; \ + case IM_BANDFMT_USHORT: GEN( unsigned short, OUT ); break; \ + case IM_BANDFMT_UINT: GEN( unsigned int, OUT ); break; \ + default: \ + im_error( "im_maplut", _( "bad input file" ) ); \ + return( -1 ); \ + } + +/* Switch for LUT types. One function for non-complex images, a + * variant for complex ones. Another pair as well, in case the input is not + * uchar. + */ +#define outer_switch( UCHAR_F, UCHAR_FC, GEN_F, GEN_FC ) \ + switch( st->fmt ) { \ + case IM_BANDFMT_UCHAR: inner_switch( UCHAR_F, GEN_F, \ + unsigned char ); break; \ + case IM_BANDFMT_CHAR: inner_switch( UCHAR_F, GEN_F, \ + char ); break; \ + case IM_BANDFMT_USHORT: inner_switch( UCHAR_F, GEN_F, \ + unsigned short ); break; \ + case IM_BANDFMT_SHORT: inner_switch( UCHAR_F, GEN_F, \ + short ); break; \ + case IM_BANDFMT_UINT: inner_switch( UCHAR_F, GEN_F, \ + unsigned int ); break; \ + case IM_BANDFMT_INT: inner_switch( UCHAR_F, GEN_F, \ + int ); break; \ + case IM_BANDFMT_FLOAT: inner_switch( UCHAR_F, GEN_F, \ + float ); break; \ + case IM_BANDFMT_DOUBLE: inner_switch( UCHAR_F, GEN_F, \ + double ); break; \ + case IM_BANDFMT_COMPLEX: inner_switch( UCHAR_FC, GEN_FC, \ + float ); break; \ + case IM_BANDFMT_DPCOMPLEX: inner_switch( UCHAR_FC, GEN_FC, \ + double ); break; \ + default: \ + im_error( "im_maplut", _( "bad lut file" ) ); \ + return( -1 ); \ + } + +/* Do a map. + */ +static int +gen_map( REGION *or, Seq *seq, IMAGE *in, LutInfo *st ) +{ + REGION *ir = seq->ir; + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int np = r->width; /* Pels across region */ + int ne = IM_REGION_N_ELEMENTS( or ); /* Number of elements */ + int x, y, z, i; + + /* Get input ready. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* Process! + */ + if( st->nb == 1 ) + /* One band lut. + */ + outer_switch( loop1, loop1c, loop1g, loop1cg ) + else + /* Many band lut. + */ + if( ir->im->Bands == 1 ) + /* ... but 1 band input. + */ + outer_switch( loop1m, loop1cm, loop1gm, loop1cgm ) + else + outer_switch( loop, loopc, loopg, loopcg ) + + return( 0 ); +} + +int +im_maplut( IMAGE *in, IMAGE *out, IMAGE *lut ) +{ + LutInfo *st; + + /* Check lut. + */ + if( lut->Coding != IM_CODING_NONE ) { + im_error( "im_maplut", _( "lut is not uncoded" ) ); + return( -1 ); + } + if( lut->Xsize * lut->Ysize > 100000 ) { + im_error( "im_maplut", _( "lut seems very large!" ) ); + return( -1 ); + } + + /* Check input output. Old-style IO from lut, for simplicity. + */ + if( im_piocheck( in, out ) || im_incheck( lut ) ) + return( -1 ); + + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_maplut", _( "input is not uncoded" ) ); + return( -1 ); + } + if( !im_isuint( in ) ) { + im_error( "im_maplut", _( "input is not some unsigned " + "integer type" ) ); + return( -1 ); + } + if( in->Bands != 1 && lut->Bands != 1 && lut->Bands != in->Bands ) { + im_error( "im_maplut", _( "lut should have 1 band, or same " + "number of bands as input, or any number of bands " + "if input has 1 band" ) ); + return( -1 ); + } + if( in->BandFmt == IM_BANDFMT_UCHAR && + lut->Xsize * lut->Ysize != 256 ) { + im_error( "im_maplut", _( "input is uchar and lut does not " + "have 256 elements" ) ); + return( -1 ); + } + + /* Prepare the output header. + */ + if( im_cp_descv( out, in, lut, NULL ) ) + return( -1 ); + + /* Force output to be the same type as lut. + */ + out->Bbits = lut->Bbits; + out->BandFmt = lut->BandFmt; + + /* Output has same number of bands as LUT, unless LUT has 1 band, in + * which case output has same number of bands as input. + */ + if( lut->Bands != 1 ) + out->Bands = lut->Bands; + + /* Make tables. + */ + if( !(st = build_luts( out, lut )) ) + return( -1 ); + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Process! + */ + if( im_generate( out, start_maplut, gen_map, stop_maplut, in, st ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_project.c b/libsrc/histograms_lut/im_project.c new file mode 100644 index 00000000..ecda7aea --- /dev/null +++ b/libsrc/histograms_lut/im_project.c @@ -0,0 +1,299 @@ +/* @(#) Find the horizontal and vertical projections of an image, ie. the sum + * @(#) of pixels in each row and column. Two output images, 1xheight and + * @(#) widthx1, with the largest required bandfmt. + * @(#) + * @(#) int im_project( in, columns, rows ) + * @(#) IMAGE *in; + * @(#) IMAGE *columns, *rows; + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * 20/4/06 + * - from im_histgr() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Accumulate a projection in one of these. + */ +typedef struct { + IMAGE *in; + IMAGE *hout; + IMAGE *vout; + void *columns; + void *rows; +} Project; + +/* For each input bandfmt, the type we accumulate pixels in. + */ +static int project_type[] = { + IM_BANDFMT_UINT, /* IM_BANDFMT_UCHAR */ + IM_BANDFMT_INT, /* IM_BANDFMT_CHAR */ + IM_BANDFMT_UINT, /* IM_BANDFMT_USHORT */ + IM_BANDFMT_INT, /* IM_BANDFMT_SHORT */ + IM_BANDFMT_UINT, /* IM_BANDFMT_UINT */ + IM_BANDFMT_INT, /* IM_BANDFMT_INT */ + IM_BANDFMT_DOUBLE, /* IM_BANDFMT_FLOAT */ + IM_BANDFMT_NOTSET, /* IM_BANDFMT_COMPLEX */ + IM_BANDFMT_DOUBLE, /* IM_BANDFMT_DOUBLE */ + IM_BANDFMT_NOTSET /* IM_BANDFMT_DPCOMPLEX */ +}; + +static Project * +project_new( IMAGE *in, IMAGE *hout, IMAGE *vout ) +{ + Project *project; + int psize = IM_IMAGE_SIZEOF_PEL( hout ); + + if( !(project = IM_NEW( hout, Project )) ) + return( NULL ); + project->in = in; + project->hout = hout; + project->vout = vout; + project->columns = IM_ARRAY( hout, psize * in->Xsize, guchar ); + project->rows = IM_ARRAY( hout, psize * in->Ysize, guchar ); + if( !project->columns || !project->rows ) + return( NULL ); + + memset( project->columns, 0, psize * in->Xsize ); + memset( project->rows, 0, psize * in->Ysize ); + + return( project ); +} + +/* Build a sub-project, based on the main project. + */ +static void * +project_new_sub( IMAGE *out, Project *mproject ) +{ + return( (void *) + project_new( mproject->in, mproject->hout, mproject->vout ) ); +} + +#define ADD_BUFFER( TYPE, Q, P, N ) { \ + TYPE *p = (TYPE *) (P); \ + TYPE *q = (TYPE *) (Q); \ + int n = (N); \ + int i; \ + \ + for( i = 0; i < n; i++ ) \ + q[i] += p[i]; \ +} + +/* Join a sub-project onto the main project. + */ +static int +project_merge( Project *sproject, Project *mproject ) +{ + IMAGE *in = mproject->in; + IMAGE *out = mproject->hout; + int hsz = in->Xsize * in->Bands; + int vsz = in->Ysize * in->Bands; + + assert( sproject->hout == mproject->hout ); + assert( sproject->vout == mproject->vout ); + + /* Add on sub-data. + */ + switch( out->BandFmt ) { + case IM_BANDFMT_UINT: + ADD_BUFFER( guint, mproject->columns, sproject->columns, hsz ); + ADD_BUFFER( guint, mproject->rows, sproject->rows, vsz ); + break; + + case IM_BANDFMT_INT: + ADD_BUFFER( int, mproject->columns, sproject->columns, hsz ); + ADD_BUFFER( int, mproject->rows, sproject->rows, vsz ); + break; + + case IM_BANDFMT_DOUBLE: + ADD_BUFFER( double, mproject->columns, sproject->columns, hsz ); + ADD_BUFFER( double, mproject->rows, sproject->rows, vsz ); + break; + + default: + assert( 0 ); + } + + /* Blank out sub-project to make sure we can't add it again. + */ + memset( sproject->columns, 0, IM_IMAGE_SIZEOF_ELEMENT( out ) * hsz ); + memset( sproject->rows, 0, IM_IMAGE_SIZEOF_ELEMENT( out ) * vsz ); + + return( 0 ); +} + +/* Add an area of pixels. + */ +#define ADD_PIXELS( OUTTYPE, INTYPE ) { \ + OUTTYPE *rows; \ + OUTTYPE *columns; \ + INTYPE *p; \ + \ + rows = ((OUTTYPE *) project->rows) + to * nb; \ + for( y = 0; y < r->height; y++ ) { \ + columns = ((OUTTYPE *) project->columns) + le * nb; \ + p = (INTYPE *) IM_REGION_ADDR( reg, le, y + to ); \ + \ + for( x = 0; x < r->width; x++ ) { \ + for( z = 0; z < nb; z++ ) { \ + columns[z] += p[z]; \ + rows[z] += p[z]; \ + } \ + \ + p += nb; \ + columns += nb; \ + } \ + \ + rows += nb; \ + } \ +} + +/* Add a region to a project. + */ +static int +project_scan( REGION *reg, Project *project ) +{ + Rect *r = ®->valid; + int le = r->left; + int to = r->top; + int nb = project->in->Bands; + int x, y, z; + + switch( project->in->BandFmt ) { + case IM_BANDFMT_UCHAR: + ADD_PIXELS( guint, guchar ); + break; + + case IM_BANDFMT_CHAR: + ADD_PIXELS( int, char ); + break; + + case IM_BANDFMT_USHORT: + ADD_PIXELS( guint, gushort ); + break; + + case IM_BANDFMT_SHORT: + ADD_PIXELS( int, short ); + break; + + case IM_BANDFMT_UINT: + ADD_PIXELS( guint, guint ); + break; + + case IM_BANDFMT_INT: + ADD_PIXELS( int, int ); + break; + + case IM_BANDFMT_FLOAT: + ADD_PIXELS( double, float ); + break; + + case IM_BANDFMT_DOUBLE: + ADD_PIXELS( double, double ); + break; + + default: + assert( 0 ); + } + + return( 0 ); +} + +int +im_project( IMAGE *in, IMAGE *hout, IMAGE *vout ) +{ + Project *mproject; + int y; + + /* Check images. PIO from in, WIO to out. + */ + if( im_pincheck( in ) || im_outcheck( hout ) || im_outcheck( vout ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_project", _( "uncoded images only" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_project", _( "non-complex images only" ) ); + return( -1 ); + } + + /* Make the output images. + */ + if( im_cp_desc( hout, in ) || im_cp_desc( vout, in ) ) + return( -1 ); + + hout->Xsize = 1; + hout->BandFmt = project_type[in->BandFmt]; + hout->Bbits = im_bits_of_fmt( hout->BandFmt ); + hout->Type = IM_TYPE_HISTOGRAM; + + vout->Ysize = 1; + vout->BandFmt = project_type[in->BandFmt]; + vout->Bbits = im_bits_of_fmt( hout->BandFmt ); + vout->Type = IM_TYPE_HISTOGRAM; + + /* Build the main project we accumulate data in. + */ + if( !(mproject = project_new( in, hout, vout )) ) + return( -1 ); + + /* Accumulate data. + */ + if( im_iterate( in, + project_new_sub, project_scan, project_merge, mproject, NULL ) ) + return( -1 ); + + if( im_setupout( hout ) || im_setupout( vout ) ) + return( -1 ); + + if( im_writeline( 0, vout, (PEL *) mproject->columns ) ) + return( -1 ); + for( y = 0; y < in->Ysize; y++ ) + if( im_writeline( y, hout, (PEL *) mproject->rows + + y * IM_IMAGE_SIZEOF_PEL( hout ) ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/im_stdif.c b/libsrc/histograms_lut/im_stdif.c new file mode 100644 index 00000000..7450e856 --- /dev/null +++ b/libsrc/histograms_lut/im_stdif.c @@ -0,0 +1,260 @@ +/* @(#) Functions which calculates statistical differenciating according to + * @(#) the formula given in page 45 of the book "An intro to digital image + * @(#) processing" by Wayne Niblack + * @(#) + * @(#) At point (i,j) the output is given by the eqn: + * @(#) + * @(#) vout(i,j) = a*m0 +(1-a)*meanv + + * @(#) (vin(i,j) - meanv) * beta*sigma0/(sigma0+beta*stdv) + * @(#) + * @(#) Values a, m0, beta and sigma0 are entered + * @(#) meanv and stdv are the values calculated over a moving window + * @(#) xwin and ywin are the sizes of the used window + * @(#) The resultant coefficients are written as floats + * @(#) in out which has a size of in + * @(#) + * @(#) int im_stdif(in, im, alpha, mean0, beta, sigma0, xwin, ywin) + * @(#) IMAGE *in, *out; + * @(#) int xwin, ywin; + * @(#) double alpha, mean0, beta, sigma0; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : + * 6/8/93 JC + * - now works for odd window sizes + * - ANSIfication + * 25/5/95 JC + * - new IM_ARRAY() macro + * 25/1/96 JC + * - im_lhisteq() adapted to make new im_stdif() + * - now partial, plus rolling window + * - 5x faster, amazingly + * - works + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Hold global stuff here. + */ +typedef struct { + int xwin, ywin; /* Parameters */ + double a, m0, b, s0; +} StdifInfo; + +/* stdif generate function. + */ +static int +stdif_gen( REGION *or, REGION *ir, IMAGE *in, StdifInfo *inf ) +{ + Rect irect; + + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int ri = IM_RECT_RIGHT(r); + + int x, y, i, j; + int lsk; + + int coff; /* Offset to move to centre of window */ + int npel = inf->xwin * inf->ywin; + + /* What part of ir do we need? + */ + irect.left = or->valid.left; + irect.top = or->valid.top; + irect.width = or->valid.width + inf->xwin; + irect.height = or->valid.height + inf->ywin; + if( im_prepare( ir, &irect ) ) + return( -1 ); + + lsk = IM_REGION_LSKIP( ir ); + coff = lsk * (inf->ywin/2) + inf->xwin/2; + + for( y = to; y < bo; y++ ) { + /* Get input and output pointers for this line. + */ + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + PEL *p1, *p2; + int sum = 0; + int sum2 = 0; + + /* Precompute some factors. + */ + double f1 = inf->a * inf->m0; + double f2 = 1.0 - inf->a; + double f3 = inf->b * inf->s0; + + /* Find sum, sum of squares for the start of this line. + */ + for( p1 = p, j = 0; j < inf->ywin; j++, p1 += lsk ) + for( p2 = p1, i = 0; i < inf->xwin; i++, p2++ ) { + int t = *p2; + + sum += t; + sum2 += t * t; + } + + /* Loop for output pels. + */ + for( x = le; x < ri; x++, p++ ) { + /* Find stats. + */ + double mean = (double)sum / npel; + double var = (double)sum2 / npel - (mean * mean); + double sig = sqrt( var ); + + /* Transform. + */ + double res = f1 + f2*mean + ((double) p[coff] - mean) * + (f3 / (inf->s0 + inf->b*sig)); + + /* And write. + */ + if( res < 0.0 ) + *q++ = 0; + else if( res >= 256.0 ) + *q++ = 255; + else + *q++ = res + 0.5; + + /* Adapt sums - remove the pels from the left hand + * column, add in pels for a new right-hand column. + */ + for( p1 = p, j = 0; j < inf->ywin; j++, p1 += lsk ) { + int t1 = p1[0]; + int t2 = p1[inf->xwin]; + + sum -= t1; + sum2 -= t1 * t1; + + sum += t2; + sum2 += t2 * t2; + } + } + } + + return( 0 ); +} + +int +im_stdif_raw( IMAGE *in, IMAGE *out, + double a, double m0, double b, double s0, + int xwin, int ywin ) +{ + StdifInfo *inf; + + if( m0 < 0 || m0 > 255 || a < 0 || a > 1.0 || b < 0 || b > 2 || + s0 < 0 || s0 > 255 ) { + im_errormsg( "im_stdif: parameters out of range" ); + return( -1 ); + } + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Bbits != IM_BBITS_BYTE || in->BandFmt != IM_BANDFMT_UCHAR || + in->Bands != 1 || in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_stdif: one band uchar uncoded only" ); + return( -1 ); + } + if( xwin > in->Xsize || ywin > in->Ysize ) { + im_errormsg( "im_stdif: window too large" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= xwin; + out->Ysize -= ywin; + + /* Save parameters. + */ + if( !(inf = IM_NEW( out, StdifInfo )) ) + return( -1 ); + inf->xwin = xwin; + inf->ywin = ywin; + inf->a = a; + inf->m0 = m0; + inf->b = b; + inf->s0 = s0; + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Write the hist. + */ + if( im_generate( out, + im_start_one, stdif_gen, im_stop_one, in, inf ) ) + return( -1 ); + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_stdif( IMAGE *in, IMAGE *out, + double a, double m0, double b, double s0, + int xwin, int ywin ) +{ + IMAGE *t1 = im_open_local( out, "im_stdif:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, xwin / 2, ywin / 2, + in->Xsize + xwin - 1, + in->Ysize + ywin - 1 ) || + im_stdif_raw( t1, out, a, m0, b, s0, xwin, ywin ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/histograms_lut/man3/Makefile.am b/libsrc/histograms_lut/man3/Makefile.am new file mode 100644 index 00000000..4310d6f3 --- /dev/null +++ b/libsrc/histograms_lut/man3/Makefile.am @@ -0,0 +1,27 @@ +man_MANS = \ + im_gammacorrect.3 \ + im_heq.3 \ + im_hist.3 \ + im_histeq.3 \ + im_histcum.3 \ + im_histnorm.3 \ + im_histgr.3 \ + im_histnD.3 \ + im_histplot.3 \ + im_histspec.3 \ + im_hsp.3 \ + im_identity.3 \ + im_identity_ushort.3 \ + im_invertlut.3 \ + im_buildlut.3 \ + im_lhisteq.3 \ + im_lhisteq_raw.3 \ + im_maplut.3 \ + im_project.3 \ + im_stdif.3 \ + im_tone_analyse.3 \ + im_tone_build.3 \ + im_tone_map.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/histograms_lut/man3/im_buildlut.3 b/libsrc/histograms_lut/man3/im_buildlut.3 new file mode 100644 index 00000000..22809486 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_buildlut.3 @@ -0,0 +1,38 @@ +.TH IM_BUILDLUT 3 "June 2006" +.SH NAME +im_buildlut \- build a LUT from a set of x/y points +.SH SYNOPSIS +#include + +int +.br +im_buildlut( DOUBLEMASK *input, IMAGE *output ) + +.SH DESCRIPTION +.B im_buildlut(3) +constructs a LUT, interpolating a set of x/y points. Interpolation is strictly +piecewise linear. For example, if the input is: + + 12 100 + 14 110 + 18 120 + +we generate + + 100 (12) + 105 + 110 + 112.5 + 115 + 117.5 (17) + +the x axis (12 .. 17) is implied. The x/y points don't need to be +sorted: we do that. You can have several Ys ... each becomes a band in the +output LUT. + +.SH RETURN VALUE +-1 on error, otherwise 0 +.SH SEE ALSO +im_invertlut(3), im_identity(3). +.SH COPYRIGHT +2006, Imperial College diff --git a/libsrc/histograms_lut/man3/im_gammacorrect.3 b/libsrc/histograms_lut/man3/im_gammacorrect.3 new file mode 100644 index 00000000..ca4a8fa5 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_gammacorrect.3 @@ -0,0 +1,24 @@ +.TH GAMMA 3 "10 May 1991" +.SH NAME +im_gammacorrect \- carry out gamma correction +.SH SYNOPSIS +#include + +int im_gammacorrect(in, out, exponent) +.br +IMAGE *in, *out; +.br +double exponent; + +.SH DESCRIPTION +im_gammacorrect performs gamma correction to in. The result in written in out. +The correction is carried out by creating a lookup table using the double +exponent and mapping in through it. The exponent is applied on a ramp lut and +the resultant lut is scaled. All channels of im are mapped through the lookup +table. +.SH SEE ALSO +im_histgr(3), im_heq(3), im_histeq(3), im_identity(3), im_maplut(3) +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/histograms_lut/man3/im_heq.3 b/libsrc/histograms_lut/man3/im_heq.3 new file mode 100644 index 00000000..4715cd8d --- /dev/null +++ b/libsrc/histograms_lut/man3/im_heq.3 @@ -0,0 +1,53 @@ +.TH IM_HEQ 3 "10 May 1991" +.SH NAME +im_heq, im_lhisteq, im_lhisteq_raw, im_hsp \- process an image using grey level transformations +.SH SYNOPSIS +#include + +int im_heq( in, out, bandno ) +.br +IMAGE *in, *out; +.br +int bandno; + +int im_lhisteq( in, out, xw, yw ) +.br +IMAGE *in, *out; +.br +int xw, yw; + +int im_hsp( in, ref, out ) +.br +IMAGE *in, *ref, *out; + +.SH DESCRIPTION +im_heq() +histogram equalises the unsigned char image held by the IMAGE descriptor +in. The result is written to the IMAGE descriptor out. +If bandno is -1 then all input bands are equalised independently. In all +other cases the input image is equalised using the histogram of bandno only. +The latter processing produces better results. + +im_hsp() +maps in to out with histogram specified by the ref. All images should be +unsigned char. Each band of the output image is specified according to the +distribution of grey levels of the reference image according to +im_histspec(3). + +im_lhisteq() +histogram equalises the one channel unsigned char image pointed to by the +Image descriptor in. The result is written to the IMAGE descriptor out. +The histogram equalisation is based on a window of size xw by yw centered at +the current location of each input pixel. The produced image has a black +border of sizes xw/2 (left), xw-xw/2 (right), yw/2 (top) and yw-yw/2 (bottom). + +im_lhisteq_raw() +is as above, but does not add the black border to the output image. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_histgr(3), im_histplot(3), im_histspec(3), im_lineprof(3), +im_stdif(3). +.SH COPYRIGHT +1991--1996 The National Gallery and Birkbeck College diff --git a/libsrc/histograms_lut/man3/im_hist.3 b/libsrc/histograms_lut/man3/im_hist.3 new file mode 100644 index 00000000..32170a06 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_hist.3 @@ -0,0 +1,36 @@ +.TH IM_HIST 3 "10 May 1991" +.SH NAME +im_hist \- create a displayable histogram from an image +.SH SYNOPSIS +.B #include + +.B int im_hist( in, plotofhist, bandno ) +.br +.B IMAGE *in, *plotofhist; +.br +.B int bandno; +.SH DESCRIPTION +.B im_hist() +creates a displayable histogram file for the image held by the image +descriptor +.B in. +The created displayable histogram is held in the IMAGE +descriptor +.B plotofhist. +If +.B bandno +== -1 a displayable histogram of all input bands +is created else the histogram of bandno only is created. + +See +.B im_histplot(3) +for rules on the size and scaling og the result. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_histgr(3), im_histplot(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/histograms_lut/man3/im_histcum.3 b/libsrc/histograms_lut/man3/im_histcum.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histcum.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_histeq.3 b/libsrc/histograms_lut/man3/im_histeq.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histeq.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_histgr.3 b/libsrc/histograms_lut/man3/im_histgr.3 new file mode 100644 index 00000000..67fb50e5 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histgr.3 @@ -0,0 +1,142 @@ +.TH IM_HISTGR 3 "10 May 1991" +.SH NAME +im_histcum, +im_histeq, +im_histgr, +im_histnD, +im_histnorm, +im_histspec, +im_identity, +im_identity_ushort, +im_ismonotonic +\- create, display and process histograms or luts +.SH SYNOPSIS +#include + +int im_histgr( in, out, bandno ) +.br +IMAGE *in, *out; +.br +int bandno; + +int im_histnD( in, out, bins ) +.br +IMAGE *in, *out; +.br +int bins; + +int im_histcum(hist, lut) +.br +IMAGE *hist, *lut; + +int im_histnorm(hist, lut) +.br +IMAGE *hist, *lut; + +int im_histeq(hist, lut) +.br +IMAGE *hist, *lut; + +int im_histspec(histin, histref, lut) +.br +IMAGE *histin, *histref, *lut; + +int im_identity(lut, bands) +.br +IMAGE *lut; +.br +int bands; + +int im_identity_ushort(lut, bands, sz) +.br +IMAGE *lut; +.br +int bands; +.br +int sz; + +int im_ismonotonic( IMAGE *lut, int *out ) + +.SH DESCRIPTION +.B im_histgr(3) +writes the histogram of image in to image out. If bandno is -1, +then out will have the same number of bands as in, and each band of out will +have the histogram of the corresponding band of in. Otherwise, bandno selects +the band of the image for which the histogram will be found, numbering from +zero. + +Image in may be either FMTUCHAR or FMTUSHORT. If in is uchar, then out will +have 256 elements, one for each possible pixel value. If in is ushort, then +.B im_histgr(3) +finds the maximum of in, and outputs a histogram with max + 1 +elements. + +For example, suppose you have an image from a 12-bit camera, where +each pixel is in the range [0,4095]. Calling +.B im_histgr(3) +for this image will +make a histogram with at most 4096 elements. If the histogram is smaller than +this, then it means that the right hand end of the histogram was all zero, and +has not been generated. + +Also check +.B im_histnD(3) +below for another way to make histograms. + +.B im_histnD(3) +makes a n-dimensional histogram from an n-band image (1, 2 and 3 bands only). +Because 3D histograms can get very large very quickly, the +.B bins +parameter sets the length of each dimension, that is, the number of bins the +possible numeric range of the image is divided into. + +Unsigned 8 and 16 bit images only. + +Use +.B im_histplot(3) +to graph the histogram for visualisation. See the separate manpage. + +.B im_histcum(3) +forms a cumulative histogram. It works for any unsigned integer format. + +.B im_histnorm(3) +normalises a histogram. The maximum histogram value becomes equal to the +number of pixels in the histogram. In effect, the histogram +becomes 'square'. Each channel is normalised separately. + +.B im_histeq(3) +takes as input a histogram held by the IMAGE descriptor hist and creates an +unsigned char look up table (held by the IMAGE descriptor lut) which when +applied on the original image, (with histogram held by hist) histogram +equalises it. Histogram equalisation is carried out for each band of hist +individually. + +.B im_histspec(3) +creates a lut for transforming an image with histogram held by histin +according to the pdf (probability density function) of a reference image with +histogram held by histref. histin and histref should have the same number +of bands. + +.B im_identity(3) +creates an look-up-table with Xsize=256, Ysize=1, Bands=bands, Bbits=BBBYTE, +BandFmt=FMTUCHAR, Type=LUT, which is held by the IMAGE descriptor lut. The +created image consist a bands linear (ramp) lut and is the basis for building +look-up tables. + +.B im_identity_ushort(3) +creates an look-up-table with Xsize=sz, Ysize=1, Bands=bands, Bbits=BBSHORT, +BandFmt=FMTUSHORT, Type=LUT, which is held by the IMAGE descriptor lut. The +created image consist of a linear (ramp) lut and is the basis for building +look-up tables. + +.B im_ismonotonic(3) +sets out to non-zero if the look-up table (or histogram) in lut is monotonic, +that is, if it's slope is always >0. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_hist(3), im_hsp(3), im_heq(3), im_maplut(3), im_tone_map(3), +im_histplot(3). +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1991 \- 1994. diff --git a/libsrc/histograms_lut/man3/im_histnD.3 b/libsrc/histograms_lut/man3/im_histnD.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histnD.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_histnorm.3 b/libsrc/histograms_lut/man3/im_histnorm.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histnorm.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_histplot.3 b/libsrc/histograms_lut/man3/im_histplot.3 new file mode 100644 index 00000000..121995eb --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histplot.3 @@ -0,0 +1,47 @@ +.TH IM_HISTPLOT 3 "10 May 1991" +.SH NAME +im_histplot \- graph an image of one horizontal or vertical line +.SH SYNOPSIS +#include + +int im_histplot(in, out) +.br +IMAGE *in, *out; + +.SH DESCRIPTION +.B im_histplot(3) +plots a graph of a horizontal or vertical image file with +one line. It is suitable for displaying the output of histogram functions +such as im_histgr(3) and for displaying line profiles. + +Note that if you try to directly plot the result of +.B im_histgr(3) +you will often get a very, very large image, since +.B im_histplot(3) +will draw an image as high (or as wide) as the largest pixel +value in the image (potentially huge). Use +.B im_histnorm(3) +to normalise the histogram maximum before plotting. + +The input image should be either one horizontal line (Ysize=1) or +one vertical line (Xsize=1). If the image is FMTUCHAR, the graph is drawn +as 256 by line-length pixels. For all other unsigned integer types, +.B im_histplot(3) +finds the image maximum, and draws the graph as max by line-length pixels. + +For signed integer types, +.B im_histplot(3) +finds minimum and maximum, moves min up to zero, and +draws the graph as min + max by line-lenth pixels. + +For float types, +.B im_histplot(3) +finds minimum and maximum, and scales the image range so as to make the +graph square. + +.SH RETURNED VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_histgr(3), im_histnorm(3), im_extract(3). +.SH COPYRIGHT +National Gallery and Birkbeck College, 1995 diff --git a/libsrc/histograms_lut/man3/im_histspec.3 b/libsrc/histograms_lut/man3/im_histspec.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_histspec.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_hsp.3 b/libsrc/histograms_lut/man3/im_hsp.3 new file mode 100644 index 00000000..0d4a60ee --- /dev/null +++ b/libsrc/histograms_lut/man3/im_hsp.3 @@ -0,0 +1 @@ +.so man3/im_heq.3 diff --git a/libsrc/histograms_lut/man3/im_identity.3 b/libsrc/histograms_lut/man3/im_identity.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_identity.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_identity_ushort.3 b/libsrc/histograms_lut/man3/im_identity_ushort.3 new file mode 100644 index 00000000..44ba7c26 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_identity_ushort.3 @@ -0,0 +1 @@ +.so man3/im_histgr.3 diff --git a/libsrc/histograms_lut/man3/im_invertlut.3 b/libsrc/histograms_lut/man3/im_invertlut.3 new file mode 100644 index 00000000..e1719ca0 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_invertlut.3 @@ -0,0 +1,41 @@ +.TH IM_INVERTLUT 3 "June 2001" +.SH NAME +im_invertlut \- turn a set of greyscale measurements into a gamma-correcting +LUT +.SH SYNOPSIS +#include + +int +.br +im_invertlut( DOUBLEMASK *input, IMAGE *output, int lut_size ) + +.SH DESCRIPTION +Given a mask of target values and real values, generate a LUT which +will map reals to targets. Handy for linearising images from +measurements of a colour chart. All values in [0,1]. +Extrapolate head and tail to 0 and 1. + +Eg. input like: + + 4 3 + 0.1 0.2 0.3 0.1 + 0.2 0.4 0.4 0.2 + 0.7 0.5 0.6 0.3 + +means a patch with 10% reflectance produces an image with 20% in +channel 1, 30% in channel 2, and 10% in channel 3. A patch with 20% +reflectance makes an image with 40% red, 40% green and 20% blue, and so on. + +Inputs don't need to be sorted (we do that). Generate any precision +LUT ... typically ask for 256 elements. + +It won't work too well for non-monotonic camera responses (should fix this). + +Interpolation is simple piecewise linear; ought to do something better really. + +.SH RETURN VALUE +-1 on error, otherwise 0 +.SH SEE ALSO +im_histgr(3), im_hsp(3), im_heq(3), im_identity(3). +.SH COPYRIGHT +2001, National Gallery diff --git a/libsrc/histograms_lut/man3/im_lhisteq.3 b/libsrc/histograms_lut/man3/im_lhisteq.3 new file mode 100644 index 00000000..0d4a60ee --- /dev/null +++ b/libsrc/histograms_lut/man3/im_lhisteq.3 @@ -0,0 +1 @@ +.so man3/im_heq.3 diff --git a/libsrc/histograms_lut/man3/im_lhisteq_raw.3 b/libsrc/histograms_lut/man3/im_lhisteq_raw.3 new file mode 100644 index 00000000..0d4a60ee --- /dev/null +++ b/libsrc/histograms_lut/man3/im_lhisteq_raw.3 @@ -0,0 +1 @@ +.so man3/im_heq.3 diff --git a/libsrc/histograms_lut/man3/im_maplut.3 b/libsrc/histograms_lut/man3/im_maplut.3 new file mode 100644 index 00000000..a5eb245b --- /dev/null +++ b/libsrc/histograms_lut/man3/im_maplut.3 @@ -0,0 +1,46 @@ +.TH IM_MAPLUT 3 "10 May 1991" +.SH NAME +im_maplut \- map an image through a lookup table +.SH SYNOPSIS +#include + +int im_maplut(in, out, lut) +.br +IMAGE *in, *out, *lut; + +.SH DESCRIPTION +im_maplut() maps an image through another image, acting as a LUT (Look Up +Table). The lut may have any type, and the output image will be of that type. + +The input image must be an unsigned integer types, that is, it must be one of +FMTUCHAR, FMTUSHORT or FMTUINT. + +If the input is FMTUCHAR, then the LUT must have 256 elements, in other words, +lut->Xsize * lut->Ysize == 256. + +If the input is FMTUSHORT or FMTUINT, then the lut may have any number of +elements, and input pels whose value is greater than lut->Xsize * lut->Ysize +are mapped with the last LUT element. The function counts and prints the +number of image elements which overflow in this way. + +As regards bands, there are three cases: + + - If LUT has one band, then the input may have any number of bands, and + each band will pass through the same LUT. + + - If LUT has the same number of bands as the input, then each band of the + input will be LUTed separately. + + - If the input has one band, then the LUT may have any number of bands, and + the output will have the same number of bands as the LUT. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_histgr(3), im_hsp(3), im_heq(3), im_identity(3). +.SH COPYRIGHT +1995, National Gallery and Birkbeck College +.SH AUTHORS +J. Cupitt, 1995 +.br +N. Dessipris \- 10/05/1991 diff --git a/libsrc/histograms_lut/man3/im_project.3 b/libsrc/histograms_lut/man3/im_project.3 new file mode 100644 index 00000000..9dd16f4c --- /dev/null +++ b/libsrc/histograms_lut/man3/im_project.3 @@ -0,0 +1,34 @@ +.TH IM_PROJECT 3 "10 April 2006" +.SH NAME +im_project \- find horizontal and vertical projections +.SH SYNOPSIS +#include + +int im_histplot(IMAGE *in, IMAGE *hout, IMAGE *vout) + +.SH DESCRIPTION +.B im_project(3) +finds the horizontal and vertical projections of an image, that is, the sum of +each row and the sum of each column. + +The +.B hout +result image is 1 x in->Ysize pixels, where each pixel is the sum of all the +pixels in that row of the input image. +The +.B vout +result image is in->Xsize x 1 pixels, where each pixel is the sum of all the +pixels in that column of the input image. + +The output images are of the largest appropriate type for the input image: so +unsigned integer images produce UINT output, and so on. The output images have +the same number of bands as the input. + +The operation does not work for complex images, or for coded images. + +.SH RETURNED VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_histplot(3), im_histnorm(3), im_extract(3). +.SH COPYRIGHT +Imperial College, 2006 diff --git a/libsrc/histograms_lut/man3/im_stdif.3 b/libsrc/histograms_lut/man3/im_stdif.3 new file mode 100644 index 00000000..d13a29a7 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_stdif.3 @@ -0,0 +1,54 @@ +.TH IM_STDIF 3 "10 May 1991" +.SH NAME +im_stdif, im_stdif_raw \- statistical differentiation of an image +.SH SYNOPSIS +.B #include + +int im_stdif(in, out, a, m0, b, s0, xw, yw) +.br +IMAGE *in, *out; +.br +double a, m0, b, s0; +.br +int xw, yw; + +int im_stdif_raw(in, out, a, m0, b, s0, xw, yw) +.br +IMAGE *in, *out; +.br +double a, m0, b, s0; +.br +int xw, yw; + +.SH DESCRIPTION +im_stdif() preforms statistical differencing according to the formula +given in page 45 of the book "An Introduction to Digital Image Processing" by +Wayne Niblack. This transformation emphasises the way in which a pel differs +statistically from its neighbours. It is useful for enhancing low-contrast +images with lots of detail, such as X-ray plates. + +At point (i,j) the output is given by the eqn: + + vout(i,j) = a*m0 + (1-a)*meanv + + (vin(i,j) - meanv) * (b*s0) / (s0+b*stdv) + +Values a, m0, b and s0 are entered, while meanv and stdv are the values +calculated over a moving window of size xw, yw centred on pixel (i,j). m0 is the +new mean, a is the weight given to it. s0 is the new standard deviation, b is +the weight given to it. Try: + + im_stdif $VIPSHOME/pics/huysum.v fred.v 0.5 128 0.5 50 11 11 + +The opreation works on one-band UCHAR images only, and writes a one-band UCHAR +image as its result. The output image has the same size as the input - a black +border is added to mark uncomputable pixels. + +im_stdif_raw() behaves exactly as im_stdif(), but does not add the border of +black pixels. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_lhisteq(3), im_histgr(3), im_hsp(3), im_heq(3). +.SH COPYRIGHT +1991-1996, The National Gallery and Birkbeck College diff --git a/libsrc/histograms_lut/man3/im_tone_analyse.3 b/libsrc/histograms_lut/man3/im_tone_analyse.3 new file mode 100644 index 00000000..adbb59b6 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_tone_analyse.3 @@ -0,0 +1 @@ +.so man3/im_tone_build.3 diff --git a/libsrc/histograms_lut/man3/im_tone_build.3 b/libsrc/histograms_lut/man3/im_tone_build.3 new file mode 100644 index 00000000..2ef1615c --- /dev/null +++ b/libsrc/histograms_lut/man3/im_tone_build.3 @@ -0,0 +1,151 @@ +.TH TONE 3 "10 May 1991" +.SH NAME +im_tone_build, im_tone_map, im_tone_analyse, im_tone_build_range \- tone-curve adjustment of LAB +images +.SH SYNOPSIS + +int +.br +im_tone_build_range( +.br + int in_max, int out_max, +.br + IMAGE *lut, +.br + double Lb, double Lw, +.br + double Ps, double Pm, double Ph, +.br + double S, double M, double H ) + +int +.br +im_tone_build( +.br + IMAGE *lut, +.br + double Lb, double Lw, +.br + double Ps, double Pm, double Ph, +.br + double S, double M, double H ) + +int +.br +im_tone_analyse( +.br + IMAGE *in, +.br + IMAGE *lut, +.br + double Ps, double Pm, double Ph, +.br + double S, double M, double H ) + +int +.br +im_tone_map( IMAGE *in, IMAGE *out, IMAGE *lut ) + +.SH DESCRIPTION +Various functions relating to tone curve adjustment. Example tone curve: + + repro L* out + ^ + | + 100 | . . . . . . . . . . . . . . . .* + | * . + Lw | . . . . . . . . . . . . . .* . + | * . . + | /* . . + | / * . . + | / .* . . + | * * /* *. . . + | * * / . . . + | * / . . . . + | * / . . . . + | * / . . . . + | * / . . . . . + | */ . . . . . + Lb | . .* . . . . . + | * . . . . . . + |* . . . . . . + 0 +---------------------------------------> + 0 Lb Ls Lm Lh Lw 100 L* in + +.B im_tone_build_range(3) +generates a tone curve for the adjustment of image levels. The curve is +an unsigned 16-bit image with (in_max + 1) entries, each in the range [0, +out_max]. + +The parameters are +expressed as 0-100, as in LAB colour space, but you specify the scaling for the +input and output images with the in_max and out_max parameters. + +Parameters: + + Lb - black point + Lw - white point + +Both in L* units, ie. in the range [0,100]. These should be set by histogram +analysis of the image to be transformed to 0.1% and 99.9% of the full range of +the image. See im_tone_analyse() below. + + Ps - shadow point + Pm - mid-tone point + Ph - highlight point + +All in [0,1], meaning max of shadow section of curve should be positioned +at Lb+Ps(Lw-Lb), etc. Suggested values: Ps, Pm, Ph should be 0.2, 0.5 and 0.8. +Ps is limited to the range [0.1,0.3], Pm to the range [0.4,0.6] and Ph to +[0.7,0.9]. + + S - shadow adjustment factor (+/- 15) + M - mid-tone adjustment factor (+/- 30) + H - highlight adjustment factor (+/- 15) + +These are the principal parameters, controlling the brightness in the shadow, +mid-tone and highlight areas. Suggested values: + + 0, 0, 0 - no change to input image + 5, 0, -2 - boost shadows a little, depress highlights slightly + +.B im_tone_build(3) +is a convenience function that calls +.B im_tone_build_range(3) +with ranges suitable for tone correcting a LABQ image to a LABS image. + +Use +.B im_ismonotonic(3) +to check that the slope of your tone curve is always >0, +use +.B im_histplot(3) +to graph the curve, use +.B im_tone_map(3) +to apply your curve to an image. + +.B im_tone_map(3) +map just the L channel of a LabQ or LabS image through a tone +curve. + +.B im_tone_analyse(3) +find the histogram of a LabS or LabQ image and use that to +set the Ln and Lw parameters of +.B im_tone_build(3). +All other parameters as above. + +Example: + + example% im_tone_analyse $VIPSHOME/pics/master.v /tmp/lut.v \ + 0.2 0.5 0.8 6.3 0.8 -3 + example% im_ismonotonic /tmp/lut.v + 255 + example% im_tone_map $VIPSHOME/pics/master.v /tmp/master2.v /tmp/lut.v + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_histplot(3), im_ismonotonic(3). +.SH COPYRIGHT +1995, National Gallery +.SH AUTHORS +J. Cupitt diff --git a/libsrc/histograms_lut/man3/im_tone_map.3 b/libsrc/histograms_lut/man3/im_tone_map.3 new file mode 100644 index 00000000..adbb59b6 --- /dev/null +++ b/libsrc/histograms_lut/man3/im_tone_map.3 @@ -0,0 +1 @@ +.so man3/im_tone_build.3 diff --git a/libsrc/histograms_lut/tone.c b/libsrc/histograms_lut/tone.c new file mode 100644 index 00000000..e0016d97 --- /dev/null +++ b/libsrc/histograms_lut/tone.c @@ -0,0 +1,528 @@ +/* @(#) Various functions relating to tone curve adjustment. + * @(#) + * @(#) im_tone_build: generate tone curve for adjustment of LabQ image. LUT we + * @(#) make is always 1024 elements, each element is a LabS L value. + * @(#) + * @(#) Base parameters: + * @(#) + * @(#) Lb - black point + * @(#) Lw - white point (both [0,100]) + * @(#) + * @(#) Ps - shadow point + * @(#) Pm - mid-tone point + * @(#) Ph - highlight point + * @(#) + * @(#) All 0-1, meaning max of shadow section of curve should be positioned + * @(#) at Lb+Ps(Lw-Lb), etc. Suggest Lb, Lw should be set by histogram + * @(#) analysis to 0.1% and 99.9%; Ps, Pm, Ph should be 0.2, 0.5, 0.8. + * @(#) + * @(#) Main parameters: + * @(#) + * @(#) S - shadow adjustment factor (+/- 30) + * @(#) M - mid-tone adjustment factor (+/- 30) + * @(#) H - highlight adjustment factor (+/- 30) + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_tone_build( + * @(#) IMAGE *lut, + * @(#) double Lb, double Lw, + * @(#) double Ps, double Pm, double Ph, + * @(#) double S, double M, double H ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) im_ismonotonic: test any LUT for monotonicity --- set out to non-zero + * @(#) if lut is monotonic. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_ismonotonic( IMAGE *lut, int *out ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) im_tone_map: map just the L channel of a LabQ or LabS image through + * @(#) a LUT. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_tone_map( IMAGE *in, IMAGE *out, IMAGE *lut ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * @(#) im_tone_analyse: find the histogram of a LabS or LabQ image and use + * @(#) that to set the Ln and Lw parameters of im_tone_build() + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_tone_analyse( + * @(#) IMAGE *in, + * @(#) IMAGE *lut, + * @(#) double Ps, double Pm, double Ph, + * @(#) double S, double M, double H ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Author: John Cupitt + * Written on: 18/7/1995 + * 17/9/96 JC + * - restrictions on Ps, Pm, Ph relaxed + * - restrictions on S, M, H relaxed + * 25/7/01 JC + * - patched for im_extract_band() change + * 11/7/04 + * - generalised to im_tone_build_range() ... so you can use it for any + * image ,not just LabS + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Parameters for tone curve formation. + */ +typedef struct { + /* Parameters. + */ + double Lb, Lw; + double Ps, Pm, Ph; + double S, M, H; + + /* Derived values. + */ + double Ls, Lm, Lh; +} ToneShape; + +/* Calculate shadow curve. + */ +static double +shad( ToneShape *ts, double x ) +{ + double x1 = (x - ts->Lb) / (ts->Ls - ts->Lb); + double x2 = (x - ts->Ls) / (ts->Lm - ts->Ls); + double out; + + if( x < ts->Lb ) + out = 0; + else if( x < ts->Ls ) + out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; + else if( x < ts->Lm ) + out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; + else + out = 0; + + return( out ); +} + +/* Calculate mid-tone curve. + */ +static double +mid( ToneShape *ts, double x ) +{ + double x1 = (x - ts->Ls) / (ts->Lm - ts->Ls); + double x2 = (x - ts->Lm) / (ts->Lh - ts->Lm); + double out; + + if( x < ts->Ls ) + out = 0; + else if( x < ts->Lm ) + out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; + else if( x < ts->Lh ) + out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; + else + out = 0; + + return( out ); +} + +/* Calculate highlight curve. + */ +static double +high( ToneShape *ts, double x ) +{ + double x1 = (x - ts->Lm) / (ts->Lh - ts->Lm); + double x2 = (x - ts->Lh) / (ts->Lw - ts->Lh); + double out; + + if( x < ts->Lm ) + out = 0; + else if( x < ts->Lh ) + out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; + else if( x < ts->Lw ) + out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; + else + out = 0; + + return( out ); +} + +/* Generate a point on the tone curve. Everything is 0-100. + */ +static double +tone_curve( ToneShape *ts, double x ) +{ + double out; + + out = x + + ts->S * shad( ts, x ) + ts->M * mid( ts, x ) + + ts->H * high( ts, x ); + + return( out ); +} + +int +im_tone_build_range( IMAGE *out, + int in_max, int out_max, + double Lb, double Lw, + double Ps, double Pm, double Ph, + double S, double M, double H ) +{ + ToneShape *ts = IM_NEW( out, ToneShape ); + unsigned short lut[65536]; + int i; + + /* Check args. + */ + if( !ts || im_outcheck( out ) ) + return( -1 ); + if( in_max < 0 || in_max > 65535 || + out_max < 0 || out_max > 65535 ) { + im_error( "im_tone_build", + _( "bad in_max, out_max parameters" ) ); + return( -1 ); + } + if( Lb < 0 || Lb > 100 || Lw < 0 || Lw > 100 || Lb > Lw ) { + im_error( "im_tone_build", _( "bad Lb, Lw parameters" ) ); + return( -1 ); + } + if( Ps < 0.0 || Ps > 1.0 ) { + im_error( "im_tone_build", _( "Ps not in range [0.0,1.0]" ) ); + return( -1 ); + } + if( Pm < 0.0 || Pm > 1.0 ) { + im_error( "im_tone_build", _( "Pm not in range [0.0,1.0]" ) ); + return( -1 ); + } + if( Ph < 0.0 || Ph > 1.0 ) { + im_error( "im_tone_build", _( "Ph not in range [0.0,1.0]" ) ); + return( -1 ); + } + if( S < -30 || S > 30 ) { + im_error( "im_tone_build", _( "S not in range [-30,+30]" ) ); + return( -1 ); + } + if( M < -30 || M > 30 ) { + im_error( "im_tone_build", _( "M not in range [-30,+30]" ) ); + return( -1 ); + } + if( H < -30 || H > 30 ) { + im_error( "im_tone_build", _( "H not in range [-30,+30]" ) ); + return( -1 ); + } + + /* Note params. + */ + ts->Lb = Lb; + ts->Lw = Lw; + ts->Ps = Ps; + ts->Pm = Pm; + ts->Ph = Ph; + ts->S = S; + ts->M = M; + ts->H = H; + + /* Note derived params. + */ + ts->Ls = Lb + Ps * (Lw - Lb); + ts->Lm = Lb + Pm * (Lw - Lb); + ts->Lh = Lb + Ph * (Lw - Lb); + + /* Generate curve. + */ + for( i = 0; i <= in_max; i++ ) { + int v = (out_max / 100.0) * + tone_curve( ts, 100.0 * i / in_max ); + + if( v < 0 ) + v = 0; + else if( v > out_max ) + v = out_max; + + lut[i] = v; + } + + /* Make the output image. + */ + im_initdesc( out, + in_max + 1, 1, 1, IM_BBITS_SHORT, IM_BANDFMT_USHORT, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( out ) ) + return( -1 ); + if( im_writeline( 0, out, (PEL *) lut ) ) + return( -1 ); + + return( 0 ); +} + +int +im_tone_build( IMAGE *out, + double Lb, double Lw, + double Ps, double Pm, double Ph, + double S, double M, double H ) +{ + IMAGE *t1 = im_open_local( out, "im_tone_build", "p" ); + + if( !t1 || + im_tone_build_range( t1, 1023, 32767, + Lb, Lw, Ps, Pm, Ph, S, M, H ) || + im_clip2s( t1, out ) ) + return( -1 ); + + return( 0 ); +} + +/* Test a lut or histogram for monotonicity. + */ +int +im_ismonotonic( IMAGE *lut, int *out ) +{ + IMAGE *t1 = im_open_local( lut, "im_ismonotonic:1", "p" ); + IMAGE *t2 = im_open_local( lut, "im_ismonotonic:2", "p" ); + IMAGE *t3 = im_open_local( lut, "im_ismonotonic:3", "p" ); + double m; + + if( !t1 || !t2 || !t3 ) + return( -1 ); + + /* Can be either a horizontal or vertical LUT. + */ + if( lut->Xsize != 1 && lut->Ysize != 1 ) { + im_error( "im_ismonotonic", _( "not 1 by n or n by 1 image" ) ); + return( -1 ); + } + + /* Extract two areas, offset by 1 pixel. + */ + if( lut->Xsize == 1 ) { + if( im_extract_area( lut, t1, 0, 0, 1, lut->Ysize - 1 ) || + im_extract_area( lut, t2, 0, 1, 1, lut->Ysize - 1 ) ) + return( -1 ); + } + else { + if( im_extract_area( lut, t1, 0, 0, lut->Xsize - 1, 1 ) || + im_extract_area( lut, t2, 1, 0, lut->Xsize - 1, 1 ) ) + return( -1 ); + } + + /* Now t2 should be >= than t1 everywhere! + */ + if( im_moreeq( t2, t1, t3 ) || im_min( t3, &m ) ) + return( -1 ); + + *out = m; + + return( 0 ); +} + +/* Map the L channel of a LabQ or LabS channel of an image through a LUT. + */ +int +im_tone_map( IMAGE *in, IMAGE *out, IMAGE *lut ) +{ + IMAGE *t1 = im_open_local( out, "im_tone_map:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_tone_map:2", "p" ); + IMAGE *t3 = im_open_local( out, "im_tone_map:3", "p" ); + IMAGE *t4 = im_open_local( out, "im_tone_map:4", "p" ); + IMAGE *t5 = im_open_local( out, "im_tone_map:5", "p" ); + IMAGE *t6 = im_open_local( out, "im_tone_map:6", "p" ); + IMAGE *t7 = im_open_local( out, "im_tone_map:7", "p" ); + IMAGE *t8 = im_open_local( out, "im_tone_map:8", "p" ); + IMAGE *imarray[3]; + + if( !t1 || !t2 || !t3 || !t4 || !t5 || !t6 || !t7 ) + return( -1 ); + + /* Need a 1024-point IM_BANDFMT_SHORT lut. + */ + if( lut->Xsize != 1 && lut->Ysize != 1 ) { + im_error( "im_tone_map", _( "not 1 by n or n by 1 image" ) ); + return( -1 ); + } + if( lut->Xsize*lut->Ysize != 1024 || + lut->BandFmt != IM_BANDFMT_SHORT ) { + im_error( "im_tone_map", + _( "not 1024-point IM_BANDFMT_SHORT lut" ) ); + return( -1 ); + } + + /* If in is IM_CODING_LABQ, unpack. + */ + if( in->Coding == IM_CODING_LABQ ) { + if( im_LabQ2LabS( in, t1 ) ) + return( -1 ); + } + else + t1 = in; + + /* Should now be 3-band short. + */ + if( t1->Coding != IM_CODING_NONE || t1->BandFmt != IM_BANDFMT_SHORT || + t1->Bands != 3 ) { + im_error( "im_tone_map", _( "input not LabS or LabQ" ) ); + return( -1 ); + } + + /* Split into bands. + */ + if( im_extract_band( t1, t2, 0 ) || im_extract_band( t1, t3, 1 ) || + im_extract_band( t1, t4, 2 ) ) + return( -1 ); + + /* Scale L down to 10 bits so we can use it to index LUT. And amke + * sure we have an unsigned type we can use for indexing. + */ + if( im_shiftright( t2, t8, 5 ) || + im_clip2us( t8, t5 ) ) + return( -1 ); + + /* Replace L. + */ + if( im_maplut( t5, t6, lut ) ) + return( -1 ); + + /* Recombine bands. If input was LabQ, repack. + */ + imarray[0] = t6; imarray[1] = t3; imarray[2] = t4; + if( in->Coding == IM_CODING_LABQ ) { + if( im_gbandjoin( imarray, t7, 3 ) || + im_LabS2LabQ( t7, out ) ) + return( -1 ); + } + else { + if( im_gbandjoin( imarray, out, 3 ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Find histogram of in, and use that to set Lb, Lw levels. + */ +int +im_tone_analyse( + IMAGE *in, + IMAGE *lut, + double Ps, double Pm, double Ph, + double S, double M, double H ) +{ + gint64 sum = in->Xsize * in->Ysize; + int *p; + int i, j; + double Lb, Lw; + + IMAGE *t1 = im_open_local( lut, "im_tone_analyse:1", "p" ); + IMAGE *t2 = im_open_local( lut, "im_tone_analyse:2", "p" ); + IMAGE *t3 = im_open_local( lut, "im_tone_analyse:3", "p" ); + IMAGE *t4 = im_open_local( lut, "im_tone_analyse:4", "p" ); + IMAGE *t6 = im_open_local( lut, "im_tone_analyse:6", "p" ); + + if( !t1 || !t2 || !t3 || !t4 || !t6 ) + return( -1 ); + + /* If in is IM_CODING_LABQ, unpack. + */ + if( in->Coding == IM_CODING_LABQ ) { + if( im_LabQ2LabS( in, t1 ) ) + return( -1 ); + } + else + t1 = in; + + /* Should now be 3-band short. + */ + if( t1->Coding != IM_CODING_NONE || t1->BandFmt != IM_BANDFMT_SHORT || + t1->Bands != 3 ) { + im_error( "im_tone_analyse", _( "input not LabS or LabQ" ) ); + return( -1 ); + } + + /* Extract and scale L. + */ + if( im_extract_band( t1, t2, 0 ) || + im_shiftright( t2, t3, 5 ) || + im_clip2us( t3, t4 ) ) + return( -1 ); + + /* Take histogram, and make it a cumulative hist. + */ + if( im_histgr( t4, t6, -1 ) ) + return( -1 ); + + /* Search for 0.1% mark. + */ + if( im_incheck( t6 ) ) + return( -1 ); + p = (int *) t6->data; + for( j = 0, i = 0; i < t6->Xsize; i++ ) { + j += p[i]; + if( j > sum * (0.1 / 100.0) ) + break; + } + Lb = i / 10.24; + + /* Search for 99.9% mark. + */ + p = (int *) t6->data; + for( j = 0, i = t6->Xsize - 1; i > 0; i-- ) { + j += p[i]; + if( j > sum * (0.1 / 100.0) ) + break; + } + Lw = i / 10.24; + + im_diagnostics( "im_tone_analyse: set Lb = %g, Lw = %g", Lb, Lw ); + + return( im_tone_build( lut, Lb, Lw, Ps, Pm, Ph, S, M, H ) ); +} diff --git a/libsrc/inplace/Makefile.am b/libsrc/inplace/Makefile.am new file mode 100644 index 00000000..73fd5301 --- /dev/null +++ b/libsrc/inplace/Makefile.am @@ -0,0 +1,17 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libinplace.la + +libinplace_la_SOURCES = \ + im_circle.c \ + im_flood.c \ + im_insertplace.c \ + im_line.c \ + im_paintrect.c \ + im_plotmask.c \ + inplace_dispatch.c \ + line_draw.c \ + plot_point.c \ + smudge_area.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/inplace/im_circle.c b/libsrc/inplace/im_circle.c new file mode 100644 index 00000000..5dc92536 --- /dev/null +++ b/libsrc/inplace/im_circle.c @@ -0,0 +1,134 @@ +/* @(#) writes a circle in a vasari file + * @(#) The circle is centred in the middle of the file (xsize/2, ysize/2) + * @(#) im must be a valid image + * @(#) int im_circle(pim, cx, cy, radius, intensity) + * @(#) IMAGE *pim; + * @(#) int cx, cy, radius, intensity; + * @(#) + * @(#) Return -1 on error 0 on sucess. + * + * Copyright 1990, N. Dessipris. + * + * Author N. Dessipris + * Written on 30/05/1990 + * Updated on: + * 22/7/93 JC + * - im_incheck() call added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_circle( IMAGE *im, int cx, int cy, int radius, int intensity) +{ + PEL *start; + int size = 0; + int x, y, d, offset; + + if( im_rwcheck( im ) ) + return( -1 ); + +/* Check args */ + if ( (im->data == NULL)||(im->BandFmt != IM_BANDFMT_UCHAR)||(im->Bands != 1) + ||(im->Bbits != IM_BBITS_BYTE) ) + { + im_errormsg("im_circle: unable to write input image"); + return(-1); + } + if ((intensity > 255)||(intensity <= 0)) + { + im_errormsg("im_circle: intensity between 0 and 255"); + return(-1); + } +/* Check if circle fits into image */ + if ( ((radius+cy)> im->Ysize - 1) || ((cy-radius)< 0 ) || + ((radius+cx)> im->Xsize - 1) || ((cx-radius) < 0 ) ) + { + im_errormsg("im_circle: The circle doesnot fit in image"); + return(-1); + } +/* Draw the circle */ + size = im->Xsize; + start = (PEL*)im->data; + offset = cy * im->Xsize + cx; /* point at the center of the circle */ + x = 0; + y = radius; + d = 3 - 2 * radius; + while ( x < y ) + { + *(start + offset + size * y + x) = (PEL)intensity; + *(start + offset + size * x + y) = (PEL)intensity; + *(start + offset + size * y - x) = (PEL)intensity; + *(start + offset + size * x - y) = (PEL)intensity; + *(start + offset - size * y - x) = (PEL)intensity; + *(start + offset - size * x - y) = (PEL)intensity; + *(start + offset - size * y + x) = (PEL)intensity; + *(start + offset - size * x + y) = (PEL)intensity; + if (d < 0 ) + d += ( 4 * x + 6 ); + else + { + d += ( 4 * ( x - y ) + 10 ); + y--; + } + x++; + } + if ( x== y ) + { + *(start + offset + size * y + x) = (PEL)intensity; + *(start + offset + size * x + y) = (PEL)intensity; + *(start + offset + size * y - x) = (PEL)intensity; + *(start + offset + size * x - y) = (PEL)intensity; + *(start + offset - size * y - x) = (PEL)intensity; + *(start + offset - size * x - y) = (PEL)intensity; + *(start + offset - size * y + x) = (PEL)intensity; + *(start + offset - size * x + y) = (PEL)intensity; + } + + im_invalidate( im ); + + return(0); +} diff --git a/libsrc/inplace/im_flood.c b/libsrc/inplace/im_flood.c new file mode 100644 index 00000000..9254b649 --- /dev/null +++ b/libsrc/inplace/im_flood.c @@ -0,0 +1,421 @@ +/* int im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) + * + * Flood fill from point (x,y) with colour ink. Flood up to boundary == ink. + * Any type, any number of bands, IM_CODING_LABQ too. Returns the bounding box + * of the modified pixels in dout, whether it succeeds or not. + * + * Currently a rather inefficient pixel-based algorithm, should put something + * better in, really. Speed isn't likely to be a problem, except for very + * large images. + * + * JC 30/8/97 + * - VIPSified, cleaned up, from "John Robinson's prog to fill + * enclosed areas" + * - something Kirk gave me, so thanks John + * JC 1/10/97 + * - swapped inner memcmp/cpy for a loop ... faster for small pixels + * 13/7/02 JC + * - im_flood_blob() added + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Size of a point buffer. We allocate a list of these to hold points we need + * to visit. + */ +#define PBUFSIZE (1000) + +/* An xy position. + */ +typedef struct { + int x, y; +} Point; + +/* A buffer of points, and how many of them have been used. When full, alloc a + * new buffer, and link it on. + */ +typedef struct _Buffer { + struct _Buffer *next; + int n; + Point points[PBUFSIZE]; +} Buffer; + +/* Our state. + */ +typedef struct { + /* Parameters. + */ + IMAGE *im; + int x, y; + PEL *ink; /* Copy of ink param */ + Rect *dout; /* Write dirty here at end */ + + /* Derived stuff. + */ + PEL *edge; /* Boundary colour */ + int equal; /* Fill to == edge, or != edge */ + int ps; /* sizeof( one pel ) */ + int ls; /* sizeof( one line ) */ + int left, right; /* Area will fill within */ + int top, bottom; + Rect dirty; /* Bounding box of pixels we have changed */ + + /* Two buffers of points which we know need checking. + */ + Buffer *buf1; + Buffer *buf2; +} State; + +/* Alloc a new buffer. + */ +static Buffer * +build_buffer( void ) +{ + Buffer *buf = IM_NEW( NULL, Buffer ); + + if( !buf ) + return( NULL ); + buf->next = NULL; + buf->n = 0; + + return( buf ); +} + +/* Free a chain of buffers. + */ +static void +free_buffer( Buffer *buf ) +{ + IM_FREE( buf->next ); + im_free( buf ); +} + +/* Free a state. + */ +static void +free_state( State *st ) +{ + /* Write dirty back to caller. + */ + if( st->dout ) + *st->dout = st->dirty; + + /* Free our stuff. + */ + IM_FREE( st->ink ); + IM_FREEF( free_buffer, st->buf1 ); + IM_FREEF( free_buffer, st->buf2 ); + im_free( st ); +} + +/* Build a state. + */ +static State * +build_state( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) +{ + State *st = IM_NEW( NULL, State ); + + if( !st ) + return( NULL ); + st->im = im; + st->x = x; + st->y = y; + st->ink = NULL; + st->dout = dout; + st->ps = IM_IMAGE_SIZEOF_PEL( im ); + st->ls = IM_IMAGE_SIZEOF_LINE( im ); + st->buf1 = NULL; + st->buf2 = NULL; + st->left = 0; + st->top = 0; + st->right = im->Xsize; + st->bottom = im->Ysize; + st->dirty.left = x; + st->dirty.top = y; + st->dirty.width = 0; + st->dirty.height = 0; + + if( !(st->ink = (PEL *) im_malloc( NULL, st->ps )) || + !(st->edge = (PEL *) im_malloc( NULL, st->ps )) || + !(st->buf1 = build_buffer()) || + !(st->buf2 = build_buffer()) ) { + free_state( st ); + return( NULL ); + } + memcpy( st->ink, ink, st->ps ); + + return( st ); +} + +/* Add xy to buffer, move buffer on on overflow. + */ +#define ADD( BUF, X, Y ) { \ + BUF->points[BUF->n].x = X; \ + BUF->points[BUF->n].y = Y; \ + BUF->n++; \ + if( BUF->n == PBUFSIZE ) { \ + if( !BUF->next ) { \ + if( !(BUF->next = build_buffer()) ) \ + return( -1 ); \ + } \ + BUF = BUF->next; \ + BUF->n = 0; \ + } \ +} + +/* If point != edge, add it to out. + */ +#define ADDIFNOTEDGE( P, X, Y ) { \ + PEL *p1 = (P); \ + \ + for( j = 0; j < st->ps; j++ ) \ + if( p1[j] != st->edge[j] ) { \ + ADD( out, X, Y ); \ + break; \ + } \ +} + +/* If point == edge, add it to out. + */ +#define ADDIFEDGE( P, X, Y ) { \ + PEL *p1 = (P); \ + \ + for( j = 0; j < st->ps; j++ ) \ + if( p1[j] != st->edge[j] ) \ + break; \ + if( j == st->ps ) \ + ADD( out, X, Y ); \ +} + +/* Read points to fill from in, write new points to out. + */ +static int +dofill( State *st, Buffer *in, Buffer *out ) +{ + int i, j; + + /* Clear output buffer. + */ + out->n = 0; + + /* Loop over chain of input buffers. + */ + for(;;) { + /* Loop for this buffer. + */ + for( i = 0; i < in->n; i++ ) { + /* Find this pixel. + */ + int x = in->points[i].x; + int y = in->points[i].y; + PEL *p = (PEL *) st->im->data + x*st->ps + y*st->ls; + + /* Is it still not fore? May have been set by us + * earlier. + */ + for( j = 0; j < st->ps; j++ ) + if( p[j] != st->ink[j] ) + break; + if( j == st->ps ) + continue; + + /* Set this pixel. + */ + for( j = 0; j < st->ps; j++ ) + p[j] = st->ink[j]; + + /* Changes bb of dirty area? + */ + if( x < st->dirty.left ) { + st->dirty.left -= x; + st->dirty.width += x; + } + else if( x > st->dirty.left + st->dirty.width ) + st->dirty.width += x; + + if( y < st->dirty.top ) { + st->dirty.top -= y; + st->dirty.height += y; + } + else if( y > st->dirty.top + st->dirty.height ) + st->dirty.height += y; + + /* Propogate to neighbours. + */ + if( st->equal ) { + if( x < st->right - 1 ) + ADDIFEDGE( p + st->ps, x + 1, y ); + if( x > st->left ) + ADDIFEDGE( p - st->ps, x - 1, y ); + if( y < st->bottom - 1 ) + ADDIFEDGE( p + st->ls, x, y + 1 ); + if( y > st->top ) + ADDIFEDGE( p - st->ls, x, y - 1 ); + } + else { + if( x < st->right - 1 ) + ADDIFNOTEDGE( p + st->ps, x + 1, y ); + if( x > st->left ) + ADDIFNOTEDGE( p - st->ps, x - 1, y ); + if( y < st->bottom - 1 ) + ADDIFNOTEDGE( p + st->ls, x, y + 1 ); + if( y > st->top ) + ADDIFNOTEDGE( p - st->ls, x, y - 1 ); + } + } + + if( in->n == PBUFSIZE ) + /* Buffer full ... must be another one. + */ + in = in->next; + else + break; + } + + return( 0 ); +} + +int +im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) +{ + State *st; + Buffer *in, *out, *t; + PEL *p; + + if( im_rwcheck( im ) ) + return( -1 ); + if( im->Coding != IM_CODING_NONE && im->Coding != IM_CODING_LABQ ) { + im_error( "im_flood", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( !(st = build_state( im, x, y, ink, dout )) ) + return( -1 ); + + /* Test start pixel ... nothing to do? + */ + p = (PEL *) im->data + x*st->ps + y*st->ls; + if( memcmp( p, ink, st->ps ) == 0 ) { + free_state( st ); + return( 0 ); + } + + /* Flood to != ink. + */ + memcpy( st->edge, ink, st->ps ); + st->equal = 0; + + /* Add start pixel to the work buffer, and loop. + */ + ADD( st->buf1, x, y ) + for( in = st->buf1, out = st->buf2; + in->n > 0; t = in, in = out, out = t ) + if( dofill( st, in, out ) ) { + free_state( st ); + return( -1 ); + } + + free_state( st ); + + im_invalidate( im ); + + return( 0 ); +} + +int +im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout ) +{ + State *st; + Buffer *in, *out, *t; + PEL *p; + + if( im_rwcheck( im ) ) + return( -1 ); + if( im->Coding != IM_CODING_NONE && im->Coding != IM_CODING_LABQ ) { + im_error( "im_flood", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( !(st = build_state( im, x, y, ink, dout )) ) + return( -1 ); + + /* Edge is set by colour of start pixel. + */ + p = (PEL *) im->data + x*st->ps + y*st->ls; + memcpy( st->edge, p, st->ps ); + st->equal = 1; + + /* Add start pixel to the work buffer, and loop. + */ + ADD( st->buf1, x, y ) + for( in = st->buf1, out = st->buf2; + in->n > 0; t = in, in = out, out = t ) + if( dofill( st, in, out ) ) { + free_state( st ); + return( -1 ); + } + + free_state( st ); + + im_invalidate( im ); + + return( 0 ); +} + +/* A Flood blob we can call from nip. Grr! Should be a way to wrap these + * automatically. Maybe nip could do it if it seems a RW image argument? + */ +int +im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink ) +{ + IMAGE *t; + + if( !(t = im_open_local( out, "im_flood_blob_copy", "t" )) || + im_copy( in, t ) || + im_flood_blob( t, x, y, ink, NULL ) || + im_copy( t, out ) ) + return( -1 ); + + return( 0 ); +} + + diff --git a/libsrc/inplace/im_insertplace.c b/libsrc/inplace/im_insertplace.c new file mode 100644 index 00000000..46cc1d6a --- /dev/null +++ b/libsrc/inplace/im_insertplace.c @@ -0,0 +1,123 @@ +/* @(#) Insert an image into another. Like im_insert, but an `in-place' + * @(#) operation. small must fit entirely inside big - no clipping is + * @(#) performed. + * @(#) + * @(#) int + * @(#) im_insertplace( big, small, x, y ) + * @(#) IMAGE *big, *small; + * @(#) int x, y; + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 1/9/04 JC + * - checks bands/types/etc match (thanks Matt) + * - smarter pixel size calculations + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Like im_insert, but perform an in-place insertion. + */ +int +im_insertplace( IMAGE *big, IMAGE *small, int x, int y ) +{ + Rect br, sr; + PEL *p, *q; + int z; + + /* Check IO. + */ + if( im_rwcheck( big ) || im_incheck( small ) ) + return( -1 ); + + /* Check compatibility. + */ + if( big->BandFmt != small->BandFmt || big->Bands != small->Bands || + big->Coding != small->Coding ) { + im_error( "im_insertplace", _( "inputs differ in format" ) ); + return( -1 ); + } + if( big->Coding != IM_CODING_NONE && big->Coding != IM_CODING_LABQ ) { + im_error( "im_insertplace", _( "input should be uncoded " + "or IM_CODING_LABQ" ) ); + return( -1 ); + } + + /* Make rects for big and small. + */ + br.left = 0; + br.top = 0; + br.width = big->Xsize; + br.height = big->Ysize; + sr.left = x; + sr.top = y; + sr.width = small->Xsize; + sr.height = small->Ysize; + + /* Small fits inside big? + */ + if( !im_rect_includesrect( &br, &sr ) ) { + im_error( "im_insertplace", _( "small not inside big" ) ); + return( -1 ); + } + + /* Loop, memcpying small to big. + */ + p = (PEL *) IM_IMAGE_ADDR( small, 0, 0 ); + q = (PEL *) IM_IMAGE_ADDR( big, x, y ); + for( z = 0; z < small->Ysize; z++ ) { + memcpy( (char *) q, (char *) p, IM_IMAGE_SIZEOF_LINE( small ) ); + p += IM_IMAGE_SIZEOF_LINE( small ); + q += IM_IMAGE_SIZEOF_LINE( big ); + } + + im_invalidate( big ); + + return( 0 ); +} diff --git a/libsrc/inplace/im_line.c b/libsrc/inplace/im_line.c new file mode 100644 index 00000000..925846ca --- /dev/null +++ b/libsrc/inplace/im_line.c @@ -0,0 +1,153 @@ +/* @#) line drawer. adapted to draw for graphics system + * @(#) Modified to be compatible with the vasari library + * @(#) In order to use this function, the input file should have been set by + * @(#) im_mmapinrw() + * + * Copyright: N. Dessipris + * Written: 02/01/1990 + * Modified : + * 22/7/93 JC + * - im_incheck() added + * - externs removed + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int im_line(image, x1, y1, x2, y2, pelval) +IMAGE *image; +int x1, x2, y1, y2, pelval; +{ + +double x, y, dx, dy, m; +long offset; +double signx, signy; + + if( im_rwcheck( image ) ) + return( -1 ); +/* check coordinates */ +if ( (x1 > image->Xsize)||(x1<0)||(y1 > image->Ysize)||(y1<0) + ||(x2 > image->Xsize)||(x2<0)||(y2 > image->Ysize)||(y2<0) ) + { im_errormsg("im_line: invalid line cooordinates"); return(-1); } +if ((pelval > 255)||(pelval < 0)) + {im_errormsg("im_line: line intensity between 0 and 255"); return(-1); } + +if (image->Bands != 1) + { im_errormsg("im_line: image should have one band only");return(-1); } + +dx = (double)(x2 - x1); +dy = (double)(y2 - y1); + +if (dx < 0.0) + signx = -1.0; +else + signx = 1.0; + +if (dy < 0.0) + signy = -1.0; +else + signy = 1.0; + +if (dx == 0.0) + { + x = x1; y = y1; + while (y != y2) + { + offset = (int)(x+.5) + ((int)(y +.5)) * image->Xsize; + *(image->data + offset) = (PEL)pelval; + y += signy; + } + /* Draw point (x2, y2) */ + offset = x2 + y2 * image->Xsize; + *(image->data + offset) = (PEL)pelval; + return(0); + } + +if (dy == 0.0) + { + y = y1; x = x1; + while (x != x2) + { + offset = (int)(x+.5) + ((int)(y +.5)) * image->Xsize; + *(image->data + offset) = (PEL)pelval; + x += signx; + } + /* Draw point (x2, y2) */ + offset = x2 + y2 * image->Xsize; + *(image->data + offset) = (PEL)pelval; + return(0); + } + +if (fabs(dy) < fabs(dx)) + { + m = fabs(dy/dx)*signy; + y = y1; + x = x1; + while (x != x2) + { + offset = (int)(x+.5) + ((int)(y +.5)) * image->Xsize; + *(image->data + offset) = (PEL)pelval; + x += signx; + y += m; + } + } +else + { + m = fabs(dx/dy)*signx; + x = x1; y = y1; + while (y != y2) + { + offset = (int)(x+.5) + ((int)(y +.5)) * image->Xsize; + *(image->data + offset) = (PEL)pelval; + x += m; + y += signy; + } + } +/* Draw point (x2, y2) */ +offset = x2 + y2 * image->Xsize; +*(image->data + offset) = (PEL)pelval; + im_invalidate( image ); +return(0); +} diff --git a/libsrc/inplace/im_paintrect.c b/libsrc/inplace/im_paintrect.c new file mode 100644 index 00000000..cfad16d3 --- /dev/null +++ b/libsrc/inplace/im_paintrect.c @@ -0,0 +1,105 @@ +/* @(#) Fill Rect r of image im with pels of colour ink. r can be any size and + * @(#) any position, we clip against the image size. + * @(#) + * @(#) int + * @(#) im_paintrect( IMAGE *im, Rect *r, PEL *ink ) + * @(#) + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Paint a rect of colour into an image. + */ +int +im_paintrect( IMAGE *im, Rect *r, PEL *ink ) +{ + int es = im->Bbits >> 3; + int ps = es * im->Bands; + int ls = ps * im->Xsize; + Rect image, clipped; + int x, y, b; + PEL *to; + PEL *q; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Find area we plot. + */ + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + /* Any points left to plot? + */ + if( im_rect_isempty( &clipped ) ) + return( 0 ); + + /* Loop through image plotting where required. + */ + to = (PEL *) im->data + clipped.left * ps + clipped.top * ls; + for( y = 0; y < clipped.height; y++ ) { + q = to; + + for( x = 0; x < clipped.width; x++ ) + for( b = 0; b < ps; b++ ) + *q++ = ink[b]; + + to += ls; + } + + im_invalidate( im ); + + return( 0 ); +} + diff --git a/libsrc/inplace/im_plotmask.c b/libsrc/inplace/im_plotmask.c new file mode 100644 index 00000000..c171086e --- /dev/null +++ b/libsrc/inplace/im_plotmask.c @@ -0,0 +1,239 @@ +/* @(#) Plot many points in a single call. Pass ink, array containing + * @(#) 0/255 showing where to plot and Rect showing size of array and + * @(#) offset to get to centre of array. ix and iy are where to plot. Rect + * @(#) can be any size, any position - we clip against the edges of the + * @(#) image. + * @(#) + * @(#) int + * @(#) im_plotmask( IMAGE *im, int ix, int iy, PEL *ink, PEL *mask, Rect *r ) + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 24/10/03 JC + * - now blends with 0-255 mask + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Paint ink into an 8 or 16 bit integer image. + */ +#define IBLEND( TYPE, TO, INK, B, W ) { \ + TYPE *tto = (TYPE *) (TO); \ + TYPE *tink = (TYPE *) (INK); \ + \ + int x, i, j; \ + \ + for( j = 0, x = 0; x < (W); x++ ) \ + for( i = 0; i < (B); i++, j++ ) \ + tto[j] = (tink[i] * mask_line[x] + \ + tto[j] * (255 - mask_line[x])) / 255; \ +} + +/* Do the blend with doubles. + */ +#define DBLEND( TYPE, TO, INK, B, W ) { \ + TYPE *tto = (TYPE *) (TO); \ + TYPE *tink = (TYPE *) (INK); \ + \ + int x, i, j; \ + \ + for( j = 0, x = 0; x < (W); x++ ) \ + for( i = 0; i < (B); i++, j++ ) \ + tto[j] = ((double) tink[i] * mask_line[x] + \ + (double) tto[j] * (255 - mask_line[x])) / 255;\ +} + +/* Blend of complex. + */ +#define CBLEND( TYPE, TO, INK, B, W ) { \ + TYPE *tto = (TYPE *) (TO); \ + TYPE *tink = (TYPE *) (INK); \ + \ + int x, i, j; \ + \ + for( j = 0, x = 0; x < (W); x++ ) \ + for( i = 0; i < (B) * 2; i += 2, j += 2 ) { \ + tto[j] = ((double) tink[i] * mask_line[x] + \ + (double) tto[j] * (255 - mask_line[x])) / 255;\ + tto[j + 1] = ((double) tink[i + 1] * mask_line[x] + \ + (double) tto[j + 1] * (255 - mask_line[x])) / \ + 255;\ + } \ +} + +/* Plot lots of points! Pass ink, array of 0/255 showing where to plot, rect + * showing size and offset for array. Used for fat lines and text. + */ +int +im_plotmask( IMAGE *im, int ix, int iy, PEL *ink, PEL *mask, Rect *r ) +{ + Rect area, image, clipped; + int y; + int mx, my; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Find area we plot. + */ + area = *r; + area.left += ix; + area.top += iy; + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_intersectrect( &area, &image, &clipped ); + + /* Any points left to plot? + */ + if( im_rect_isempty( &clipped ) ) + return( 0 ); + + /* Find area of mask we use. + */ + mx = IM_MAX( 0, clipped.left - area.left ); + my = IM_MAX( 0, clipped.top - area.top ); + + /* Loop through image plotting where required. + */ + if( im->Coding == IM_CODING_LABQ ) { + float *lab_buffer; + float ink_buffer[3]; + + if( !(lab_buffer = + IM_ARRAY( NULL, clipped.width * 3, float )) ) + return( -1 ); + + imb_LabQ2Lab( ink, ink_buffer, 1 ); + + for( y = 0; y < clipped.height; y++ ) { + PEL *to = (PEL *) IM_IMAGE_ADDR( im, + clipped.left, y + clipped.top ); + PEL *mask_line = mask + + mx + (y + my) * area.width; + + imb_LabQ2Lab( to, lab_buffer, clipped.width ); + DBLEND( float, + lab_buffer, ink_buffer, 3, clipped.width ); + imb_Lab2LabQ( lab_buffer, to, clipped.width ); + } + + im_free( lab_buffer ); + } + else { + for( y = 0; y < clipped.height; y++ ) { + PEL *to = (PEL *) IM_IMAGE_ADDR( im, + clipped.left, y + clipped.top ); + PEL *mask_line = mask + + mx + (y + my) * area.width; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + IBLEND( unsigned char, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_CHAR: + IBLEND( signed char, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_USHORT: + IBLEND( unsigned short, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_SHORT: + IBLEND( signed short, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_UINT: + DBLEND( unsigned int, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_INT: + DBLEND( signed int, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_FLOAT: + DBLEND( float, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_DOUBLE: + DBLEND( double, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_COMPLEX: + CBLEND( float, + to, ink, im->Bands, clipped.width ); + break; + + case IM_BANDFMT_DPCOMPLEX: + CBLEND( double, + to, ink, im->Bands, clipped.width ); + break; + + default: + im_error( "im_plotmask", + _( "internal error" ) ); + return( -1 ); + } + } + } + + im_invalidate( im ); + + return( 0 ); +} + diff --git a/libsrc/inplace/inplace_dispatch.c b/libsrc/inplace/inplace_dispatch.c new file mode 100644 index 00000000..4e8adb6c --- /dev/null +++ b/libsrc/inplace/inplace_dispatch.c @@ -0,0 +1,287 @@ +/* Function dispatch tables for inplace. + * + * J. Cupitt, 8/2/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Args for im_circle. + */ +static im_arg_desc circle_args[] = { + IM_RW_IMAGE( "image" ), + IM_INPUT_INT( "cx" ), + IM_INPUT_INT( "cy" ), + IM_INPUT_INT( "radius" ), + IM_INPUT_INT( "intensity" ) +}; + +/* Call im_circle via arg vector. + */ +static int +circle_vec( im_object *argv ) +{ + int cx = *((int *) argv[1]); + int cy = *((int *) argv[2]); + int radius = *((int *) argv[3]); + int intensity = *((int *) argv[4]); + + return( im_circle( argv[0], cx, cy, radius, intensity ) ); +} + +/* Description of im_circle. + */ +static im_function circle_desc = { + "im_circle", /* Name */ + "plot circle on image", + 0, /* Flags */ + circle_vec, /* Dispatch function */ + IM_NUMBER( circle_args ), /* Size of arg list */ + circle_args /* Arg list */ +}; + +/* Args for im_insertplace. + */ +static im_arg_desc insertplace_args[] = { + IM_RW_IMAGE( "main" ), + IM_INPUT_IMAGE( "sub" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ) +}; + +/* Call im_insertplace via arg vector. + */ +static int +insertplace_vec( im_object *argv ) +{ + int x = *((int *) argv[2]); + int y = *((int *) argv[3]); + + return( im_insertplace( argv[0], argv[1], x, y ) ); +} + +/* Description of im_insertplace. + */ +static im_function insertplace_desc = { + "im_insertplace", /* Name */ + "draw image sub inside image main at position (x,y)", + 0, /* Flags */ + insertplace_vec, /* Dispatch function */ + IM_NUMBER( insertplace_args ), /* Size of arg list */ + insertplace_args /* Arg list */ +}; + +/* Args for im_line. + */ +static im_arg_desc line_args[] = { + IM_RW_IMAGE( "im" ), + IM_INPUT_INT( "x1" ), + IM_INPUT_INT( "y1" ), + IM_INPUT_INT( "x2" ), + IM_INPUT_INT( "y2" ), + IM_INPUT_INT( "pelval" ) +}; + +/* Call im_line via arg vector. + */ +static int +line_vec( im_object *argv ) +{ + int x1 = *((int *) argv[1]); + int y1 = *((int *) argv[2]); + int x2 = *((int *) argv[3]); + int y2 = *((int *) argv[4]); + int pel = *((int *) argv[5]); + + return( im_line( argv[0], x1, y1, x2, y2, pel ) ); +} + +/* Description of im_line. + */ +static im_function line_desc = { + "im_line", /* Name */ + "draw line between points (x1,y1) and (x2,y2)", + 0, /* Flags */ + line_vec, /* Dispatch function */ + IM_NUMBER( line_args ), /* Size of arg list */ + line_args /* Arg list */ +}; + +/* Args for im_lineset. + */ +static im_arg_desc lineset_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMAGE( "mask" ), + IM_INPUT_IMAGE( "ink" ), + IM_INPUT_INTVEC( "x1" ), + IM_INPUT_INTVEC( "y1" ), + IM_INPUT_INTVEC( "x2" ), + IM_INPUT_INTVEC( "y2" ) +}; + +/* Call im_lineset via arg vector. + */ +static int +lineset_vec( im_object *argv ) +{ + im_intvec_object *x1v = (im_intvec_object *) argv[4]; + im_intvec_object *y1v = (im_intvec_object *) argv[5]; + im_intvec_object *x2v = (im_intvec_object *) argv[6]; + im_intvec_object *y2v = (im_intvec_object *) argv[7]; + + if( x1v->n != y1v->n || x1v->n != x2v->n || x1v->n != y2v->n ) { + im_error( "im_lineset", _( "vectors not same length" ) ); + return( -1 ); + } + + return( im_lineset( argv[0], argv[1], argv[2], argv[3], + x1v->n, x1v->vec, y1v->vec, x2v->vec, y2v->vec ) ); +} + +/* Description of im_lineset. + */ +static im_function lineset_desc = { + "im_lineset", /* Name */ + "draw line between points (x1,y1) and (x2,y2)", + 0, /* Flags */ + lineset_vec, /* Dispatch function */ + IM_NUMBER( lineset_args ), /* Size of arg list */ + lineset_args /* Arg list */ +}; + +/* Calculate a pixel for an image from a vec of double. Valid while im is + * valid. + */ +static PEL * +vector_to_ink( IMAGE *im, double *vec ) +{ + const int n = im->Bands; + + IMAGE *t[3]; + double *zeros; + int i; + + if( im_open_local_array( im, t, 3, "vector_to_ink", "t" ) || + !(zeros = IM_ARRAY( im, n, double )) ) + return( NULL ); + for( i = 0; i < n; i++ ) + zeros[i] = 0.0; + + if( im_black( t[0], 1, 1, n ) || + im_lintra_vec( n, zeros, t[0], vec, t[1] ) || + im_clip2fmt( t[1], t[2], im->BandFmt ) ) + return( NULL ); + + return( (PEL *) t[2]->data ); +} + +/* Args for im_flood_blob_copy(). + */ +static im_arg_desc flood_blob_copy_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "start_x" ), + IM_INPUT_INT( "start_y" ), + IM_INPUT_DOUBLEVEC( "ink" ) +}; + +/* Call im_flood_blob_copy() via arg vector. + */ +static int +flood_blob_copy_vec( im_object *argv ) +{ + IMAGE *in = argv[0]; + IMAGE *out = argv[1]; + int start_x = *((int *) argv[2]); + int start_y = *((int *) argv[3]); + im_doublevec_object *dv = (im_doublevec_object *) argv[4]; + + PEL *ink; + + if( dv->n != in->Bands ) { + im_error( "im_flood_blob_copy", _( "bad vector length" ) ); + return( -1 ); + } + if( !(ink = vector_to_ink( in, dv->vec )) ) + return( -1 ); + + return( im_flood_blob_copy( in, out, start_x, start_y, ink ) ); +} + +/* Description of im_flood_blob_copy(). + */ +static im_function flood_blob_copy_desc = { + "im_flood_blob_copy", /* Name */ + "flood with ink from start_x, start_y while pixel == start pixel", + 0, /* Flags */ + flood_blob_copy_vec, /* Dispatch function */ + IM_NUMBER( flood_blob_copy_args ),/* Size of arg list */ + flood_blob_copy_args /* Arg list */ +}; + +/* To do: + * these all need some kind of pel type + * + im_flood.c + im_paintrect.c + im_plotmask.c + line_draw.c + plot_point.c + smudge_area.c + * + */ + +/* Package up all these functions. + */ +static im_function *inplace_list[] = { + &circle_desc, + &flood_blob_copy_desc, + &insertplace_desc, + &line_desc, + &lineset_desc +}; + +/* Package of functions. + */ +im_package im__inplace = { + "inplace", + IM_NUMBER( inplace_list ), + inplace_list +}; diff --git a/libsrc/inplace/line_draw.c b/libsrc/inplace/line_draw.c new file mode 100644 index 00000000..c83ea94c --- /dev/null +++ b/libsrc/inplace/line_draw.c @@ -0,0 +1,449 @@ +/* @(#) Line drawer. Faster than the old im_line. Any number of bands, + * @(#) any type including complex. Instead of passing a PEL value, pass a + * @(#) pointer to the pel value you wish to plot. The correct number of + * @(#) bytes must be there! Both start and end points should be in the + * @(#) image. + * @(#) + * @(#) int + * @(#) im_fastline( im, x1, y1, x2, y2, pel ) + * @(#) IMAGE *im; + * @(#) int x1, x2, y1, y2; + * @(#) PEL *pel; + * @(#) + * @(#) As above, but rather than plotting a point, call a passed function + * @(#) for every point on the line. Up to three extra args passed down too. + * @(#) If the passed function returns non-zero, im_fastlineuser stops and + * @(#) returns non-zero. Start and end points may be outside the image - + * @(#) clipping is the responsibility of the user function. + * @(#) + * @(#) int + * @(#) im_fastlineuser( im, x1, y1, x2, y2, plot_fn, + * @(#) client1, client2, client3 ) + * @(#) IMAGE *im; + * @(#) int x1, x2, y1, y2; + * @(#) int (*plot_fn)(); + * @(#) void *client1, *client2, *client3; + * @(#) + * @(#) int + * @(#) plot_fn( im, x, y, client1, client2, client3 ) + * @(#) IMAGE *im; + * @(#) int x, y; + * @(#) void *client1, *client2, *client3; + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * Modified : 22/10/92 - clipping constraints changed + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define SWAP(A,B) {int t; t = (A); (A) = (B); (B) = t;} + +/* Draw a line on a image. + */ +int +im_fastline( IMAGE *im, int x1, int y1, int x2, int y2, PEL *pel ) +{ + int es = im->Bbits >> 3; + int ps = es * im->Bands; + int ls = ps * im->Xsize; + PEL *p; + + int x, y, dx, dy; + int err; + int b; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Check coordinates in range. + */ + if( x1 > im->Xsize || x1 < 0 || + y1 > im->Ysize || y1 < 0 || + x2 > im->Xsize || x2 < 0 || + y2 > im->Ysize || y2 < 0 ) { + im_errormsg( "im_fastline: invalid line cooordinates" ); + return( -1 ); + } + + /* Find offsets. + */ + dx = x2 - x1; + dy = y2 - y1; + + /* Swap endpoints to reduce number of cases. + */ + if( abs( dx ) >= abs( dy ) && dx < 0 ) { + /* Swap to get all x greater or equal cases going to the + * right. Do diagonals here .. just have up and right and down + * and right now. + */ + SWAP( x1, x2 ); + SWAP( y1, y2 ); + } + else if( abs( dx ) < abs( dy ) && dy < 0 ) { + /* Swap to get all y greater cases going down the screen. + */ + SWAP( x1, x2 ); + SWAP( y1, y2 ); + } + + /* Recalculate dx, dy. + */ + dx = x2 - x1; + dy = y2 - y1; + + /* Start point and offset. + */ + x = x1; + y = y1; + p = (PEL *) im->data + x * ps + y * ls; + + /* Plot point macro. + */ +#define PLOT \ + for( b = 0; b < ps; b++ ) \ + p[b] = pel[b]; + + /* Special case: zero width and height is single point. + */ + if( dx == 0 && dy == 0 ) { + PLOT; + } + /* Special case vertical and horizontal lines for speed. + */ + else if( dx == 0 ) { + /* Vertical line going down. + */ + for( ; y <= y2; y++ ) { + PLOT; + p += ls; + } + } + else if( dy == 0 ) { + /* Horizontal line to the right. + */ + for( ; x <= x2; x++ ) { + PLOT; + p += ps; + } + } + /* Special case diagonal lines. + */ + else if( abs( dy ) == abs( dx ) && dy > 0 ) { + /* Diagonal line going down and right. + */ + for( ; x <= x2; x++ ) { + PLOT; + p += ps + ls; + } + } + else if( abs( dy ) == abs( dx ) && dy < 0 ) { + /* Diagonal line going up and right. + */ + for( ; x <= x2; x++ ) { + PLOT; + p += ps - ls; + } + } + else if( abs( dy ) < abs( dx ) && dy > 0 ) { + /* Between -45 and 0 degrees. + */ + for( err = 0; x <= x2; x++ ) { + PLOT; + p += ps; + err += dy; + if( err >= dx ) { + err -= dx; + p += ls; + } + } + } + else if( abs( dy ) < abs( dx ) && dy < 0 ) { + /* Between 0 and 45 degrees. + */ + for( err = 0; x <= x2; x++ ) { + PLOT; + p += ps; + err -= dy; + if( err >= dx ) { + err -= dx; + p -= ls; + } + } + } + else if( abs( dy ) > abs( dx ) && dx > 0 ) { + /* Between -45 and -90 degrees. + */ + for( err = 0; y <= y2; y++ ) { + PLOT; + p += ls; + err += dx; + if( err >= dy ) { + err -= dy; + p += ps; + } + } + } + else if( abs( dy ) > abs( dx ) && dx < 0 ) { + /* Between -90 and -135 degrees. + */ + for( err = 0; y <= y2; y++ ) { + PLOT; + p += ls; + err -= dx; + if( err >= dy ) { + err -= dy; + p -= ps; + } + } + } + else + error_exit( "internal error #9872659823475982375" ); + + im_invalidate( im ); + + return( 0 ); +} + +/* Draw a line on a image with a user plot function. We do no clipping: the + * user function should check ranges for each pixel when it is called. + */ +int +im_fastlineuser( IMAGE *im, + int x1, int y1, int x2, int y2, + int (*fn)(), void *client1, void *client2, void *client3 ) +{ + int x, y, dx, dy; + int err; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Find offsets. + */ + dx = x2 - x1; + dy = y2 - y1; + + /* Swap endpoints to reduce number of cases. + */ + if( abs( dx ) >= abs( dy ) && dx < 0 ) { + /* Swap to get all x greater or equal cases going to the + * right. Do diagonals here .. just have up and right and down + * and right now. + */ + SWAP( x1, x2 ); + SWAP( y1, y2 ); + } + else if( abs( dx ) < abs( dy ) && dy < 0 ) { + /* Swap to get all y greater cases going down the screen. + */ + SWAP( x1, x2 ); + SWAP( y1, y2 ); + } + + /* Recalculate dx, dy. + */ + dx = x2 - x1; + dy = y2 - y1; + + /* Start point and offset. + */ + x = x1; + y = y1; + + /* Special case: zero width and height is single point. + */ + if( dx == 0 && dy == 0 ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + /* Special case vertical and horizontal lines for speed. + */ + else if( dx == 0 ) { + /* Vertical line going down. + */ + for( ; y <= y2; y++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + } + else if( dy == 0 ) { + /* Horizontal line to the right. + */ + for( ; x <= x2; x++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + } + /* Special case diagonal lines. + */ + else if( abs( dy ) == abs( dx ) && dy > 0 ) { + /* Diagonal line going down and right. + */ + for( ; x <= x2; x++, y++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + } + else if( abs( dy ) == abs( dx ) && dy < 0 ) { + /* Diagonal line going up and right. + */ + for( ; x <= x2; x++, y-- ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + } + } + else if( abs( dy ) < abs( dx ) && dy > 0 ) { + /* Between -45 and 0 degrees. + */ + for( err = 0; x <= x2; x++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + err += dy; + if( err >= dx ) { + err -= dx; + y++; + } + } + } + else if( abs( dy ) < abs( dx ) && dy < 0 ) { + /* Between 0 and 45 degrees. + */ + for( err = 0; x <= x2; x++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + err -= dy; + if( err >= dx ) { + err -= dx; + y--; + } + } + } + else if( abs( dy ) > abs( dx ) && dx > 0 ) { + /* Between -45 and -90 degrees. + */ + for( err = 0; y <= y2; y++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + err += dx; + if( err >= dy ) { + err -= dy; + x++; + } + } + } + else if( abs( dy ) > abs( dx ) && dx < 0 ) { + /* Between -90 and -135 degrees. + */ + for( err = 0; y <= y2; y++ ) { + if( fn( im, x, y, client1, client2, client3 ) ) + return( 1 ); + err -= dx; + if( err >= dy ) { + err -= dy; + x--; + } + } + } + else + error_exit( "internal error #9872659823475982375" ); + + im_invalidate( im ); + + return( 0 ); +} + +/* Draw a set of lines with an ink and a mask. A non-inplace operation, handy + * for nip2. + */ +int +im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink, + int n, int *x1v, int *y1v, int *x2v, int *y2v ) +{ + Rect mask_rect; + int i; + + if( mask->Bands != 1 || mask->BandFmt != IM_BANDFMT_UCHAR || + mask->Coding != IM_CODING_NONE ) { + im_error( "im_lineset", + _( "mask image not 1 band 8 bit uncoded" ) ); + return( -1 ); + } + if( ink->Bands != in->Bands || ink->BandFmt != in->BandFmt || + ink->Coding != in->Coding ) { + im_error( "im_lineset", + _( "ink image does not match in image" ) ); + return( -1 ); + } + if( ink->Xsize != 1 || ink->Ysize != 1 ) { + im_error( "im_lineset", _( "ink image not 1x1 pixels" ) ); + return( -1 ); + } + + /* Copy the image thenm fastline to it ... this will render to a "t" + * usually. + */ + if( im_incheck( mask ) || + im_incheck( ink ) || + im_copy( in, out ) ) + return( -1 ); + + mask_rect.left = mask->Xsize / 2; + mask_rect.top = mask->Ysize / 2; + mask_rect.width = mask->Xsize; + mask_rect.height = mask->Ysize; + + for( i = 0; i < n; i++ ) { + if( im_fastlineuser( out, x1v[i], y1v[i], x2v[i], y2v[i], + im_plotmask, ink->data, mask->data, &mask_rect ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/inplace/man3/Makefile.am b/libsrc/inplace/man3/Makefile.am new file mode 100644 index 00000000..d8379a52 --- /dev/null +++ b/libsrc/inplace/man3/Makefile.am @@ -0,0 +1,18 @@ +man_MANS = \ + im_circle.3 \ + im_fastline.3 \ + im_fastlineuser.3 \ + im_flood.3 \ + im_flood_blob.3 \ + im_insertplace.3 \ + im_line.3 \ + im_lineset.3 \ + im_paintrect.3 \ + im_plotmask.3 \ + im_plotpoint.3 \ + im_readpoint.3 \ + im_smear.3 \ + im_smudge.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/inplace/man3/im_circle.3 b/libsrc/inplace/man3/im_circle.3 new file mode 100644 index 00000000..0306aad5 --- /dev/null +++ b/libsrc/inplace/man3/im_circle.3 @@ -0,0 +1,33 @@ +.TH IM_CIRCLE 3 "10 May 1991" +.SH NAME +im_circle \- draws a circle within an image file +.SH SYNOPSIS +.B #include + +.B int im_circle(im, cx, cy, radius, intensity) +.br +.B IMAGE *im; +.br +.B int cx, cy; +.br +.B int radius, intensity; +.SH DESCRIPTION +.B im_circle() +draws a circle on top of an image pointed by the image descriptor im. +Input im should be one band unsigned char image. + +It must be stressed that a call to this function overwrites the content +of the original image at the points corresponding to the drawn circle. +The centre of the circle is at point (cx, cy) and has radius radius and +the intensity of the produced circle is given by the int intensity (between +0 and 255). If the circle does not fit in the original image, an error code +is returned. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_line(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/inplace/man3/im_fastline.3 b/libsrc/inplace/man3/im_fastline.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_fastline.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_fastlineuser.3 b/libsrc/inplace/man3/im_fastlineuser.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_fastlineuser.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_flood.3 b/libsrc/inplace/man3/im_flood.3 new file mode 100644 index 00000000..a261e53e --- /dev/null +++ b/libsrc/inplace/man3/im_flood.3 @@ -0,0 +1,41 @@ +.TH IM_FLOOD 3 "30 October 1992" +.SH NAME +im_flood, im_flood_blob \- flood a area +.SH SYNOPSIS +.B #include +.B #include + +int im_flood( im, x, y, ink, dout ) +.br +IMAGE *im; +.br +int x, y; +.br +PEL *ink; +.br +Rect *dout; + +int im_flood_blob( im, x, y, ink, dout ) +.br +IMAGE *im; +.br +int x, y; +.br +PEL *ink; +.br +Rect *dout; + +.SH DESCRIPTION +.B im_flood() +fills an enclosed area from a starting point, painting ink into 4-way +connected pels whose colour is not equal to ink. + +.B im_flood_blob() +floods with the ink colour, finding pels 4-way connected to the start pel +which are the same colour as the start pel. It is useful for changing the +colour of a blob of pels which all have the same value. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_insertplace(3), im_smudge(3). diff --git a/libsrc/inplace/man3/im_flood_blob.3 b/libsrc/inplace/man3/im_flood_blob.3 new file mode 100644 index 00000000..dac0a2ee --- /dev/null +++ b/libsrc/inplace/man3/im_flood_blob.3 @@ -0,0 +1 @@ +.so man3/im_flood.3 diff --git a/libsrc/inplace/man3/im_insertplace.3 b/libsrc/inplace/man3/im_insertplace.3 new file mode 100644 index 00000000..c3cb9dba --- /dev/null +++ b/libsrc/inplace/man3/im_insertplace.3 @@ -0,0 +1,27 @@ +.TH IM_INSERTPLACE 3 "30 October 1992" +.SH NAME +im_insertplace \- paste small images into big images +.SH SYNOPSIS +.B #include + +im_insertplace( big, small, x, y ) +.br +IMAGE *big, *small; +.br +int x, y; + +.SH DESCRIPTION +Paste image small into image big, with small's left-left-hand corner at (x,y) +in image big. Image big must be large enough to hold all of small! No +clipping. Images may have any type, but must both have the same type. + +This is an in-place operation. Big is damaged! Careful. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE\ ALSO +im_fastline(3), im_smudge(3). +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/inplace/man3/im_line.3 b/libsrc/inplace/man3/im_line.3 new file mode 100644 index 00000000..51f5a998 --- /dev/null +++ b/libsrc/inplace/man3/im_line.3 @@ -0,0 +1,29 @@ +.TH IM_LINE 3 "10 May 1991" +.SH NAME +im_line \- writes a line on a vasari image +.SH SYNOPSIS +.B #include + +.B int im_line(image, x1, y1, x2, y2, pelval) +.br +.B IMAGE *image; +.br +.B int x1, x2, y1, y2, pelval; +.SH DESCRIPTION +im_line() +draws a line in the image held by image. The start of the line is at point +(x1,y1) and the end is at (x2,y2). The intensity of the drawn line is pelval. +Input image should be one byte unsigned char. The function overwrites any +data in the file, so take care when applying it. + +This function is here for compatibility only. You should use im_fastline(3) or +im_fastlineuser(3). +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_grey(3), im_fastline(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/inplace/man3/im_lineset.3 b/libsrc/inplace/man3/im_lineset.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_lineset.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_paintrect.3 b/libsrc/inplace/man3/im_paintrect.3 new file mode 100644 index 00000000..2c9381d6 --- /dev/null +++ b/libsrc/inplace/man3/im_paintrect.3 @@ -0,0 +1,126 @@ +.TH IM_AND 3 "30 October 1992" +.SH NAME +im_paintrect, im_plotmask, im_readpoint, im_plotpoint, im_fastline, +im_fastlineuser \- suck pels up, and paint them down somewhere else +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_readpoint( im, x, y, ink ) +.br +IMAGE *im; +.br +int x, y; +.br +PEL *ink; + +int im_plotpoint( im, x, y, ink ) +.br +IMAGE *im; +.br +int x, y; +.br +PEL *ink; + +int im_plotmask( im, ix, iy, ink, mask, r ) +.br +IMAGE *im; +.br +int ix, iy; +.br +PEL *ink; +.br +PEL *mask; +.br +Rect *r; + +int im_paintrect( im, r, ink ) +.br +IMAGE *im; +.br +Rect *r; +.br +PEL *ink; + +int im_fastline( im, x1, y1, x2, y2, ink ) +.br +IMAGE *im; +.br +int x1, y1, x2, y2; +.br +PEL *ink; + +int im_fastlineuser( im, x1, y1, x2, y2, +.br + plot_fn, client1, client2, client3 ) +.br +IMAGE *im; +.br +int x1, y1, x2, y2; +.br +int (*plot_fn)(); +.br +void *client1, *client2, *client3; + +int plot_fn( im, x, y, client1, client2, client3 ) +.br +IMAGE *im; +.br +int x, y; +.br +void *client1, *client2, *client3; + +int im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink, +.br + int n, int *x1v, int *y1v, int *x2v, int *y2v ) + +.SH DESCRIPTION +.B im_readpoint(3) +sucks a pel from position (x,y) into a buffer; all other +functions paint that buffer back again in various ways. These functions will +work for an image of any type, since they rely on +.B im_readpoint(3) +for their +data. If you read a pel from one image and paint with it to another, it is +your responsibility to check that the two images have identical pels. + +All these functions are `in place,' that is, they write directly to the +output image! Be very careful, you can destroy data. + +.B im_plotmask(3) +takes an array of 0/255s and a Rect describing the size and +offset of the array. It adds (ix,iy) to the array offset, and then paint pels +into all array positions which are non-zero. Rect need not lie inside the +image - the function clips carefully at the edges. + +.B im_paintrect(3) +fills the rectangle described with ink. The Rect can be any +size and at any position - the function clipps against the edges of the +image. + +.B im_fastline(3) +is a replacement for +.B im_line(3) +which plots the specified ink in +the image. The start and end points must lie entirely inside the image. + +.B im_fastlineuser(3) +does not plot points, instead it calls a user plot function +for every position along the line. There is no clipping - the endpoints may be +anywhere at all. Three client values are carried around and passed into the +user function. + +.B im_lineset(3) +copies the image and draws a set of lines on the copy. The lines are drawn +using the mask image as a brush to apply the ink image. Useful for nip2. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE\ ALSO +im_insertplace(3), im_smudge(3). +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/inplace/man3/im_plotmask.3 b/libsrc/inplace/man3/im_plotmask.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_plotmask.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_plotpoint.3 b/libsrc/inplace/man3/im_plotpoint.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_plotpoint.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_readpoint.3 b/libsrc/inplace/man3/im_readpoint.3 new file mode 100644 index 00000000..24a57b00 --- /dev/null +++ b/libsrc/inplace/man3/im_readpoint.3 @@ -0,0 +1 @@ +.so man3/im_paintrect.3 diff --git a/libsrc/inplace/man3/im_smear.3 b/libsrc/inplace/man3/im_smear.3 new file mode 100644 index 00000000..b180aebc --- /dev/null +++ b/libsrc/inplace/man3/im_smear.3 @@ -0,0 +1 @@ +.so man3/im_smudge.3 diff --git a/libsrc/inplace/man3/im_smudge.3 b/libsrc/inplace/man3/im_smudge.3 new file mode 100644 index 00000000..d7825ca4 --- /dev/null +++ b/libsrc/inplace/man3/im_smudge.3 @@ -0,0 +1,42 @@ +.TH IM_SMUDGE 3 "30 October 1992" +.SH NAME +im_smudge, im_smear \- filter and smudge in place +.SH SYNOPSIS +.B #include +.br +.B #include + +int +.br +im_smudge( im, ix, iy, r ) +.br +IMAGE *im; +.br +int ix, iy; +.br +Rect *r; + +int +.br +im_smear( im, ix, iy, r ) +.br +IMAGE *im; +.br +int ix, iy; +.br +Rect *r; +.SH DESCRIPTION +im_smudge() performs a low-pass filter of the pels inside rect r. Rect r is +clipped against the edges of the image. im_smear() is not very useful. + +Both these functions are `in place,' that is, they write directly to the +output image! Be very careful, you can destroy data. +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE\ ALSO +im_insertplace(3), im_readpoint(3). +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/inplace/plot_point.c b/libsrc/inplace/plot_point.c new file mode 100644 index 00000000..379f3157 --- /dev/null +++ b/libsrc/inplace/plot_point.c @@ -0,0 +1,119 @@ +/* @(#) Read a pel out of an image and into a buffer, plot a pel back into + * @(#) the image again. + * @(#) + * @(#) int + * @(#) im_readpoint( IMAGE *im, int x, int y, PEL *ink ) + * @(#) + * @(#) int + * @(#) im_plotpoint( IMAGE *im, int x, int y, PEL *ink ) + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Read a colour from an image. + */ +int +im_readpoint( IMAGE *im, int x, int y, PEL *pel ) +{ + int es = im->Bbits >> 3; + int ps = es * im->Bands; + int ls = ps * im->Xsize; + int b; + PEL *from; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Check coordinates in range. + */ + if( x > im->Xsize || x < 0 || y > im->Ysize || y < 0 ) { + im_errormsg( "im_readpoint: invalid cooordinates" ); + return( 1 ); + } + + /* Suck single pixel. + */ + from = (PEL *) im->data + x * ps + y * ls; + for( b = 0; b < ps; b++ ) + *pel++ = *from++; + + return( 0 ); +} + +/* Plot a point in an image. + */ +int +im_plotpoint( IMAGE *im, int x, int y, PEL *pel ) +{ + int es = im->Bbits >> 3; + int ps = es * im->Bands; + int ls = ps * im->Xsize; + int b; + PEL *to; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Check coordinates in range. + */ + if( x > im->Xsize || x < 0 || y > im->Ysize || y < 0 ) + return( 0 ); + + /* Paint single pixel. + */ + to = (PEL *) im->data + x * ps + y * ls; + for( b = 0; b < ps; b++ ) + *to++ = *pel++; + + im_invalidate( im ); + + return( 0 ); +} diff --git a/libsrc/inplace/smudge_area.c b/libsrc/inplace/smudge_area.c new file mode 100644 index 00000000..73730fa9 --- /dev/null +++ b/libsrc/inplace/smudge_area.c @@ -0,0 +1,319 @@ +/* @(#) Smudge and smear a piece of image. Smudge is a low-pass filter, + * @(#) smear is an early version of smudge which contains a bug: it + * @(#) pushes pels to the left a little too. Looks cute though. + * @(#) + * @(#) r is the area to smudge/smear. Clipped against the image edges + * @(#) properly. + * @(#) + * @(#) int + * @(#) im_smudge( IMAGE *im, int ix, int iy, Rect *r ) + * @(#) + * @(#) int + * @(#) im_smear( IMAGE *im, int ix, int iy, Rect *r ) + * @(#) + * + * Copyright: J. Cupitt + * Written: 15/06/1992 + * 22/7/93 JC + * - im_incheck() added + * 16/8/94 JC + * - im_incheck() changed to im_makerw() + * ? JC + * - im_makerw() changed to im_rwcheck() + * 5/12/06 + * - im_invalidate() after paint + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Smudge a section of an IMAGE. Smudge area r offset by x, y. Take average + * of pixels in 3x3 area surrounding current pixel for every pixel in r. We do + * not change the outermost pixels in the image, although we do read them. + */ +int +im_smudge( IMAGE *im, int ix, int iy, Rect *r ) +{ + int x, y, a, b, c; + int ba = im->Bands; + int el = ba * im->Xsize; + Rect area, image, clipped; + double total[ 256 ]; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Don't do the margins. + */ + area = *r; + area.left += ix; + area.top += iy; + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_marginadjust( &image, -1 ); + im_rect_intersectrect( &area, &image, &clipped ); + + /* Any left? + */ + if( im_rect_isempty( &clipped ) ) + return( 0 ); + +/* What we do for each type. + */ +#define SMUDGE(TYPE) \ + for( y = clipped.top; y < clipped.top + clipped.height; y++ ) \ + for( x = clipped.left; \ + x < clipped.left + clipped.width; x++ ) { \ + TYPE *to = (TYPE *) im->data + x * ba + y * el; \ + TYPE *from = to - el - ba; \ + TYPE *f; \ + \ + for( a = 0; a < ba; a++ ) \ + total[a] = 0.0; \ + \ + for( a = 0; a < 3; a++ ) { \ + f = from; \ + for( b = 0; b < 3; b++ ) \ + for( c = 0; c < ba; c++ ) \ + total[c] += *f++; \ + from += el; \ + } \ + \ + for( a = 0; a < ba; a++ ) \ + to[a] = (16 * (double) to[a] + total[a]) \ + / 25.0; \ + } + + /* Loop through the remaining pixels. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + SMUDGE(unsigned char); + break; + + case IM_BANDFMT_CHAR: + SMUDGE(char); + break; + + case IM_BANDFMT_USHORT: + SMUDGE(unsigned short); + break; + + case IM_BANDFMT_SHORT: + SMUDGE(short); + break; + + case IM_BANDFMT_UINT: + SMUDGE(unsigned int); + break; + + case IM_BANDFMT_INT: + SMUDGE(int); + break; + + case IM_BANDFMT_FLOAT: + SMUDGE(float); + break; + + case IM_BANDFMT_DOUBLE: + SMUDGE(double); + break; + + /* Do complex types too. Just treat as float and double, but with + * twice the number of bands. + */ + case IM_BANDFMT_COMPLEX: + /* Twice number of bands: double size and bands. + */ + ba *= 2; + el *= 2; + + SMUDGE(float); + + break; + + case IM_BANDFMT_DPCOMPLEX: + /* Twice number of bands: double size and bands. + */ + ba *= 2; + el *= 2; + + SMUDGE(double); + + break; + + default: + im_error( "im_smudge", _( "unknown band format" ) ); + return( -1 ); + + } + + im_invalidate( im ); + + return( 0 ); +} + +/* Smear a section of an IMAGE. As above, but shift it left a bit. + */ +int +im_smear( IMAGE *im, int ix, int iy, Rect *r ) +{ + int x, y, a, b, c; + int ba = im->Bands; + int el = ba * im->Xsize; + Rect area, image, clipped; + double total[ 256 ]; + + if( im_rwcheck( im ) ) + return( -1 ); + + /* Don't do the margins. + */ + area = *r; + area.left += ix; + area.top += iy; + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_marginadjust( &image, -1 ); + image.left--; + im_rect_intersectrect( &area, &image, &clipped ); + + /* Any left? + */ + if( im_rect_isempty( &clipped ) ) + return( 0 ); + +/* What we do for each type. + */ +#define SMEAR(TYPE) \ + for( y = clipped.top; y < clipped.top + clipped.height; y++ ) \ + for( x = clipped.left; \ + x < clipped.left + clipped.width; x++ ) { \ + TYPE *to = (TYPE *) im->data + x * ba + y * el; \ + TYPE *from = to - el; \ + TYPE *f; \ + \ + for( a = 0; a < ba; a++ ) \ + total[a] = 0.0; \ + \ + for( a = 0; a < 3; a++ ) { \ + f = from; \ + for( b = 0; b < 3; b++ ) \ + for( c = 0; c < ba; c++ ) \ + total[c] += *f++; \ + from += el; \ + } \ + \ + for( a = 0; a < ba; a++ ) \ + to[a] = (40 * (double) to[a+ba] + total[a]) \ + / 49.0; \ + } + + /* Loop through the remaining pixels. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + SMEAR(unsigned char); + break; + + case IM_BANDFMT_CHAR: + SMEAR(char); + break; + + case IM_BANDFMT_USHORT: + SMEAR(unsigned short); + break; + + case IM_BANDFMT_SHORT: + SMEAR(short); + break; + + case IM_BANDFMT_UINT: + SMEAR(unsigned int); + break; + + case IM_BANDFMT_INT: + SMEAR(int); + break; + + case IM_BANDFMT_FLOAT: + SMEAR(float); + break; + + case IM_BANDFMT_DOUBLE: + SMEAR(double); + break; + + /* Do complex types too. Just treat as float and double, but with + * twice the number of bands. + */ + case IM_BANDFMT_COMPLEX: + /* Twice number of bands: double size and bands. + */ + ba *= 2; + el *= 2; + + SMEAR(float); + + break; + + case IM_BANDFMT_DPCOMPLEX: + /* Twice number of bands: double size and bands. + */ + ba *= 2; + el *= 2; + + SMEAR(double); + + break; + + default: + im_error( "im_smear", _( "unknown band format" ) ); + return( -1 ); + } + + im_invalidate( im ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/Makefile.am b/libsrc/iofuncs/Makefile.am new file mode 100644 index 00000000..0a1c17f0 --- /dev/null +++ b/libsrc/iofuncs/Makefile.am @@ -0,0 +1,64 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libiofuncs.la + +libiofuncs_la_SOURCES = \ + meta.c \ + base64.h \ + base64.c \ + callback.c \ + debug.c \ + dispatch_types.c \ + error.c \ + error_exit.c \ + im_binfile.c \ + im_bits_of_fmt.c \ + im_close.c \ + im_cp_desc.c \ + im_debugim.c \ + im_demand_hint.c \ + im_desc_hd.c \ + im_generate.c \ + im_header.c \ + im_histlin.c \ + im_image.c \ + im_init.c \ + im_initdesc.c \ + im_iocheck.c \ + im_iterate.c \ + im_makerw.c \ + im_mapfile.c \ + im_openin.c \ + im_open.c \ + im_openout.c \ + im_partial.c \ + im_piocheck.c \ + im_prepare.c \ + im_printdesc.c \ + im_printlines.c \ + im_readhist.c \ + im_render.c \ + im_setbox.c \ + im_setbuf.c \ + im_setupout.c \ + im_unmapfile.c \ + im_updatehist.c \ + im_guess_prefix.c \ + im_wrapmany.c \ + im_wrapone.c \ + im_writeline.c \ + memory.c \ + package.c \ + predicate.c \ + region.c \ + rect.c \ + semaphore.c \ + threadgroup.c \ + util.c \ + im_init_world.c \ + vbuf.c \ + window.c \ + buffer.c \ + time.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/iofuncs/base64.c b/libsrc/iofuncs/base64.c new file mode 100644 index 00000000..d0479229 --- /dev/null +++ b/libsrc/iofuncs/base64.c @@ -0,0 +1,285 @@ +/* base64.c -- Encode/decode integers in base64 format + * Created: Mon Sep 23 16:55:12 1996 by faith@dict.org + * Revised: Sat Mar 30 12:02:36 2002 by faith@dict.org + * Copyright 1996, 2002 Rickard E. Faith (faith@dict.org) + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: base64.c,v 1.5 2007/01/03 18:31:34 jcupitt Exp $ + * + * \section{Base-64 Routines} + * + * \intro These routines use the 64-character subset of International + * Alphabet IA5 discussed in RFC 1421 (printeable encoding) and RFC 1522 + * (base64 MIME). + * + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + * + */ + +/* + + Hacked for VIPS ... does any length object (not just ints), formats + base64 into 70 character lines, output to a malloc'd buffer. + + VIPS uses this to write BLOBs (like ICC profiles, for example) to the + XML that follows an image. + +Modified on: +23/7/07 JC + - oop, needed a slightly larger worst-case buffer in im__b64_encode() + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "base64.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static unsigned char b64_list[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define XX 100 + +static unsigned char b64_index[256] = { + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63, + 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX, + XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX, + XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, +}; + +/* Read (up to) 3 bytes from in. Be careful about byte ordering :-/ we need to + * end up with in[2] in the bottom few bits. + */ +static int +read24( const unsigned char *in, size_t remaining ) +{ + int bits; + int i; + + bits = 0; + for( i = 0; i < 3; i++ ) { + bits <<= 8; + if( remaining > 0 ) { + bits |= in[i]; + remaining -= 1; + } + } + + return( bits ); +} + +/* Output (up to) 24 bits as four base64 chars. Pad with '=' characters. + */ +static void +encode24( char *p, int bits, size_t remaining ) +{ + int i; + + for( i = 0; i < 4; i++ ) { + if( remaining <= 0 ) + p[i] = '='; + else { + /* Take the top 6 bits of 24. + */ + p[i] = b64_list[(bits >> 18) & 63]; + bits <<= 6; + remaining -= 6; + } + } +} + +/* Output to a malloc'd buffer, NULL on error. Try to be simple and reliable, + * rather than quick. + */ +char * +im__b64_encode( const unsigned char *data, size_t data_length ) +{ + /* Worst case: 1.333 chars per byte, plus 10% for extra carriage + * returns and stuff. And the \n\0 at the end. + */ + const size_t output_data_length = data_length * 44 / 30 + 2; + + char *buffer; + char *p; + size_t i; + int cursor; + + if( data_length <= 0 ) { + im_error( "im__b64_encode", _( "too little data" ) ); + return( NULL ); + } + if( output_data_length > 1024 * 1024 ) { + /* We shouldn't really be used for large amounts of data. + */ + im_error( "im__b64_encode", _( "too much data" ) ); + return( NULL ); + } + if( !(buffer = im_malloc( NULL, output_data_length )) ) + return( NULL ); + + p = buffer; + *p++ = '\n'; + cursor = 0; + + for( i = 0; i < data_length; i += 3 ) { + size_t remaining = data_length - i; + int bits; + + bits = read24( data + i, remaining ); + encode24( p, bits, remaining * 8 ); + p += 4; + cursor += 4; + + if( cursor >= 76 ) { + *p++ = '\n'; + cursor = 0; + } + } + if( cursor > 0 ) + *p++ = '\n'; + *p++ = '\0'; + +#ifdef DEBUG +{ + unsigned int total; + + /* Calculate a very simple checksum for debugging. + */ + for( total = 0, i = 0; i < data_length; i++ ) + total += data[i]; + + printf( "im__b64_encode: length = %d, checksum 0x%x\n", + data_length, total & 0xffff ); +} +#endif /*DEBUG*/ + + return( buffer ); +} + +/* Decode base64 back to binary in a malloc'd buffer. NULL on error. + */ +unsigned char * +im__b64_decode( const char *buffer, size_t *data_length ) +{ + const size_t buffer_length = strlen( buffer ); + + /* Worst case. + */ + const size_t output_data_length = buffer_length * 3 / 4; + + unsigned char *data; + unsigned char *p; + unsigned int bits; + int nbits; + size_t i; + + if( output_data_length > 1024 * 1024 ) { + /* We shouldn't really be used for large amounts of data. + */ + im_error( "im__b64_decode", _( "too much data" ) ); + return( NULL ); + } + + if( !(data = im_malloc( NULL, output_data_length )) ) + return( NULL ); + + p = data; + bits = 0; + nbits = 0; + + for( i = 0; i < buffer_length; i++ ) { + unsigned int val; + + if( (val = b64_index[(int) buffer[i]]) != XX ) { + bits <<= 6; + bits |= val; + nbits += 6; + + if( nbits >= 8 ) { + *p++ = (bits >> (nbits - 8)) & 0xff; + nbits -= 8; + } + } + } + + assert( p - data < output_data_length ); + + if( data_length ) + *data_length = p - data; + +#ifdef DEBUG +{ + unsigned int total; + + /* Calculate a very simple checksum for debugging. + */ + for( total = 0, i = 0; i < p - data; i++ ) + total += data[i]; + + printf( "im__b64_decode: length = %d, checksum 0x%x\n", + p - data, total & 0xffff ); +} +#endif /*DEBUG*/ + + return( data ); +} diff --git a/libsrc/iofuncs/base64.h b/libsrc/iofuncs/base64.h new file mode 100644 index 00000000..ffe09f28 --- /dev/null +++ b/libsrc/iofuncs/base64.h @@ -0,0 +1,25 @@ +/* + + Copyright (C) 1991-2005 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* base64 encode/decode functions. + */ + +char *im__b64_encode( const unsigned char *data, size_t data_length ); +unsigned char *im__b64_decode( const char *buffer, size_t *data_length ); diff --git a/libsrc/iofuncs/buffer.c b/libsrc/iofuncs/buffer.c new file mode 100644 index 00000000..03dd439a --- /dev/null +++ b/libsrc/iofuncs/buffer.c @@ -0,0 +1,579 @@ +/* Manage sets of pixel buffers on an image. + * + * 30/10/06 + * - from window.c + * 2/2/07 + * - speed up the search, use our own lock (thanks Christian) + * 5/2/07 + * - split to many buffer lists per image + * 11/2/07 + * - split to a buffer hash per thread + * - reuse buffer mallocs when we can + * 20/2/07 + * - add im_buffer_cache_list_t and we can avoid some hash ops on + * done/undone + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG +#define DEBUG_CREATE + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef DEBUG +/* Track all regions here for debugging. + */ +static GSList *im__buffers_all = NULL; +#endif /*DEBUG*/ + +#ifdef DEBUG_CREATE +static int buffer_cache_n = 0; +#endif /*DEBUG_CREATE*/ + +#ifdef HAVE_THREADS +static GPrivate *thread_buffer_cache_key = NULL; +#else /*!HAVE_THREADS*/ +static im_buffer_cache_t *thread_buffer_cache = NULL; +#endif /*HAVE_THREADS*/ + +/* Only need this if we're threading and need to do a lot of start/stop. + */ +#ifdef HAVE_THREADS +static void +buffer_cache_free( im_buffer_cache_t *cache ) +{ +#ifdef DEBUG_CREATE + buffer_cache_n -= 1; + + printf( "buffer_cache_free: freeing cache %p on thread %p\n", + cache, g_thread_self() ); + printf( "\t(%d cachees left)\n", buffer_cache_n ); +#endif /*DEBUG_CREATE*/ + + IM_FREEF( g_hash_table_destroy, cache->hash ); + IM_FREE( cache ); +} +#endif /*HAVE_THREADS*/ + +static void +buffer_cache_list_free( im_buffer_cache_list_t *cache_list ) +{ + GSList *p; + + /* Need to mark undone so we don't try and take them off this hash on + * unref. + */ + for( p = cache_list->buffers; p; p = p->next ) { + im_buffer_t *buffer = (im_buffer_t *) p->data; + + buffer->done = FALSE; + } + + g_slist_free( cache_list->buffers ); + im_free( cache_list ); +} + +static im_buffer_cache_list_t * +buffer_cache_list_new( im_buffer_cache_t *cache, IMAGE *im ) +{ + im_buffer_cache_list_t *cache_list; + + if( !(cache_list = IM_NEW( NULL, im_buffer_cache_list_t )) ) + return( NULL ); + cache_list->buffers = NULL; + cache_list->thread = g_thread_self(); + cache_list->cache = cache; + cache_list->im = im; + +#ifdef DEBUG_CREATE + printf( "buffer_cache_list_new: new cache %p for thread %p\n", + cache, g_thread_self() ); + printf( "\t(%d cachees now)\n", buffer_cache_n ); +#endif /*DEBUG_CREATE*/ + + return( cache_list ); +} + +static im_buffer_cache_t * +buffer_cache_new( void ) +{ + im_buffer_cache_t *cache; + + if( !(cache = IM_NEW( NULL, im_buffer_cache_t )) ) + return( NULL ); + + cache->hash = g_hash_table_new_full( g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) buffer_cache_list_free ); + cache->thread = g_thread_self(); + +#ifdef DEBUG_CREATE + buffer_cache_n += 1; + + printf( "buffer_cache_new: new cache %p for thread %p\n", + cache, g_thread_self() ); + printf( "\t(%d cachees now)\n", buffer_cache_n ); +#endif /*DEBUG_CREATE*/ + + return( cache ); +} + +/* Get the buffer cache. + */ +static im_buffer_cache_t * +buffer_cache_get( void ) +{ + im_buffer_cache_t *cache; + +#ifdef HAVE_THREADS + if( !(cache = g_private_get( thread_buffer_cache_key )) ) { + cache = buffer_cache_new(); + g_private_set( thread_buffer_cache_key, cache ); + } +#else /*!HAVE_THREADS*/ + if( !thread_buffer_cache ) + thread_buffer_cache = buffer_cache_new(); + cache = thread_buffer_cache; +#endif /*HAVE_THREADS*/ + + return( cache ); +} + +/* Pixels have been calculated: publish for other parts of this thread to see. + */ +void +im_buffer_done( im_buffer_t *buffer ) +{ + if( !buffer->done ) { + IMAGE *im = buffer->im; + im_buffer_cache_t *cache = buffer_cache_get(); + im_buffer_cache_list_t *cache_list; + +#ifdef DEBUG + printf( "im_buffer_done: thread %p adding " + "buffer %p to cache %p\n", + g_thread_self(), buffer, cache ); +#endif /*DEBUG*/ + + /* Look up and update the buffer list. + */ + if( !(cache_list = g_hash_table_lookup( cache->hash, im )) ) { + cache_list = buffer_cache_list_new( cache, im ); + g_hash_table_insert( cache->hash, im, cache_list ); + } + + assert( !g_slist_find( cache_list->buffers, buffer ) ); + assert( cache_list->thread == cache->thread ); + + cache_list->buffers = + g_slist_prepend( cache_list->buffers, buffer ); + buffer->done = TRUE; + buffer->cache = cache; + } +} + +/* Take off the public 'done' list. + */ +void +im_buffer_undone( im_buffer_t *buffer ) +{ + if( buffer->done ) { + IMAGE *im = buffer->im; + im_buffer_cache_t *cache = buffer->cache; + im_buffer_cache_list_t *cache_list; + +#ifdef DEBUG + printf( "im_buffer_undone: thread %p removing " + "buffer %p from cache %p\n", + g_thread_self(), buffer, cache ); +#endif /*DEBUG*/ + + assert( cache->thread == g_thread_self() ); + + cache_list = g_hash_table_lookup( cache->hash, im ); + + assert( cache_list ); + assert( cache_list->thread == cache->thread ); + assert( g_slist_find( cache_list->buffers, buffer ) ); + + cache_list->buffers = + g_slist_remove( cache_list->buffers, buffer ); + buffer->done = FALSE; + buffer->cache = NULL; + +#ifdef DEBUG + printf( "im_buffer_undone: %d buffers left\n", + g_slist_length( buffers ) ); +#endif /*DEBUG*/ + } +} + +void +im_buffer_unref( im_buffer_t *buffer ) +{ +#ifdef DEBUG + printf( "** im_buffer_unref: left = %d, top = %d, " + "width = %d, height = %d (%p)\n", + buffer->area.left, buffer->area.top, + buffer->area.width, buffer->area.height, + buffer ); +#endif /*DEBUG*/ + + assert( buffer->ref_count > 0 ); + + buffer->ref_count -= 1; + + if( buffer->ref_count == 0 ) { +#ifdef DEBUG + if( !buffer->done ) + printf( "im_buffer_unref: buffer was not done\n" ); +#endif /*DEBUG*/ + + im_buffer_undone( buffer ); + + buffer->im = NULL; + IM_FREE( buffer->buf ); + buffer->bsize = 0; + im_free( buffer ); + +#ifdef DEBUG + g_mutex_lock( im__global_lock ); + assert( g_slist_find( im__buffers_all, buffer ) ); + im__buffers_all = g_slist_remove( im__buffers_all, buffer ); + printf( "%d buffers in vips\n", + g_slist_length( im__buffers_all ) ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG*/ + } +} + +/* Make a new buffer. + */ +static im_buffer_t * +buffer_new( IMAGE *im, Rect *area ) +{ + im_buffer_t *buffer; + + if( !(buffer = IM_NEW( NULL, im_buffer_t )) ) + return( NULL ); + + buffer->ref_count = 1; + buffer->im = im; + buffer->area = *area; + buffer->done = FALSE; + buffer->cache = NULL; + buffer->invalid = FALSE; + buffer->bsize = (size_t) IM_IMAGE_SIZEOF_PEL( im ) * + area->width * area->height; + if( !(buffer->buf = im_malloc( NULL, buffer->bsize )) ) { + im_buffer_unref( buffer ); + return( NULL ); + } + +#ifdef DEBUG + printf( "** buffer_new: left = %d, top = %d, " + "width = %d, height = %d (%p)\n", + buffer->area.left, buffer->area.top, + buffer->area.width, buffer->area.height, + buffer ); +#endif /*DEBUG*/ + +#ifdef DEBUG + g_mutex_lock( im__global_lock ); + im__buffers_all = g_slist_prepend( im__buffers_all, buffer ); + printf( "%d buffers in vips\n", g_slist_length( im__buffers_all ) ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG*/ + + return( buffer ); +} + +static int +buffer_move( im_buffer_t *buffer, Rect *area ) +{ + IMAGE *im = buffer->im; + size_t new_bsize; + + assert( buffer->ref_count == 1 ); + + buffer->area = *area; + im_buffer_undone( buffer ); + assert( !buffer->done ); + buffer->invalid = FALSE; + + new_bsize = (size_t) IM_IMAGE_SIZEOF_PEL( im ) * + area->width * area->height; + if( buffer->bsize < new_bsize ) { + buffer->bsize = new_bsize; + IM_FREE( buffer->buf ); + if( !(buffer->buf = im_malloc( NULL, buffer->bsize )) ) + return( -1 ); + } + + return( 0 ); +} + +/* Find an existing buffer that encloses area and return a ref. + */ +static im_buffer_t * +buffer_find( IMAGE *im, Rect *r ) +{ + im_buffer_cache_t *cache = buffer_cache_get(); + im_buffer_cache_list_t *cache_list; + im_buffer_t *buffer; + GSList *p; + Rect *area; + + cache_list = g_hash_table_lookup( cache->hash, im ); + p = cache_list ? cache_list->buffers : NULL; + + /* This needs to be quick :-( don't use + * im_slist_map2()/im_rect_includesrect(), do the search inline. + */ + for( ; p; p = p->next ) { + buffer = (im_buffer_t *) p->data; + area = &buffer->area; + + if( area->left <= r->left && + area->top <= r->top && + area->left + area->width >= r->left + r->width && + area->top + area->height >= r->top + r->height ) { + buffer->ref_count += 1; + +#ifdef DEBUG + printf( "im_buffer_find: left = %d, top = %d, " + "width = %d, height = %d, count = %d (%p)\n", + buffer->area.left, buffer->area.top, + buffer->area.width, buffer->area.height, + buffer->ref_count, + buffer ); +#endif /*DEBUG*/ + + break; + } + } + + if( p ) + return( buffer ); + else + return( NULL ); +} + +/* Return a ref to a buffer that encloses area. + */ +im_buffer_t * +im_buffer_ref( IMAGE *im, Rect *area ) +{ + im_buffer_t *buffer; + + if( !(buffer = buffer_find( im, area )) ) + /* No existing buffer ... make a new one. + */ + if( !(buffer = buffer_new( im, area )) ) + return( NULL ); + + return( buffer ); +} + +/* Unref old, ref new, in a single operation. Move the buffer if we can. + */ +im_buffer_t * +im_buffer_unref_ref( im_buffer_t *old_buffer, IMAGE *im, Rect *area ) +{ + im_buffer_t *buffer; + + assert( !old_buffer || old_buffer->im == im ); + + if( (buffer = buffer_find( im, area )) && !buffer->invalid ) { + /* The new area has an OK buffer already: use that. + */ + IM_FREEF( im_buffer_unref, old_buffer ); + } + else if( old_buffer && old_buffer->ref_count == 1 ) { + /* The old buffer is not shared ... we can reuse it. + */ + buffer = old_buffer; + if( buffer_move( buffer, area ) ) { + im_buffer_unref( buffer ); + return( NULL ); + } + } + else { + /* Old buffer in use ... need another. + */ + IM_FREEF( im_buffer_unref, old_buffer ); + if( !(buffer = buffer_new( im, area )) ) + return( NULL ); + } + + return( buffer ); +} + +void +im_buffer_print( im_buffer_t *buffer ) +{ + printf( "im_buffer_t: %p ref_count = %d, ", buffer, buffer->ref_count ); + printf( "im = %p, ", buffer->im ); + printf( "area.left = %d, ", buffer->area.left ); + printf( "area.top = %d, ", buffer->area.top ); + printf( "area.width = %d, ", buffer->area.width ); + printf( "area.height = %d, ", buffer->area.height ); + printf( "done = %d, ", buffer->done ); + printf( "invalid = %d, ", buffer->invalid ); + printf( "buf = %p, ", buffer->buf ); + printf( "bsize = %zd\n", buffer->bsize ); +} + +/* Make a parent/child link. + */ +void +im__link_make( IMAGE *parent, IMAGE *child ) +{ + assert( parent ); + assert( child ); + + parent->children = g_slist_prepend( parent->children, child ); + child->parents = g_slist_prepend( child->parents, parent ); +} + +/* Break link. + */ +static void * +im__link_break( IMAGE *parent, IMAGE *child ) +{ + assert( parent ); + assert( child ); + assert( g_slist_find( parent->children, child ) ); + assert( g_slist_find( child->parents, parent ) ); + + parent->children = g_slist_remove( parent->children, child ); + child->parents = g_slist_remove( child->parents, parent ); + + return( NULL ); +} + +static void * +im__link_break_rev( IMAGE *child, IMAGE *parent ) +{ + return( im__link_break( parent, child ) ); +} + +/* An IMAGE is going ... break all links. + */ +void +im__link_break_all( IMAGE *im ) +{ + im_slist_map2( im->parents, + (VSListMap2Fn) im__link_break, im, NULL ); + im_slist_map2( im->children, + (VSListMap2Fn) im__link_break_rev, im, NULL ); +} + +static void * +im__link_mapp( IMAGE *im, VSListMap2Fn fn, int *serial, void *a, void *b ) +{ + void *res; + + /* Loop? + */ + if( im->serial == *serial ) + return( NULL ); + im->serial = *serial; + + if( (res = fn( im, a, b )) ) + return( res ); + + return( im_slist_map4( im->parents, + (VSListMap4Fn) im__link_mapp, fn, serial, a, b ) ); +} + +/* Apply a function to an image and all it's parents, direct and indirect. + */ +void * +im__link_map( IMAGE *im, VSListMap2Fn fn, void *a, void *b ) +{ + static int serial = 0; + + serial += 1; + return( im__link_mapp( im, fn, &serial, a, b ) ); +} + +static void * +im_invalidate_region( REGION *reg ) +{ + if( reg->buffer ) + reg->buffer->invalid = TRUE; + + return( NULL ); +} + +static void * +im_invalidate_image( IMAGE *im ) +{ + (void) im_slist_map2( im->regions, + (VSListMap2Fn) im_invalidate_region, NULL, NULL ); + + return( NULL ); +} + +/* Invalidate all pixel caches on an IMAGE and any parents. + */ +void +im_invalidate( IMAGE *im ) +{ + (void) im__link_map( im, + (VSListMap2Fn) im_invalidate_image, NULL, NULL ); +} + +/* Init the buffer cache system. + */ +void +im__buffer_init( void ) +{ +#ifdef HAVE_THREADS + if( !thread_buffer_cache_key ) + thread_buffer_cache_key = g_private_new( + (GDestroyNotify) buffer_cache_free ); +#endif /*HAVE_THREADS*/ +} diff --git a/libsrc/iofuncs/callback.c b/libsrc/iofuncs/callback.c new file mode 100644 index 00000000..8e3d82c1 --- /dev/null +++ b/libsrc/iofuncs/callback.c @@ -0,0 +1,145 @@ +/* Close and generate callbacks. + * + * 1/7/93 JC + * 20/7/93 JC + * - eval callbacks added + * 16/8/94 JC + * - evalend callbacks added + * 16/1/04 JC + * - now always calls all callbacks, even if some fail + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Callback struct. We attach a list of callbacks to images to be invoked when + * the image is closed. These do things like closing previous elements in a + * chain of operations, freeing client data, etc. + */ +typedef struct { + IMAGE *im; /* IMAGE we are attached to */ + int (*fn)(); /* callback function */ + void *a, *b; /* arguments to callback */ +} VCallback; + +/* Add a callback to an IMAGE. We can't use IM_NEW(), note! Freed eventually by + * im__close(), or by im_generate(), etc. for evalend callbacks. + */ +static int +add_callback( IMAGE *im, GSList **cblist, int (*fn)(), void *a, void *b ) +{ + VCallback *cbs; + + if( !(cbs = IM_NEW( NULL, VCallback )) ) + return( -1 ); + + cbs->fn = fn; + cbs->a = a; + cbs->b = b; + cbs->im = im; + *cblist = g_slist_prepend( *cblist, cbs ); + + return( 0 ); +} + +/* Add a close callback to an IMAGE. + */ +int +im_add_close_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->closefns, fn, a, b ) ); +} + +/* Add an eval callback to an IMAGE. + */ +int +im_add_eval_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->evalfns, fn, a, b ) ); +} + +/* Add an eval end callback to an IMAGE. + */ +int +im_add_evalend_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->evalendfns, fn, a, b ) ); +} + +/* Perform a user callback. + */ +static void * +call_callback( VCallback *cbs, int *result ) +{ + int res; + + if( (res = cbs->fn( cbs->a, cbs->b )) ) { + im_error( "im__trigger_callbacks", _( "user callback " + "failed for %s" ), cbs->im->filename ); + *result = res; + +#ifdef DEBUG_IO + printf( "im__trigger_callbacks: user callback " + "failed for %s\n", cbs->im->filename ); +#endif /*DEBUG_IO*/ + } + + return( NULL ); +} + +/* Perform a list of user callbacks. + */ +int +im__trigger_callbacks( GSList *cblist ) +{ + int result; + +#ifdef DEBUG_IO + printf( "im__trigger_callbacks: calling %d user callbacks ..\n", + g_slist_length( cblist ) ); +#endif /*DEBUG_IO*/ + + result = 0; + (void) im_slist_map2( cblist, + (VSListMap2Fn) call_callback, &result, NULL ); + + return( result ); +} diff --git a/libsrc/iofuncs/debug.c b/libsrc/iofuncs/debug.c new file mode 100644 index 00000000..5370ae2b --- /dev/null +++ b/libsrc/iofuncs/debug.c @@ -0,0 +1,134 @@ +/* debug.c: support for debugging + * + * 24/10/95 JC + * - first version + * 24/2/05 + * - print more mem allocation info + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Track all open images in this. + */ +GSList *im__open_images = NULL; + +static void * +print_one_line_region( REGION *r, int *n2, int *total ) +{ + if( r->type == IM_REGION_BUFFER && r->buffer ) { + printf( "\t*** %d) %zd malloced bytes\n", + *n2, r->buffer->bsize ); + *total += r->buffer->bsize; + } + + *n2 += 1; + + return( NULL ); +} + +/* Print a one-line description of an image, with an index. + */ +static void * +print_one_line( IMAGE *im, int *n, int *total ) +{ + printf( "%2d) %p, %s, %s: %dx%d, %d bands, %s\n", + *n, + im, + im_dtype2char( im->dtype ), im->filename, + im->Xsize, im->Ysize, im->Bands, + im_BandFmt2char( im->BandFmt ) ); + *n += 1; + + if( im->dtype == IM_SETBUF && im->data ) { + int size = IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize; + + printf( "\t*** %d malloced bytes\n", size ); + *total += size; + } + + if( im->regions ) { + int n2; + int total2; + + printf( "\t%d regions\n", g_slist_length( im->regions ) ); + n2 = 0; + total2 = 0; + (void) im_slist_map2( im->regions, + (VSListMap2Fn) print_one_line_region, &n2, &total2 ); + if( total2 ) + printf( "\t*** using total of %d bytes\n", total2 ); + *total += total2; + } + + return( NULL ); +} + +/* Print one line for each open descriptor. + */ +void +im__print_all( void ) +{ + int n = 0; + int total = 0; + + if( im__open_images ) { + printf( "%d images\n", g_slist_length( im__open_images ) ); + (void) im_slist_map2( im__open_images, + (VSListMap2Fn) print_one_line, &n, &total ); + if( total ) + printf( "\n\t*** all-image total = %d bytes\n", total ); + } +} + +/* Debugging: given an index, print everything we know about that descriptor. + */ +void +im__print_one( int n ) +{ + IMAGE *im = g_slist_nth_data( im__open_images, n ); + + if( !im ) { + printf( "bad index: %d\n", n ); + return; + } + + im_printdesc( im ); +} diff --git a/libsrc/iofuncs/dispatch_types.c b/libsrc/iofuncs/dispatch_types.c new file mode 100644 index 00000000..c723c888 --- /dev/null +++ b/libsrc/iofuncs/dispatch_types.c @@ -0,0 +1,829 @@ +/* Define built-in VIPS types. + * + * J. Cupitt, 8/4/93. + * + * Modified: + * 21/5/07 + * - any length vector (Tom) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Max str we parse. + */ +#define IM_MAX_STR (4096) + +/* String containing each of the characters which can be used within a + * single command line argument to separate the elements of a vector. + */ +#define VEC_SEPS " " + +/* Init function for input displays. + */ +static int +input_display_init( im_object *obj, char *str ) +{ + struct im_col_display *scr = im_col_display_name( str ); + + if( !scr ) { + int i; + + im_error( "input_display", + _( "unknown display type \"%s\"" ), str ); + im_error( "input_display", _( "display should be one of:\n" ) ); + for( i = 0; (scr = im_col_displays( i )); i++ ) + im_error( "input_display", + " '%s'\n", scr->d_name ); + + return( -1 ); + } + + *obj = scr; + + return( 0 ); +} + +/* Input display type. + */ +im_type_desc im__input_display = { + IM_TYPE_DISPLAY, /* Its a display */ + 0, /* No storage needed */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_display_init, /* Init function */ + NULL /* Destroy function */ +}; + +/* Output display type. + */ +im_type_desc im__output_display = { + IM_TYPE_DISPLAY, /* Its a display */ + sizeof( struct im_col_display ),/* Memory to allocate */ + IM_TYPE_OUTPUT, /* Output object */ + NULL, /* Init function */ + NULL /* Destroy function */ +}; + +/* Init function for input images. + */ +static int +input_image_init( im_object *obj, char *str ) +{ + IMAGE **im = (IMAGE **) obj; + + return( !(*im = im_open( str, "r" )) ); +} + +/* Input image type. + */ +im_type_desc im__input_image = { + IM_TYPE_IMAGE, /* Its an image */ + 0, /* No storage needed */ + IM_TYPE_ARG, /* It requires a command-line arg */ + (im_init_obj_fn) input_image_init, /* Init function */ + (im_dest_obj_fn) im_close /* Destroy function */ +}; + +/* Init function for output images. + */ +static int +output_image_init( im_object *obj, char *str ) +{ + IMAGE **im = (IMAGE **) obj; + + return( !(*im = im_open( str, "w" )) ); +} + +/* Output image type. + */ +im_type_desc im__output_image = { + IM_TYPE_IMAGE, /* Its an image */ + 0, /* No storage to be allocated */ + IM_TYPE_OUTPUT | IM_TYPE_ARG, /* Flags! */ + (im_init_obj_fn) output_image_init, /* Init function */ + (im_dest_obj_fn) im_close /* Destroy function */ +}; + +/* Init function for RW images. + */ +static int +rw_image_init( im_object *obj, char *str ) +{ + IMAGE **im = (IMAGE **) obj; + + return( !(*im = im_open( str, "rw" )) ); +} + +/* RW image type. + */ +im_type_desc im__rw_image = { + IM_TYPE_IMAGE, /* Its an image */ + 0, /* No storage to be allocated */ + IM_TYPE_ARG, /* Flags! Pretend its an input type */ + (im_init_obj_fn) rw_image_init, /* Init function */ + (im_dest_obj_fn) im_close /* Destroy function */ +}; + +/* im_imagevec_object destroy function. + */ +static int +imagevec_dest( im_object obj ) +{ + im_imagevec_object *iv = obj; + + if( iv->vec ) { + int i; + + for( i = 0; i < iv->n; i++ ) + if( iv->vec[i] ) { + im_close( iv->vec[i] ); + iv->vec[i] = NULL; + } + + im_free( iv->vec ); + iv->vec = NULL; + iv->n = 0; + } + + return( 0 ); +} + +/* Init function for imagevec input. + */ +static int +input_imagevec_init( im_object *obj, char *str ) +{ + im_imagevec_object *iv = *obj; + char **strv; + int nargs; + int i; + + strv = g_strsplit( str, VEC_SEPS, -1 ); + nargs = g_strv_length( strv ); + + if( !(iv->vec = IM_ARRAY( NULL, nargs, IMAGE * )) ) { + g_strfreev( strv ); + return( -1 ); + } + iv->n = nargs; + + /* Must NULL them out in case we fail halfway though opening them all. + */ + for( i = 0; i < nargs; i++ ) + iv->vec[i] = NULL; + + for( i = 0; i < nargs; i++ ) + if( !(iv->vec[i] = im_open( strv[i], "r" )) ) { + g_strfreev( strv ); + return( -1 ); + } + + g_strfreev( strv ); + + return( 0 ); +} + +/* Input image vector type. + */ +im_type_desc im__input_imagevec = { + IM_TYPE_IMAGEVEC, /* Its an array of IMAGE */ + sizeof( im_imagevec_object ), /* Memory to allocate in vec build */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_imagevec_init, /* Init function */ + imagevec_dest /* Destroy function */ +}; + +/* Init function for masks. "str" can be NULL for output masks. + */ +static int +mask_init( im_object *obj, char *str ) +{ + im_mask_object *mo = *obj; + + /* Install string, clear mask. + */ + if( str && !(mo->name = im_strdup( NULL, str )) ) + return( -1 ); + mo->mask = NULL; + + return( 0 ); +} + +/* Init function for input dmasks. As above, but read in the mask. + */ +static int +dmask_init( im_object *obj, char *str ) +{ + im_mask_object *mo = *obj; + + if( mask_init( obj, str ) ) + return( -1 ); + if( !(mo->mask = im_read_dmask( str )) ) + return( -1 ); + + return( 0 ); +} + +/* Init function for input imasks. + */ +static int +imask_init( im_object *obj, char *str ) +{ + im_mask_object *mo = *obj; + + if( mask_init( obj, str ) ) + return( -1 ); + if( !(mo->mask = im_read_imask( str )) ) + return( -1 ); + + return( 0 ); +} + +/* DOUBLEMASK destroy function. + */ +static int +dmask_dest( im_object obj ) +{ + im_mask_object *mo = obj; + + if( mo->name ) { + im_free( mo->name ); + mo->name = NULL; + } + if( mo->mask ) { + im_free_dmask( (DOUBLEMASK *) mo->mask ); + mo->mask = NULL; + } + + return( 0 ); +} + +/* INTMASK destroy function. + */ +static int +imask_dest( im_object obj ) +{ + im_mask_object *mo = obj; + + if( mo->name ) { + im_free( mo->name ); + mo->name = NULL; + } + if( mo->mask ) { + im_free_imask( (INTMASK *) mo->mask ); + mo->mask = NULL; + } + + return( 0 ); +} + +/* As above, but save the mask first. + */ +static int +save_dmask_dest( im_object obj ) +{ + im_mask_object *mo = obj; + + if( mo->mask && im_write_dmask( mo->mask ) ) + return( -1 ); + return( dmask_dest( obj ) ); +} + +/* As above, but save the mask first. + */ +static int +save_imask_dest( im_object obj ) +{ + im_mask_object *mo = obj; + + if( mo->mask && im_write_imask( mo->mask ) ) + return( -1 ); + return( imask_dest( obj ) ); +} + +/* Output dmask type. + */ +im_type_desc im__output_dmask = { + IM_TYPE_DMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_OUTPUT | IM_TYPE_ARG, /* Flags */ + mask_init, /* Init function */ + save_dmask_dest /* Save and destroy function */ +}; + +/* Input dmask type. + */ +im_type_desc im__input_dmask = { + IM_TYPE_DMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_ARG, /* It requires a command-line arg */ + dmask_init, /* Init function */ + dmask_dest /* Destroy function */ +}; + +/* Output imask type. + */ +im_type_desc im__output_imask = { + IM_TYPE_IMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_OUTPUT | IM_TYPE_ARG, /* Flags */ + mask_init, /* Init function */ + save_imask_dest /* Save and destroy function */ +}; + +/* Input imask type. + */ +im_type_desc im__input_imask = { + IM_TYPE_IMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_ARG, /* It requires a command-line arg */ + imask_init, /* Init function */ + imask_dest /* Destroy function */ +}; + +/* Output dmask to screen type. Set a `print' function to get actual output. + * Used for things like "stats". + */ +im_type_desc im__output_dmask_screen = { + IM_TYPE_DMASK, /* Its a mask */ + sizeof( im_mask_object ),/* Storage for mask object */ + IM_TYPE_OUTPUT, /* Its an output argument */ + mask_init, /* Init function */ + dmask_dest /* Destroy function */ +}; + +/* Init function for double input. + */ +static int +input_double_init( im_object *obj, char *str ) +{ + double *d = (double *) *obj; + + *d = g_ascii_strtod( str, NULL ); + + return( 0 ); +} + +/* Input double type. + */ +im_type_desc im__input_double = { + IM_TYPE_DOUBLE, /* Its a double */ + sizeof( double ), /* Memory to allocate */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_double_init, /* Init function */ + NULL /* Destroy function */ +}; + +/* im_doublevec_object destroy function. + */ +static int +doublevec_dest( im_object obj ) +{ + im_doublevec_object *dv = obj; + + if( dv->vec ) { + im_free( dv->vec ); + dv->vec = NULL; + dv->n = 0; + } + + return( 0 ); +} + +/* Init function for doublevec input. + */ +static int +input_doublevec_init( im_object *obj, char *str ) +{ + im_doublevec_object *dv = *obj; + char **strv; + int nargs; + int i; + + strv = g_strsplit( str, VEC_SEPS, -1 ); + nargs = g_strv_length( strv ); + + if( !(dv->vec = IM_ARRAY( NULL, nargs, double )) ) { + g_strfreev( strv ); + return( -1 ); + } + dv->n = nargs; + + for( i = 0; i < nargs; i++ ) { + dv->vec[i] = g_ascii_strtod( strv[i], NULL ); + if( errno ) { + im_error_system( errno, "input_doublevec_init", + _( "bad double \"%s\"" ), strv[i] ); + g_strfreev( strv ); + return( -1 ); + } + } + + g_strfreev( strv ); + + return( 0 ); +} + +/* Input double vector type. + */ +im_type_desc im__input_doublevec = { + IM_TYPE_DOUBLEVEC, /* Its an array of double */ + sizeof( im_doublevec_object ), /* Memory to allocate in vec build */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_doublevec_init, /* Init function */ + doublevec_dest /* Destroy function */ +}; + +/* im_intvec_object destroy function. + */ +static int +intvec_dest( im_object obj ) +{ + im_intvec_object *iv = obj; + + if( iv->vec ) { + im_free( iv->vec ); + iv->vec = NULL; + iv->n = 0; + } + + return( 0 ); +} + +/* Init function for intvec input. + */ +static int +input_intvec_init( im_object *obj, char *str ) +{ + im_intvec_object *iv = *obj; + char **strv; + int nargs; + int i; + + strv = g_strsplit( str, VEC_SEPS, -1 ); + nargs = g_strv_length( strv ); + + if( !(iv->vec = IM_ARRAY( NULL, nargs, int )) ) { + g_strfreev( strv ); + return( -1 ); + } + iv->n = nargs; + + for( i = 0; i < nargs; i++ ) { + long int val= strtol( strv[i], NULL, 10 ); + + if( errno ) { + im_error_system( errno, "input_intvec_init", + _( "bad integer \"%s\"" ), strv[i] ); + g_strfreev( strv ); + return( -1 ); + } + if( INT_MAX < val || INT_MIN > val ) { + im_error( "input_intvec_init", + "%ld overflows integer type", val ); + } + iv->vec[i] = (int) val; + } + + g_strfreev( strv ); + + return( 0 ); +} + +/* Input int vector type. + */ +im_type_desc im__input_intvec = { + IM_TYPE_INTVEC, /* It's an array of int */ + sizeof( im_intvec_object ), /* Memory to allocate in vec build */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_intvec_init, /* Init function */ + intvec_dest /* Destroy function */ +}; + +/* Init function for int input. + */ +static int +input_int_init( im_object *obj, char *str ) +{ + int *i = (int *) *obj; + + if( sscanf( str, "%d", i ) != 1 ) { + im_error( "input_int", _( "bad format" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* Input int type. + */ +im_type_desc im__input_int = { + IM_TYPE_INT, /* Its an int */ + sizeof( int ), /* Memory to allocate */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_int_init, /* Init function */ + NULL /* Destroy function */ +}; + +/* Init function for string input. + */ +static int +input_string_init( im_object *obj, char *str ) +{ + if( !(*obj = (im_object) im_strdup( NULL, str )) ) + return( -1 ); + + return( 0 ); +} + +/* Input string type. + */ +im_type_desc im__input_string = { + IM_TYPE_STRING, /* Its a string */ + 0, /* Memory to allocate */ + IM_TYPE_ARG, /* It requires a command-line arg */ + input_string_init, /* Init function */ + im_free /* Destroy function */ +}; + +/* Output string type. + */ +im_type_desc im__output_string = { + IM_TYPE_STRING, /* Its a string */ + 0, /* Memory to allocate */ + IM_TYPE_OUTPUT, /* Its an output argument */ + NULL, /* Init function */ + im_free /* Destroy function */ +}; + +/* Output double type. + */ +im_type_desc im__output_double = { + IM_TYPE_DOUBLE, /* Its a double */ + sizeof( double ), /* Memory to allocate */ + IM_TYPE_OUTPUT, /* Its an output argument */ + NULL, /* Init function */ + NULL /* Destroy function */ +}; + +/* Output complex type. + */ +im_type_desc im__output_complex = { + IM_TYPE_COMPLEX, /* Its a complex */ + 2 * sizeof( double ), /* Memory to allocate */ + IM_TYPE_OUTPUT, /* Its an output argument */ + NULL, /* Init function */ + NULL /* Destroy function */ +}; + +/* Output int type. + */ +im_type_desc im__output_int = { + IM_TYPE_INT, /* Its an int */ + sizeof( int ), /* Memory to allocate */ + IM_TYPE_OUTPUT, /* Its an output argument */ + NULL, /* Init function */ + NULL /* Destroy function */ +}; + +/* Print function for int output. + */ +int +im__iprint( im_object obj ) +{ + int *i = (int *) obj; + + printf( "%d\n", *i ); + + return( 0 ); +} + +/* Print function for string output. + */ +int +im__sprint( im_object obj ) +{ + char *s = (char *) obj; + + printf( "%s\n", s ); + + return( 0 ); +} + +/* Print function for double output. + */ +int +im__dprint( im_object obj ) +{ + double *d = (double *) obj; + + printf( "%G\n", *d ); + + return( 0 ); +} + +/* Print function for complex output. + */ +int +im__cprint( im_object obj ) +{ + double *d = (double *) obj; + + printf( "%G %G\n", d[0], d[1] ); + + return( 0 ); +} + +/* Statistics to stdout. + */ +int +im__dmsprint( im_object obj ) +{ + DOUBLEMASK *mask = ((im_mask_object *) obj)->mask; + double *row; + int i, j; + + /* Print statistics band stats eg: 2 bands:b 0,1 + */ + printf( "\ +band minimum maximum sum sum^2 mean deviation\ +\n" ); + for( j = 0; j < mask->ysize; j++ ) { + row = mask->coeff + j * 6; + if( j == 0 ) + printf( "all" ); + else + printf( "%2d ", j ); + + for( i = 0; i < 6; i++ ) + printf( "%12g", row[i] ); + printf( "\n" ); + } + + return( 0 ); +} + +static char *decode_dtype( enum im_col_disp_type type ) +{ + switch( type ) { + case DISP_BARCO: + return( "DISP_BARCO" ); + case DISP_DUMB: + return( "DISP_DUMB" ); + default: + return( "" ); + } +} + +/* Print display stuff. + */ +int +im__displayprint( im_object obj ) +{ + struct im_col_display *scr = (struct im_col_display *) obj; + + printf( "im_col_display:\n" ); + printf( "\td_name: %s\n", scr->d_name ); + printf( "\td_type: %s\n", decode_dtype( scr->d_type ) ); + printf( "\td_mat:\n" ); + printf( "\t\t %g %g %g\n", + scr->d_mat[0][0], scr->d_mat[0][1], scr->d_mat[0][2] ); + printf( "\t\t %g %g %g\n", + scr->d_mat[1][0], scr->d_mat[1][1], scr->d_mat[1][2] ); + printf( "\t\t %g %g %g\n", + scr->d_mat[2][0], scr->d_mat[2][1], scr->d_mat[2][2] ); + + printf( "\td_YCW: %g\n", scr->d_YCW ); + printf( "\td_xCW: %g\n", scr->d_xCW ); + printf( "\td_yCW: %g\n", scr->d_yCW ); + + printf( "\td_YCR: %g\n", scr->d_YCR ); + printf( "\td_YCG: %g\n", scr->d_YCG ); + printf( "\td_YCB: %g\n", scr->d_YCB ); + + printf( "\td_Vrwr: %d\n", scr->d_Vrwr ); + printf( "\td_Vrwg: %d\n", scr->d_Vrwg ); + printf( "\td_Vrwb: %d\n", scr->d_Vrwb ); + + printf( "\td_Y0R: %g\n", scr->d_Y0R ); + printf( "\td_Y0G: %g\n", scr->d_Y0G ); + printf( "\td_Y0B: %g\n", scr->d_Y0B ); + + printf( "\td_gammaR: %g\n", scr->d_gammaR ); + printf( "\td_gammaG: %g\n", scr->d_gammaG ); + printf( "\td_gammaB: %g\n", scr->d_gammaB ); + + printf( "\td_B: %g\n", scr->d_B ); + printf( "\td_P: %g\n", scr->d_P ); + + return( 0 ); +} + +/* GValue + */ + +/* Init function for input gvalue. Just make a string ... will get cast to + * whatever later. + */ +static int +input_gvalue_init( im_object *obj, char *str ) +{ + GValue *value = *obj; + + g_value_init( value, G_TYPE_STRING ); + g_value_set_string( value, str ); + + return( 0 ); +} + +static int +gvalue_free( im_object obj ) +{ + GValue *value = obj; + + g_value_unset( value ); + + return( 0 ); +} + +/* Input GValue type. + */ +im_type_desc im__input_gvalue = { + IM_TYPE_GVALUE, + sizeof( GValue ), /* Need some storage */ + IM_TYPE_ARG, /* It requires a command-line arg */ + (im_init_obj_fn) input_gvalue_init, /* Init function */ + (im_dest_obj_fn) gvalue_free /* Destroy function */ +}; + +int +im__gprint( im_object obj ) +{ + GValue *value = obj; + char *str_value; + + str_value = g_strdup_value_contents( value ); + printf( "%s\n", str_value ); + g_free( str_value ); + + return( 0 ); +} + +/* Init function for output gvalue. Just init to zero. + */ +static int +output_gvalue_init( im_object *obj ) +{ + GValue *value = *obj; + + memset( value, 0, sizeof( GValue ) ); + + return( 0 ); +} + +im_type_desc im__output_gvalue = { + IM_TYPE_GVALUE, + sizeof( GValue ), /* Need some storage */ + IM_TYPE_OUTPUT, /* No arg needed (just print) */ + (im_init_obj_fn) output_gvalue_init, /* Init function */ + (im_dest_obj_fn) gvalue_free /* Destroy function */ +}; diff --git a/libsrc/iofuncs/error.c b/libsrc/iofuncs/error.c new file mode 100644 index 00000000..7da6b2b4 --- /dev/null +++ b/libsrc/iofuncs/error.c @@ -0,0 +1,248 @@ +/* @(#) error handling + * @(#) + * @(#) Usage: + * @(#) void im_errormsg(variable_list) + * @(#) (variable_list) is (format, arg1, arg2, ...) + * @(#) format, arg1, arg2, etc are the same as in fprintf + * @(#) + * Copyright: N. Dessipris + * Written on: 18/03/1991 + * Updated on: 9/7/92 KM + * 20/12/2003 JC + * - i18n added, domain now separate arg + * 14/2/07 + * - lock around error buffer changes + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef OS_WIN32 +#include +#include +#endif /*OS_WIN32*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Make global array to keep the error message buffer. + */ +#define IM_MAX_ERROR (10240) +static char im_error_text[IM_MAX_ERROR] = ""; +static VBuf im_error_buf = + IM_BUF_STATIC( im_error_text, IM_MAX_ERROR ); + +#define IM_DIAGNOSTICS "IM_DIAGNOSTICS" +#define IM_WARNING "IM_WARNING" + +const char * +im_error_buffer( void ) +{ + const char *msg; + + g_mutex_lock( im__global_lock ); + msg = im_buf_all( &im_error_buf ); + g_mutex_unlock( im__global_lock ); + + return( msg ); +} + +void +im_verror( const char *domain, const char *fmt, va_list ap ) +{ + g_mutex_lock( im__global_lock ); + im_buf_appendf( &im_error_buf, "%s: ", domain ); + im_buf_vappendf( &im_error_buf, fmt, ap ); + im_buf_appends( &im_error_buf, "\n" ); + g_mutex_unlock( im__global_lock ); +} + +void +im_error( const char *domain, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_verror( domain, fmt, ap ); + va_end( ap ); +} + +void +im_verror_system( int err, const char *domain, const char *fmt, va_list ap ) +{ + im_verror( domain, fmt, ap ); + +#ifdef OS_WIN32 +{ + char *buf; + + if( FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + (LPSTR) &buf, 0, NULL ) ) { + im_error( _( "windows error" ), "%s", buf ); + LocalFree( buf ); + } +} +#else /*OS_WIN32*/ +{ + char *buf; + + buf = g_locale_to_utf8( strerror( err ), -1, NULL, NULL, NULL ); + im_error( _( "unix error" ), "%s", buf ); + g_free( buf ); +} +#endif /*OS_WIN32*/ +} + +void +im_error_system( int err, const char *domain, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_verror_system( err, domain, fmt, ap ); + va_end( ap ); +} + +void +im_error_clear( void ) +{ + g_mutex_lock( im__global_lock ); + im_buf_rewind( &im_error_buf ); + g_mutex_unlock( im__global_lock ); +} + +void +im_vdiag( const char *domain, const char *fmt, va_list ap ) +{ + if( !g_getenv( IM_DIAGNOSTICS ) ) { + (void) fprintf( stderr, _( "%s: " ), _( "vips diagnostic" ) ); + (void) fprintf( stderr, _( "%s: " ), domain ); + (void) vfprintf( stderr, fmt, ap ); + (void) fprintf( stderr, "\n" ); + } +} + +void +im_diag( const char *domain, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_vdiag( domain, fmt, ap ); + va_end( ap ); +} + +void +im_vwarn( const char *domain, const char *fmt, va_list ap ) +{ + if( !g_getenv( IM_WARNING ) ) { + (void) fprintf( stderr, _( "%s: " ), _( "vips warning" ) ); + (void) fprintf( stderr, _( "%s: " ), domain ); + (void) vfprintf( stderr, fmt, ap ); + (void) fprintf( stderr, "\n" ); + } +} + +void +im_warn( const char *domain, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_vwarn( domain, fmt, ap ); + va_end( ap ); +} + +/* Compatibility with pre-7.10 ... can't portably do these as macros sadly. + */ + +void +im_errormsg( const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_verror( "untranslated", fmt, ap ); + va_end( ap ); +} + +void +im_verrormsg( const char *fmt, va_list ap ) +{ + im_verror( "untranslated", fmt, ap ); +} + +void +im_errormsg_system( int err, const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_verror_system( err, "untranslated", fmt, ap ); + va_end( ap ); +} + +void +im_diagnostics( const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_vdiag( "untranslated", fmt, ap ); + va_end( ap ); +} + +void +im_warning( const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + im_vwarn( "untranslated", fmt, ap ); + va_end( ap ); +} diff --git a/libsrc/iofuncs/error_exit.c b/libsrc/iofuncs/error_exit.c new file mode 100644 index 00000000..da2a3064 --- /dev/null +++ b/libsrc/iofuncs/error_exit.c @@ -0,0 +1,75 @@ +/* @(#) print error mesg on stderr and exit(1) + * @(#) It also prints any additional error messages set by the routines + * @(#) + * @(#) Usage: + * @(#) void error_exit(variable_arg_list) + * + * Copyright: N. Dessipris + * Written on: 19/03/1991 + * Modified on: + * 11/5/93 J.Cupitt + * - strange extra newlines removed - see im_errormsg() + * - strange tests removed + * 28-4-99 JC + * - ansified + * 2/8/06 + * - print prgname + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void +error_exit( const char *fmt, ... ) +{ + va_list ap; + + fprintf( stderr, "%s: ", g_get_prgname() ); + + va_start( ap, fmt ); + (void) vfprintf( stderr, fmt, ap ); + va_end( ap ); + + fprintf( stderr, "\n" ); + fprintf( stderr, "%s", im_errorstring() ); + + exit( 1 ); +} diff --git a/libsrc/iofuncs/im_binfile.c b/libsrc/iofuncs/im_binfile.c new file mode 100644 index 00000000..e55f7156 --- /dev/null +++ b/libsrc/iofuncs/im_binfile.c @@ -0,0 +1,182 @@ +/* @(#) Function to read a binary file with no header to vasari file + * @(#) Usage: + * @(#) + * @(#) IMAGE * + * @(#) im_binfile(in, xs, ys, bands, offset) + * @(#) char *in; + * @(#) int xs, ys, bands, offset; + * @(#) + * @(#) The function returns NULL on error. + * @(#) Works for uchar input only. + * Author: N. Dessipris + * Written on: 31/7/91 + * Modified on: + * 15/6/93 JC + * - includes fixed + * - externs fixed + * 7/6/94 JC + * - im_outcheck() added + * 13/12/94 JC + * - now uses im_mapfile(), rather than read() and copy + * - hence returns a new IMAGE descriptor + * 25/5/95 JC + * - oops! mess up with fd on errors + * 5/2/04 JC + * - added offset param + * 26/5/04 + * - data init was broken by im_mapfile() change + * 30/9/05 + * - use int64 for size calcs so we can map >31 bit files on 64 bit + * machines + * - delay mmap() for large files, cf. im_openin() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_binfile( const char *name, int xs, int ys, int bands, int offset ) +{ + IMAGE *im; + gint64 length; + gint64 psize; + + /* Check parameters. + */ + if( xs <= 0 || ys <= 0 || bands <=0 ) { + im_error( "im_binfile", _( "bad parameters" ) ); + return( NULL ); + } + + /* Make new output image for us. + */ + if( !(im = im_init( name )) ) + return( NULL ); + if( (im->fd = im__open_image_file( name )) == -1 ) { + im_close( im ); + return( NULL ); + } + im->dtype = IM_OPENIN; + im->sizeof_header = offset; + + /* Predict file size. + */ + psize = (gint64) xs * ys * bands + offset; + + /* Read the file length and check against what we think + * the size should be. + */ + if( (length = im_file_length( im->fd )) == -1 ) { + im_close( im ); + return( NULL ); + } + + /* Very common, so special message. + */ + if( psize > length ) { + im_error( "im_binfile", _( "unable to open %s: " + "file has been truncated" ), im->filename ); + im_close( im ); + return( NULL ); + } + + /* Just wierd. Only print a warning for this, since we should + * still be able to process it without coredumps. + */ + if( psize < length ) + im_warn( "im_binfile", _( "%s is longer than expected" ), + im->filename ); + + /* If the predicted size is under our mmap threshold, mmap the whole + * thing now. Otherwise, delay the map until region create and we'll + * use a rolling window. See also im_openin(). + */ + if( psize < im__mmap_limit ) { + if( im_mapfile( im ) ) { + im_close( im ); + return( NULL ); + } + im->data = im->baseaddr + offset; + im->dtype = IM_MMAPIN; + } + + /* Set header fields. + */ + im->Xsize = xs; + im->Ysize = ys; + im->Bands = bands; + + /* Set others to standard values. + */ + im->Bbits = IM_BBITS_BYTE; + im->BandFmt = IM_BANDFMT_UCHAR; + im->Coding = IM_CODING_NONE; + + if( bands == 1 ) + im->Type = IM_TYPE_B_W; + else if( bands == 3 ) + im->Type = IM_TYPE_RGB; + else + im->Type = IM_TYPE_MULTIBAND; + + im->Xres = 1.0; + im->Yres = 1.0; + + im->Length = 0; + im->Compression = 0; + im->Level = 0; + + im->Xoffset = 0; + im->Yoffset = 0; + + /* Init others too. + */ + im->dhint = IM_THINSTRIP; + + return( im ); +} diff --git a/libsrc/iofuncs/im_bits_of_fmt.c b/libsrc/iofuncs/im_bits_of_fmt.c new file mode 100644 index 00000000..c2abe152 --- /dev/null +++ b/libsrc/iofuncs/im_bits_of_fmt.c @@ -0,0 +1,64 @@ +/* Return number of bits of band format or -1 on error. + * + * 02/06/05 JF + * - original code + * 12/1/06 + * - use a table + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static const int bits[] = { + IM_BBITS_BYTE, + IM_BBITS_BYTE, + IM_BBITS_SHORT, + IM_BBITS_SHORT, + IM_BBITS_INT, + IM_BBITS_INT, + IM_BBITS_FLOAT, + IM_BBITS_COMPLEX, + IM_BBITS_DOUBLE, + IM_BBITS_DPCOMPLEX +}; + +/* Return number of pels bits for band format or -1 on error. + */ +int +im_bits_of_fmt( int bandfmt ) +{ + return( bandfmt < 0 || bandfmt > IM_BANDFMT_DPCOMPLEX ? + im_error( "im_bits_of_fmt", + _( "unsupported band format: %d" ), bandfmt ), + -1 : + bits[bandfmt] ); +} diff --git a/libsrc/iofuncs/im_close.c b/libsrc/iofuncs/im_close.c new file mode 100644 index 00000000..d7353b8a --- /dev/null +++ b/libsrc/iofuncs/im_close.c @@ -0,0 +1,303 @@ +/* @(#) Frees all resources associated with IMAGE, and frees the memory + * @(#) occupied by IMAGE. + * @(#) + * @(#) int + * @(#) im_close( image ) + * @(#) IMAGE *image; + * @(#) + * @(#) As above, but just free resources attached to IMAGE. After call, IMAGE + * @(#) is as if im_init() had just been called. Useful for closing and + * @(#) re-opening as another image type. See im_piocheck() etc. + * @(#) + * @(#) int + * @(#) im__close( image ) + * @(#) IMAGE *image; + * @(#) + * @(#) Returns 0 on success and 1 on error. + * + * Copyright: Nicos Dessipris + * Written on: 12/04/1990 + * Modified on : + * 24/7/92 JC + * - im_update_descfile code tidied up + * - free on NULL string when junking Hist fixed + * - now calls im_unmapfile + * - better behaviour if image has been opened and closed with + * no im_setupout call + * - better behaviour for half-made IMAGE descriptors + * 15/4/93 JC + * - additions for freeing partial images + * 29/4/93 JC + * - close callback list added + * 10/5/93 JC + * - im__close() added + * 9/11/93 JC + * - im_update_descfile -> write_descfile + * - if Hist is NULL, no longer makes up and writes .desc file + * 16/8/94 JC + * - evalend callbacks added + * - ANSIfied + * 24/10/95 JC + * - now tracks open images ... see also im_init() and debug.c + * 11/7/00 JC + * - SETBUF_FOREIGN added + * 16/1/04 JC + * - frees as much as possible on im_close() failure + * 6/6/05 Markus Wollgarten + * - free Meta on close + * 30/6/05 JC + * - actually, free Meta on final close, so we carry meta over on an + * im__close()/im_openin() pair (eg. see im_pincheck()) + * 11/7/05 + * - call im__writehist() to send history to XML after image data + * 3/1/07 + * - free history_list + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO +#define DEBUG_NEW + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef HAVE_IO_H +#include +#endif /*HAVE_IO_H*/ +#include + +#include +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Maximum file name length. + */ +#define NAMELEN 1024 + +/* Free any resources owned by this descriptor. The descriptor is left as if a + * call to im_init had just happened - ie. the filename is set, but no other + * resources are attached. Information is lost if this is a im_setbuf() + * image! On an error, return non-zero and leave the image in an indeterminate + * state. Too hard to recover gracefully. + */ +int +im__close( IMAGE *im ) +{ + int result = 0; + + /* No action for NULL image. + */ + if( !im ) + return( result ); + +#ifdef DEBUG_IO + printf( "im__close: starting for %s ..\n", im->filename ); +#endif /*DEBUG_IO*/ + + /* Free any regions defined on this image. This will, in turn, call + * all stop functions still running, freeing all regions we have on + * other images, etc. + */ +#ifdef DEBUG_IO + printf( "im__close: freeing %d regions ..\n", + g_slist_length( (List *) im->regions ) ); +#endif /*DEBUG_IO*/ + while( im->regions ) + im_region_free( (REGION *) im->regions->data ); + + /* That should mean we have no windows. + */ + if( im->windows ) { + GSList *p; + + printf( "** im__close: leaked windows!\n" ); + for( p = im->windows; p; p = p->next ) + im_window_print( (im_window_t *) p->data ); + } + + /* Make sure all evalend functions have been called, perform all close + * callbacks, and free eval callbacks. + */ + result |= im__trigger_callbacks( im->evalendfns ); + IM_FREEF( im_slist_free_all, im->evalendfns ); + IM_FREEF( im_slist_free_all, im->evalfns ); + result |= im__trigger_callbacks( im->closefns ); + IM_FREEF( im_slist_free_all, im->closefns ); + + /* Junk generate functions. + */ + im->start = NULL; + im->generate = NULL; + im->stop = NULL; + + /* No more parent/child links. + */ + im__link_break_all( im ); + + /* What resources are associated with this IMAGE descriptor? + */ + if( im->baseaddr ) { + /* MMAP file. + */ +#ifdef DEBUG_IO + printf( "im__close: unmapping file ..\n" ); +#endif /*DEBUG_IO*/ + + if( im_unmapfile( im ) ) + return( -1 ); + im->data = NULL; + } + + /* Is there a file descriptor? + */ + if( im->fd != -1 ) { +#ifdef DEBUG_IO + printf( "im__close: closing output file ..\n" ); +#endif /*DEBUG_IO*/ + + if( im->dtype == IM_OPENOUT && im__writehist( im ) ) { + im_errormsg( "im_close: unable to write metadata " + "for %s", im->filename ); + result = -1; + } + if( close( im->fd ) == -1 ) { + im_errormsg( "im_close: unable to close fd (2) " + "for %s", im->filename ); + result = -1; + } + im->fd = -1; + } + + /* Any image data? + */ + if( im->data ) { + /* Buffer image. Only free stuff we know we allocated. + */ + if( im->dtype == IM_SETBUF ) { +#ifdef DEBUG_IO + printf( "im__close: freeing buffer ..\n" ); +#endif /*DEBUG_IO*/ + im_free( im->data ); + im->dtype = IM_NONE; + } + + im->data = NULL; + } + + /* Reset other state. + */ + im->dtype = IM_NONE; + im->dhint = IM_SMALLTILE; + im->kill = 0; + im->close_pending = 0; + im->sizeof_header = IM_SIZEOF_HEADER; + +#ifdef DEBUG_IO + printf( "im__close: final success for %s (%p)\n", + im->filename, im ); +#endif /*DEBUG_IO*/ + + return( result ); +} + +/* Free resources and close descriptor. + */ +int +im_close( IMAGE *im ) +{ + int result = 0; + + /* No action for NULL image. + */ + if( !im ) + return( result ); + + /* Are there any regions left on this image? If there are, just set + * close_pending and return. The image will be then be closed when + * the last region is freed (see im_region_free()). This prevents + * some dangling region pointers. + */ +#ifdef DEBUG_IO + if( im->close_pending ) + /* Strange! Just print a warning. + */ + printf( "im_close: im_close called twice on \"%s\"\n", + im->filename ); +#endif /*DEBUG_IO*/ + if( im->regions ) { +#ifdef DEBUG_IO + printf( "im_close: pending close for \"%s\"\n", im->filename ); +#endif /*DEBUG_IO*/ + im->close_pending = 1; + return( result ); + } + + /* Is this descriptor currently being closed somewhere else? Immediate + * return if it is. This prevents infinite descent if a close callback + * includes an im_close for this image. This can happen sometimes! + */ + if( im->closing ) + return( result ); + im->closing = 1; + + /* Free IMAGE resources. + */ + if( im__close( im ) ) + result = -1; + +#ifdef DEBUG_NEW + printf( "im_close: freeing IMAGE 0x%p, \"%s\"\n", im, im->filename ); +#endif /*DEBUG_NEW*/ + + /* Final cleanup. + */ + IM_FREEF( g_mutex_free, im->sslock ); + IM_FREE( im->filename ); + IM_FREE( im->Hist ); + IM_FREEF( im__gslist_gvalue_free, im->history_list ); + im__meta_destroy( im ); + im__open_images = g_slist_remove( im__open_images, im ); + IM_FREE( im ); + + return( result ); +} diff --git a/libsrc/iofuncs/im_cp_desc.c b/libsrc/iofuncs/im_cp_desc.c new file mode 100644 index 00000000..c18536b9 --- /dev/null +++ b/libsrc/iofuncs/im_cp_desc.c @@ -0,0 +1,152 @@ +/* @(#) Function which copies IMAGE descriptor image2 to image1; + * @(#) data, fd and filename are not copied + * @(#) used to make programs simpler by copying most parameters + * @(#) + * @(#) int + * @(#) im_cp_desc( image1, image2 ) + * @(#) IMAGE *image1, *image2; + * @(#) + * @(#) Returns 0 on success or -1 on fail. + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 09/02/1990 + * Modified on : 22/2/93 By Kirk Martinez: v6.3 + * 28/10/1992 J.Cupitt + * - now calls im_cp_Hist, and hence frees old history correctly + * 10/5/93 J.Cupitt + * - checks return result from im_cp_Hist() + * 22/11/00 JC + * - ANSIfied + * 5/9/02 JC + * - copy Xoffset/Yoffset too + * 14/4/04 JC + * - hmm, in fact no, zero them + * 6/6/05 Markus Wollgarten + * - copy Meta + * 29/8/05 + * - added im_cp_descv() and im_cp_desc_array() + * 2/9/05 + * - simplified ... no more skip the first line stuff + * 4/1/07 + * - merge hists with history_list instead + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* in is a NULL-termnated array of input images. Always at least one image + * there. + */ +int +im_cp_desc_array( IMAGE *out, IMAGE *in[] ) +{ + int i; + int ni; + + assert( in[0] ); + + out->Xsize = in[0]->Xsize; + out->Ysize = in[0]->Ysize; + out->Bands = in[0]->Bands; + out->Bbits = in[0]->Bbits; + out->BandFmt = in[0]->BandFmt; + out->Type = in[0]->Type; + out->Coding = in[0]->Coding; + out->Xres = in[0]->Xres; + out->Yres = in[0]->Yres; + out->Xoffset = 0; + out->Yoffset = 0; + + /* Count number of images. + */ + for( ni = 0; in[ni]; ni++ ) + ; + + /* Need to copy last-to-first so that in0 meta will override any + * earlier meta. + */ + im__meta_destroy( out ); + for( i = ni - 1; i >= 0; i-- ) + if( im_image_sanity( in[i] ) || + im__meta_cp( out, in[i] ) ) + return( -1 ); + + /* Merge hists first to last. + */ + for( i = 0; in[i]; i++ ) + out->history_list = im__gslist_gvalue_merge( out->history_list, + in[i]->history_list ); + + return( 0 ); +} + +/* Max number of images we can handle. + */ +#define MAX_IMAGES (1000) + +int +im_cp_descv( IMAGE *out, IMAGE *in1, ... ) +{ + va_list ap; + int i; + IMAGE *in[MAX_IMAGES]; + + in[0] = in1; + va_start( ap, in1 ); + for( i = 1; i < MAX_IMAGES && (in[i] = va_arg( ap, IMAGE * )); i++ ) + ; + va_end( ap ); + if( i == MAX_IMAGES ) { + im_error( "im_cp_descv", _( "too many images" ) ); + return( -1 ); + } + + return( im_cp_desc_array( out, in ) ); +} + +int +im_cp_desc( IMAGE *dest, IMAGE *src ) +{ + return( im_cp_descv( dest, src, NULL ) ); +} diff --git a/libsrc/iofuncs/im_debugim.c b/libsrc/iofuncs/im_debugim.c new file mode 100644 index 00000000..59d1c6b3 --- /dev/null +++ b/libsrc/iofuncs/im_debugim.c @@ -0,0 +1,138 @@ +/* @(#) Function which prints in stdout the values of a picture + * @(#) + * @(#) For debuging only + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) void + * @(#) im_debugim( in ) + * @(#) IMAGE *in; + * @(#) + * + * Copyright: 1991 N. Dessipris + * + * Author: N. Dessipris + * Written on: 18/03/1991 + * Modified on: + * 15/4/93 J.Cupitt + * - returns int, not void now, so error messages work + * - detects im->data invalid. + * 15/4/93 J.Cupitt + * - uses %g format, not %f for printf() + * 23/7/93 JC + * - im_incheck() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_debugim( IMAGE *in ) +{ +/* Check our args. */ + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_debugim: input must be uncoded" ); + return( -1 ); + } + +/* What type? First define the loop we want to perform for all types. */ +#define loopuc(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + for ( x=0; xXsize; x++ ) {\ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr, "%4d", (TYPE)*p++ );\ + } \ + } \ + fprintf(stderr, "\n");\ + } \ + } + +#define loop(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + for ( x=0; xXsize; x++ ) {\ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr, "%g\t", (double)*p++ );\ + } \ + } \ + fprintf(stderr, "\n");\ + } \ + } + +#define loopcmplx(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + for ( x=0; xXsize; x++ ) {\ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr,"re=%g\t",(double)*p++);\ + fprintf(stderr,"im=%g\t",(double)*p++);\ + } \ + } \ + fprintf(stderr, "\n");\ + } \ + } + +/* Now generate code for all types. */ + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: loopuc(unsigned char); break; + case IM_BANDFMT_CHAR: loop(char); break; + case IM_BANDFMT_USHORT: loop(unsigned short); break; + case IM_BANDFMT_SHORT: loop(short); break; + case IM_BANDFMT_UINT: loop(unsigned int); break; + case IM_BANDFMT_INT: loop(int); break; + case IM_BANDFMT_FLOAT: loop(float); break; + case IM_BANDFMT_DOUBLE: loop(double); break; + case IM_BANDFMT_COMPLEX: loopcmplx(float); break; + case IM_BANDFMT_DPCOMPLEX: loopcmplx(double); break; + + default: + im_errormsg("im_debugim: unknown input format"); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_demand_hint.c b/libsrc/iofuncs/im_demand_hint.c new file mode 100644 index 00000000..628e4fd2 --- /dev/null +++ b/libsrc/iofuncs/im_demand_hint.c @@ -0,0 +1,188 @@ +/* @(#) Hint to the evaluation mechanism that it should ask for output from + * @(#) this image with a certain shape of patch. + * @(#) + * @(#) int + * @(#) im_demand_hint( im, hint, in1, in2, ..., NULL ) + * @(#) IMAGE *im, *in1, *in2, ...; + * @(#) im_demand_type hint; + * @(#) + * @(#) hint may be one of + * @(#) + * @(#) IM_THINSTRIP + * @(#) This operation would like to output strips the width of the + * @(#) image and a few pels high. This is option suitable for + * @(#) point-to-point operations, such as those in the arithmetic + * @(#) package. + * @(#) + * @(#) This is the fastest style for most simple operations. + * @(#) + * @(#) IM_FATSTRIP + * @(#) This operation would like to output strips the width of the + * @(#) image and as high as possible. This option is suitable for + * @(#) area operations which do not violently transform coordinates, + * @(#) such as im_conv(). + * @(#) + * @(#) IM_SMALLTILE + * @(#) This is the most general demand format, and is the default. + * @(#) Output is demanded in small (around 100x100 pel) sections. + * @(#) This style works reasonably efficiently, even for bizzare + * @(#) operations like 45 degree rotate. + * @(#) + * @(#) IM_ANY + * @(#) Not from a disc file, so any geometry is OK. + * @(#) + * @(#) NOTE: demand style falls back to the most restrictive in the pipeline. + * @(#) All pipeline elements in the pipeline must agree on IM_THINSTRIP + * @(#) before output will be asked for in this manner. If you do not set a + * @(#) hint, you will get IM_SMALLTILE. + * @(#) + * @(#) in1, in2, ... are the images on which out will make demands. You + * @(#) should terminate the list with NULL. + * @(#) + * @(#) int + * @(#) im_demand_hint_array( im, hint, in ) + * @(#) IMAGE *im, **in; + * @(#) im_demand_type hint; + * @(#) + * @(#) As above, but in is a NULL-terminated array of input images. Use + * @(#) im_allocate_input_array() to build the input array. + * @(#) + * @(#) Returns non-zero on failure. + * @(#) + * + * Copyright: The National Gallery, 1993 + * Written on: 6/9/93 + * Modified on : + * 2/3/98 JC + * - IM_ANY added + * 19/5/06 + * - minor change to rules: don't force ANY on no-input operations ... + * fails for image import + * 1/12/06 + * - build parent/child links as well + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Max number of images we can handle. + */ +#define MAX_IMAGES (1000) + +/* Given two im_demand_type, return the most restrictive. + */ +static im_demand_type +find_least( im_demand_type a, im_demand_type b ) +{ + return( (im_demand_type) IM_MIN( (int) a, (int) b ) ); +} + +/* Set hint for this image. + */ +int +im_demand_hint_array( IMAGE *im, im_demand_type hint, IMAGE **in ) +{ + int i, len, nany; + + /* How many input images are there? And how many are IM_ANY? + */ + for( i = 0, len = 0, nany = 0; in[i]; i++, len++ ) + if( in[i]->dhint == IM_ANY ) + nany++; + + if( len == 0 ) + /* No input images? Just set the requested hint. We don't + * force ANY, since the operation might be something like + * tiled read of an EXR image, where we certainly don't want + * ANY. + */ + ; + else if( nany == len ) + /* Special case: if all the inputs are IM_ANY, then output can + * be IM_ANY regardless of what this function wants. + */ + hint = IM_ANY; + else + /* Find the most restrictive of all the hints available to us. + */ + for( i = 0; i < len; i++ ) + hint = find_least( hint, in[i]->dhint ); + + im->dhint = hint; + +#ifdef DEBUG + printf( "im_demand_hint_array: set dhint for \"%s\" to %s\n", + im->filename, im_dhint2char( im->dhint ) ); +#endif /*DEBUG*/ + + /* im depends on all these ims. + */ + for( i = 0; i < len; i++ ) + im__link_make( im, in[i] ); + + return( 0 ); +} + +/* Build an array, and call the above. + */ +int +im_demand_hint( IMAGE *im, im_demand_type hint, ... ) +{ + va_list ap; + int i; + IMAGE *ar[MAX_IMAGES]; + + va_start( ap, hint ); + for( i = 0; i < MAX_IMAGES && (ar[i] = va_arg( ap, IMAGE * )); i++ ) + ; + va_end( ap ); + if( i == MAX_IMAGES ) { + im_error( "im_demand_hint", _( "too many images" ) ); + return( -1 ); + } + + return( im_demand_hint_array( im, hint, ar ) ); +} diff --git a/libsrc/iofuncs/im_desc_hd.c b/libsrc/iofuncs/im_desc_hd.c new file mode 100644 index 00000000..4db8046f --- /dev/null +++ b/libsrc/iofuncs/im_desc_hd.c @@ -0,0 +1,204 @@ +/* @(#) im_desc_hd: Copies vips file header to an IMAGE descriptor. + * @(#) + * @(#) int + * @(#) im__read_header_bytes( IMAGE *im, unsigned char *from ) + * @(#) + * @(#) int + * @(#) im__write_header_bytes( IMAGE *im, unsigned char *to ) + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 22/2/92 v6.3 Kirk Martinez + * 17/11/94 JC + * - read compression fields too + * 28/10/98 JC + * - byteswap stuff added + * 21/8/02 JC + * - oops, was truncating float + * 22/8/05 + * - slightly less stupid + * 30/12/06 + * - use gunit32/16 for 2 and 4 byte quantities + * 12/1/07 + * - override bbits in the file ... it's now deprecated + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Read short/int/float LSB and MSB first. + */ +void +im__read_4byte( int msb_first, unsigned char *to, unsigned char **from ) +{ + unsigned char *p = *from; + int out; + + if( msb_first ) + out = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + else + out = p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; + + *from += 4; + *((guint32 *) to) = out; +} + +void +im__read_2byte( int msb_first, unsigned char *to, unsigned char **from ) +{ + int out; + unsigned char *p = *from; + + if( msb_first ) + out = p[0] << 8 | p[1]; + else + out = p[1] << 8 | p[0]; + + *from += 2; + *((guint16 *) to) = out; +} + +/* We always write in native byte order. + */ +void +im__write_4byte( unsigned char **to, unsigned char *from ) +{ + *((guint32 *) *to) = *((guint32 *) from); + *to += 4; +} + +void +im__write_2byte( unsigned char **to, unsigned char *from ) +{ + *((guint16 *) *to) = *((guint16 *) from); + *to += 2; +} + +/* offset, read, write functions. + */ +typedef struct _FieldIO { + glong offset; + void (*read)( int msb_first, unsigned char *to, unsigned char **from ); + void (*write)( unsigned char **to, unsigned char *from ); +} FieldIO; + +static FieldIO fields[] = { + { G_STRUCT_OFFSET( IMAGE, Xsize ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Ysize ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Bands ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Bbits ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, BandFmt ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Coding ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Type ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Xres ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Yres ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Length ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Compression ), + im__read_2byte, im__write_2byte }, + { G_STRUCT_OFFSET( IMAGE, Level ), + im__read_2byte, im__write_2byte }, + { G_STRUCT_OFFSET( IMAGE, Xoffset ), + im__read_4byte, im__write_4byte }, + { G_STRUCT_OFFSET( IMAGE, Yoffset ), + im__read_4byte, im__write_4byte } +}; + +int +im__read_header_bytes( IMAGE *im, unsigned char *from ) +{ + int msb_first; + int i; + + im__read_4byte( 1, (unsigned char *) &im->magic, &from ); + if( im->magic != IM_MAGIC_INTEL && im->magic != IM_MAGIC_SPARC ) { + im_error( "im_open", _( "\"%s\" is not a VIPS image" ), + im->filename ); + return( -1 ); + } + msb_first = im->magic == IM_MAGIC_SPARC; + + for( i = 0; i < IM_NUMBER( fields ); i++ ) + fields[i].read( msb_first, + &G_STRUCT_MEMBER( unsigned char, im, fields[i].offset ), + &from ); + + /* Set this ourselves ... bbits is deprecated in the file format. + */ + im->Bbits = im_bits_of_fmt( im->BandFmt ); + + return( 0 ); +} + +int +im__write_header_bytes( IMAGE *im, unsigned char *to ) +{ + guint32 magic; + int i; + unsigned char *q; + + /* How odd, you'd think it would be the other way around. + */ + magic = im_amiMSBfirst() ? IM_MAGIC_INTEL : IM_MAGIC_SPARC; + q = to; + im__write_4byte( &q, (unsigned char *) &magic ); + + for( i = 0; i < IM_NUMBER( fields ); i++ ) + fields[i].write( &q, + &G_STRUCT_MEMBER( unsigned char, im, + fields[i].offset ) ); + + /* Pad spares with zeros. + */ + while( q - to < im->sizeof_header ) + *q++ = 0; + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_generate.c b/libsrc/iofuncs/im_generate.c new file mode 100644 index 00000000..ff616a0a --- /dev/null +++ b/libsrc/iofuncs/im_generate.c @@ -0,0 +1,850 @@ +/* Manage pipelines of partial images. + * + * J.Cupitt, 17/4/93. + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 6/7/93 JC + * - im_setupout() conventions clarified - see autorewind in + * im_iocheck(). + * 20/7/93 JC + * - eval callbacks added + * 7/9/93 JC + * - demand hint mechanism added + * 25/10/93 + * - asynchronous output mechanisms removed, as no observable speed-up + * 9/5/94 + * - new thread stuff added, with a define to turn it off + * 15/8/94 + * - start & stop functions can now be NULL for no-op + * 7/10/94 JC + * - evalend callback system added + * 23/12/94 JC + * - IM_ARRAY uses added + * 22/2/95 JC + * - im_fill_copy() added + * - im_region_region() uses modified + * 24/4/95 JC & KM + * - im_fill_lines() bug removed + * 30/8/96 JC + * - revised and simplified ... some code shared with im_iterate() + * - new im_generate_region() added + * 2/3/98 JC + * - IM_ANY added + * 20/7/99 JC + * - tile geometry made into ints for easy tuning + * 30/7/99 RP JC + * - threads reorganised for POSIX + * 29/9/99 JC + * - threadgroup stuff added + * 15/4/04 + * - better how-many-pixels-calculated + * 27/11/06 + * - merge background write stuff + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Start and stop functions for one image in, input image is first user data. + */ +void * +im_start_one( IMAGE *out, IMAGE *in, void *dummy ) +{ + return( im_region_create( in ) ); +} + +int +im_stop_one( REGION *reg, void *dummy1, void *dummy2 ) +{ + im_region_free( reg ); + + return( 0 ); +} + +/* Stop and start functions for many images in. First client is pointer to + * null-terminated array of input images. + */ +int +im_stop_many( REGION **ar, void *dummy1, void *dummy2 ) +{ + int i; + + if( ! ar ) + return 0; + + for( i = 0; ar[i]; i++ ) + im_region_free( ar[i] ); + im_free( (char *) ar ); + + return( 0 ); +} + +void * +im_start_many( IMAGE *out, IMAGE **in, void *dummy ) +{ + int i, n; + REGION **ar; + + /* How many images? + */ + for( n = 0; in[n]; n++ ) + ; + + /* Alocate space for region array. + */ + if( !(ar = IM_ARRAY( NULL, n + 1, REGION * )) ) + return( NULL ); + + /* Create a set of regions. + */ + for( i = 0; i < n; i++ ) + if( !(ar[i] = im_region_create( in[i] )) ) { + im_stop_many( ar, NULL, NULL ); + return( NULL ); + } + ar[n] = NULL; + + return( ar ); +} + +/* Convenience function - make a null-terminated array of input images. + * Use with im_start_many. + */ +IMAGE ** +im_allocate_input_array( IMAGE *out, ... ) +{ + va_list ap; + IMAGE **ar; + IMAGE *im; + int i, n; + + /* Count input images. + */ + va_start( ap, out ); + for( n = 0; (im = va_arg( ap, IMAGE * )); n++ ) + ; + va_end( ap ); + + /* Allocate array. + */ + if( !(ar = IM_ARRAY( out, n + 1, IMAGE * )) ) + return( NULL ); + + /* Fill array. + */ + va_start( ap, out ); + for( i = 0; i < n; i++ ) + ar[i] = va_arg( ap, IMAGE * ); + va_end( ap ); + ar[n] = NULL; + + return( ar ); +} + +/* Loop over a big region, filling it in many small pieces with threads. + */ +static int +eval_to_region( REGION *or, im_threadgroup_t *tg ) +{ + Rect *r = &or->valid; + Rect image; + + int x, y; + + image.left = 0; + image.top = 0; + image.width = or->im->Xsize; + image.height = or->im->Ysize; + + /* Note we'll be working to fill a contigious area. + */ + tg->inplace = 1; + + /* Loop over or, attaching to all sub-parts in turn. + */ + for( y = r->top; y < IM_RECT_BOTTOM( r ); y += tg->ph ) + for( x = r->left; x < IM_RECT_RIGHT( r ); x += tg->pw ) { + im_thread_t *thr; + Rect pos; + Rect clipped; + + /* thrs appear on idle when the child thread does + * threadgroup_idle_add and hits the 'go' semaphore. + */ + thr = im_threadgroup_get( tg ); + + /* Set the position we want to generate with this + * thread. Clip against the size of the image and the + * space available in or. + */ + pos.left = x; + pos.top = y; + pos.width = tg->pw; + pos.height = tg->ph; + im_rect_intersectrect( &pos, &image, &clipped ); + im_rect_intersectrect( &clipped, r, &clipped ); + + /* Note params and start work. + */ + thr->oreg = or; + thr->pos = clipped; + thr->x = clipped.left; + thr->y = clipped.top; + im_threadgroup_trigger( thr ); + + /* Trigger any eval callbacks on our source image. + */ + im__handle_eval( tg->im, tg->pw, tg->ph ); + + /* Check for errors. + */ + if( im_threadgroup_iserror( tg ) ) { + /* Don't kill threads yet ... we may want to + * get some error stuff out of them. + */ + im_threadgroup_wait( tg ); + return( -1 ); + } + } + + /* Wait for all threads to hit 'go' again. + */ + im_threadgroup_wait( tg ); + + if( im_threadgroup_iserror( tg ) ) + return( -1 ); + + return( 0 ); +} + +/* Output to a memory area. Might be im_setbuf(), im_mmapin()/im_makerw() or + * im_mmapinrw(). + */ +static int +eval_to_memory( im_threadgroup_t *tg, REGION *or ) +{ + int y, chunk; + IMAGE *im = or->im; + +#ifdef DEBUG_IO + int ntiles = 0; + printf( "eval_to_memory: partial image output to memory area\n" ); +#endif /*DEBUG_IO*/ + + /* Choose a chunk size ... 1/100th of the height of the image, about. + * This sets the granularity of user feedback on eval progress, but + * does not affect mem requirements etc. + */ + chunk = (im->Ysize / 100) + 1; + + /* Loop down the output image, evaling each chunk. + */ + for( y = 0; y < im->Ysize; y += chunk ) { + Rect pos; + + /* Attach or to this position in image. + */ + pos.left = 0; + pos.top = y; + pos.width = im->Xsize; + pos.height = chunk; + if( im_region_image( or, &pos ) ) + return( -1 ); + + /* Ask for evaluation of this area. + */ + if( eval_to_region( or, tg ) ) + return( -1 ); + +#ifdef DEBUG_IO + ntiles++; +#endif /*DEBUG_IO*/ + } + +#ifdef DEBUG_IO + printf( "eval_to_memory: success! %d patches written\n", ntiles ); +#endif /*DEBUG_IO*/ + + return( 0 ); +} + +/* A buffer we are going to write to disc in a background thread. + */ +typedef struct _WriteBuffer { + im_threadgroup_t *tg; /* What makes the pixels */ + REGION *region; /* Pixels */ + Rect area; /* Part of image this region covers */ + im_semaphore_t go; /* Start bg thread loop */ + im_semaphore_t nwrite; /* Number of threads writing to region */ + im_semaphore_t done; /* Bg thread has done write */ + int write_errno; /* Save write errors here */ + GThread *thread; /* BG writer thread */ + gboolean kill; /* Set to ask thread to exit */ +} WriteBuffer; + +static void +wbuffer_free( WriteBuffer *wbuffer ) +{ + /* Is there a thread running this region? Kill it! + */ + if( wbuffer->thread ) { + wbuffer->kill = TRUE; + im_semaphore_up( &wbuffer->go ); + + /* Return value is always NULL (see wbuffer_write_thread). + */ + (void) g_thread_join( wbuffer->thread ); +#ifdef DEBUG_CREATE + printf( "wbuffer_free: g_thread_join()\n" ); +#endif /*DEBUG_CREATE*/ + + wbuffer->thread = NULL; + } + + IM_FREEF( im_region_free, wbuffer->region ); + im_semaphore_destroy( &wbuffer->go ); + im_semaphore_destroy( &wbuffer->nwrite ); + im_semaphore_destroy( &wbuffer->done ); + im_free( wbuffer ); +} + +static void +wbuffer_write( WriteBuffer *wbuffer ) +{ + im_threadgroup_t *tg = wbuffer->tg; + IMAGE *im = tg->im; + REGION *region = wbuffer->region; + Rect *area = &wbuffer->area; + size_t nwritten, count; + void *buf; + + count = region->bpl * area->height; + buf = IM_REGION_ADDR( region, 0, area->top ); + do { + nwritten = write( im->fd, buf, count ); + + /* Write failed? Note in wbuffer errno for the main + * thread to pick up. + */ + if( nwritten == (size_t) -1 ) { + wbuffer->write_errno = errno; + break; + } + + buf = (void *) ((char *) buf + nwritten); + count -= nwritten; + } while( count > 0 ); + +#ifdef DEBUG_IO + printf( "wbuffer_write: %d bytes from wbuffer %p\n", + region->bpl * area->height, wbuffer ); +#endif /*DEBUG_IO*/ +} + +#ifdef HAVE_THREADS +/* Run this as a thread to do a BG write. + */ +static void * +wbuffer_write_thread( void *data ) +{ + WriteBuffer *wbuffer = (WriteBuffer *) data; + + for(;;) { + im_semaphore_down( &wbuffer->go ); + + if( wbuffer->kill ) + break; + + /* Wait for all writer threads to leave this wbuffer. + */ + im_semaphore_downn( &wbuffer->nwrite, 0 ); + + wbuffer_write( wbuffer ); + + /* Signal write complete. + */ + im_semaphore_up( &wbuffer->done ); + } + + return( NULL ); +} +#endif /*HAVE_THREADS*/ + +static WriteBuffer * +wbuffer_new( im_threadgroup_t *tg ) +{ + WriteBuffer *wbuffer; + + if( !(wbuffer = IM_NEW( NULL, WriteBuffer )) ) + return( NULL ); + wbuffer->tg = tg; + wbuffer->region = NULL; + im_semaphore_init( &wbuffer->go, 0, "go" ); + im_semaphore_init( &wbuffer->nwrite, 0, "nwrite" ); + im_semaphore_init( &wbuffer->done, 0, "done" ); + wbuffer->write_errno = 0; + wbuffer->thread = NULL; + wbuffer->kill = FALSE; + + if( !(wbuffer->region = im_region_create( tg->im )) ) { + wbuffer_free( wbuffer ); + return( NULL ); + } + +#ifdef HAVE_THREADS + /* Make this last (picks up parts of wbuffer on startup). + */ + if( !(wbuffer->thread = g_thread_create( wbuffer_write_thread, wbuffer, + TRUE, NULL )) ) { + im_error( "wbuffer_new", _( "unable to create thread" ) ); + wbuffer_free( wbuffer ); + return( NULL ); + } +#endif /*HAVE_THREADS*/ + + return( wbuffer ); +} + +/* At end of work_fn ... need to tell wbuffer write thread that we're done. + */ +static int +wbuffer_work_fn( REGION *region, WriteBuffer *wbuffer ) +{ + im_semaphore_upn( &wbuffer->nwrite, 1 ); + + return( 0 ); +} + +/* Attach a wbuffer to a position. + */ +static int +wbuffer_position( WriteBuffer *wbuffer, + int left, int top, int width, int height ) +{ + Rect image, area; + + image.left = 0; + image.top = 0; + image.width = wbuffer->tg->im->Xsize; + image.height = wbuffer->tg->im->Ysize; + + area.left = left; + area.top = top; + area.width = width; + area.height = height; + + im_rect_intersectrect( &area, &image, &wbuffer->area ); + if( im_region_buffer( wbuffer->region, &wbuffer->area ) ) + return( -1 ); + + /* This should be an exclusive buffer, hopefully. + */ + assert( !wbuffer->region->buffer->done ); + + return( 0 ); +} + +/* Loop over a wbuffer filling it threadily. + */ +static int +wbuffer_fill( WriteBuffer *wbuffer ) +{ + Rect *area = &wbuffer->area; + im_threadgroup_t *tg = wbuffer->tg; + IMAGE *im = tg->im; + Rect image; + + int x, y; + +#ifdef DEBUG_IO + printf( "wbuffer_fill: starting for wbuffer %p at line %d\n", + wbuffer, area->top ); +#endif /*DEBUG_IO*/ + + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + + /* Loop over area, sparking threads for all sub-parts in turn. + */ + for( y = area->top; y < IM_RECT_BOTTOM( area ); y += tg->ph ) + for( x = area->left; x < IM_RECT_RIGHT( area ); x += tg->pw ) { + im_thread_t *thr; + Rect pos; + Rect clipped; + + /* thrs appear on idle when the child thread does + * threadgroup_idle_add and hits the 'go' semaphore. + */ + thr = im_threadgroup_get( tg ); + + /* Set the position we want to generate with this + * thread. Clip against the size of the image and the + * space available in or. + */ + pos.left = x; + pos.top = y; + pos.width = tg->pw; + pos.height = tg->ph; + im_rect_intersectrect( &pos, &image, &clipped ); + im_rect_intersectrect( &clipped, area, &clipped ); + + /* Note params. + */ + thr->oreg = wbuffer->region; + thr->pos = clipped; + thr->x = clipped.left; + thr->y = clipped.top; + thr->a = wbuffer; + +#ifdef DEBUG_IO + printf( "wbuffer_fill: starting for tile at %d x %d\n", + x, y ); +#endif /*DEBUG_IO*/ + + /* Add writer to n of writers on wbuffer, set it going. + */ + im_semaphore_upn( &wbuffer->nwrite, -1 ); + im_threadgroup_trigger( thr ); + + /* Trigger any eval callbacks on our source image. + */ + im__handle_eval( tg->im, tg->pw, tg->ph ); + + /* Check for errors. + */ + if( im_threadgroup_iserror( tg ) ) { + /* Don't kill threads yet ... we may want to + * get some error stuff out of them. + */ + im_threadgroup_wait( tg ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Eval to file. + */ +static int +wbuffer_eval_to_file( WriteBuffer *b1, WriteBuffer *b2 ) +{ + im_threadgroup_t *tg = b1->tg; + IMAGE *im = tg->im; + int y; + + assert( b1->tg == b2->tg ); + +#ifdef DEBUG_IO + int nstrips; + + nstrips = 0; + printf( "wbuffer_eval_to_file: partial image output to file\n" ); +#endif /*DEBUG_IO*/ + + /* Note we'll be working to fill a contigious area. + */ + tg->inplace = 1; + + /* What threads do at the end of each tile ... decrement the nwrite + * semaphore. + */ + tg->work = (im__work_fn) wbuffer_work_fn; + + /* Fill to in steps, write each to the output. + */ + for( y = 0; y < im->Ysize; y += tg->nlines ) { + /* Attach to this position in image. + */ + if( wbuffer_position( b1, 0, y, im->Xsize, tg->nlines ) ) + return( -1 ); + + /* Spark off threads to fill with data. + */ + if( wbuffer_fill( b1 ) ) + return( -1 ); + + /* We have to keep the ordering on wbuffer writes, so we can't + * have more than one background write going at once. Plus we + * want to make sure write()s don't get interleaved. Wait for + * the previous BG write (if any) to finish. + */ + if( y > 0 ) { + im_semaphore_down( &b2->done ); + + /* Previous write suceeded? + */ + if( b2->write_errno ) { + im_error_system( b2->write_errno, + "im__eval_to_file", + _( "write failed" ) ); + return( -1 ); + } + } + + /* b1 write can go. + */ + im_semaphore_up( &b1->go ); + +#ifndef HAVE_THREADS + /* No threading ... just write. + */ + wbuffer_write( b1 ); +#endif /*HAVE_THREADS*/ + + /* Rotate wbuffers. + */ + { + WriteBuffer *t; + + t = b1; b1 = b2; b2 = t; + } + +#ifdef DEBUG_IO + nstrips++; +#endif /*DEBUG_IO*/ + } + + /* Wait for all threads to finish, check for any errors. + */ + im_threadgroup_wait( tg ); + im_semaphore_down( &b2->done ); + if( im_threadgroup_iserror( tg ) ) + return( -1 ); + if( b1->write_errno || b2->write_errno ) { + im_error_system( + b1->write_errno ? b1->write_errno : b2->write_errno, + "im__eval_to_file", _( "write failed" ) ); + return( -1 ); + } + +#ifdef DEBUG_IO + printf( "wbuffer_eval_to_file: success! %d strips written\n", nstrips ); +#endif /*DEBUG_IO*/ + + return( 0 ); +} + +static int +eval_to_file( im_threadgroup_t *tg ) +{ + WriteBuffer *b1, *b2; + + b1 = wbuffer_new( tg ); + b2 = wbuffer_new( tg ); + + if( !b1 || !b2 || wbuffer_eval_to_file( b1, b2 ) ) { + IM_FREEF( wbuffer_free, b1 ); + IM_FREEF( wbuffer_free, b2 ); + + return( -1 ); + } + + wbuffer_free( b1 ); + wbuffer_free( b2 ); + + return( 0 ); +} + +/* Attach a generate function to an image. + */ +int +im_generate( IMAGE *im, + void *(*start_fn)(), int (*gen_fn)(), int (*stop_fn)(), + void *a, void *b ) +{ + int res; + REGION *or; + im_threadgroup_t *tg; + + if( im_image_sanity( im ) ) + return( -1 ); + if( im->Xsize <= 0 || im->Ysize <= 0 || im->Bands <= 0 ) { + im_error( "im_generate", _( "bad dimensions" ) ); + return( -1 ); + } + + /* Look at output type to decide our action. + */ + switch( im->dtype ) { + case IM_PARTIAL: + /* Output to partial image. Just attach functions and return. + */ + if( im->generate || im->start || im->stop ) { + im_error( "im_generate", _( "func already attached" ) ); + return( -1 ); + } + + im->start = start_fn; + im->generate = gen_fn; + im->stop = stop_fn; + im->client1 = a; + im->client2 = b; + +#ifdef DEBUG_IO + printf( "im_generate: attaching partial callbacks\n" ); +#endif /*DEBUG_IO*/ + + break; + + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPINRW: + case IM_OPENOUT: + /* Eval now .. sanity check. + */ + if( im->generate || im->start || im->stop ) { + im_error( "im_generate", _( "func already attached" ) ); + return( -1 ); + } + + /* Get output ready. + */ + if( im_setupout( im ) ) + return( -1 ); + + /* Attach callbacks. + */ + im->start = start_fn; + im->generate = gen_fn; + im->stop = stop_fn; + im->client1 = a; + im->client2 = b; + + /* Evaluate. Two output styles: to memory area (im_setbuf() + * or im_mmapinrw()) or to file (im_openout()). + */ + if( !(or = im_region_create( im )) ) + return( -1 ); + if( !(tg = im_threadgroup_create( im )) ) { + im_region_free( or ); + return( -1 ); + } + if( im->dtype == IM_OPENOUT ) + res = eval_to_file( tg ); + else + res = eval_to_memory( tg, or ); + + /* Clean up. + */ + im_threadgroup_free( tg ); + im_region_free( or ); + + /* Evaluation is now complete, with all sequences finished. + * Trigger evalend callbacks, then free them to make sure we + * don't trigger twice. + */ + res |= im__trigger_callbacks( im->evalendfns ); + IM_FREEF( im_slist_free_all, im->evalendfns ); + + /* Error? + */ + if( res ) + return( -1 ); + + break; + + default: + /* Not a known output style. + */ + im_error( "im_generate", _( "unable to output to a %s image" ), + im_dtype2char( im->dtype ) ); + return( -1 ); + } + + return( 0 ); +} + +/* Generate a region of pixels ... with threads! Very like im_prepare(), but + * threaded and does sub-division. + */ +int +im_prepare_thread( im_threadgroup_t *tg, REGION *or, Rect *r ) +{ + IMAGE *im = or->im; + + if( im_image_sanity( im ) ) + return( -1 ); + + switch( im->dtype ) { + case IM_PARTIAL: + if( im_region_fill( or, r, + (im_region_fill_fn) eval_to_region, tg ) ) + return( -1 ); + + break; + + case IM_OPENIN: + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPIN: + case IM_MMAPINRW: + /* Attach to existing buffer. + */ + if( im_region_image( or, r ) ) + return( -1 ); + + break; + + default: + im_error( "im_prepare_thread", _( "unable to input from a %s " + "image" ), im_dtype2char( im->dtype ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_guess_prefix.c b/libsrc/iofuncs/im_guess_prefix.c new file mode 100644 index 00000000..0281e6c5 --- /dev/null +++ b/libsrc/iofuncs/im_guess_prefix.c @@ -0,0 +1,398 @@ +/* @(#) Guess a value for install prefix. Pass in argv[0] (or NULL) as a clue, + * @(#) plus the name of the controlling environment variable (eg. VIPSHOME). + * @(#) + * @(#) const char * + * @(#) im_guess_prefix( const char *argv0, const char *env_name ) + * @(#) + * @(#) Don't free the string you get back (treat as result of g_getenv()). + * @(#) The function returns NULL on error. + * + * Written on: 5/2/01 + * Modified on: + * 3/3/01 JC + * - better behaviour for relative paths in argv0 + * 22/9/01 JC + * - oops, SEGV in some cases for argv0 contains relative path + * 26/9/01 JC + * - reworked for new prefix scheme + * 9/11/01 JC + * - grr! added strdup() on putenv() for newer linuxes + * 14/12/01 JC + * - now uses realpath() for better relative pathname guessing + * 21/10/02 JC + * - turn off realpath() if not available + * - path_is_absolute() from glib + * - append ".exe" to name on w32 + * - prefix cwd() to path on w32 + * 31/7/03 JC + * - better relative path handling + * 23/12/04 + * - use g_setenv()/g_getenv() + * 29/4/05 + * - gah, back to plain setenv() so we work with glib-2.2 + * 5/10/05 + * - phew, now we can use g_setenv() again + * 18/8/06 + * - use IM_EXEEXT + * 6/2/07 CB + * - move trailing '\0' too in extract_prefix + * 21/7/07 + * - fall back to configure-time prefix rather than returning an error + * (thanks Jay) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_SYS_PARAM_H +#include +#endif /*HAVE_SYS_PARAM_H*/ +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef HAVE_DIRECT_H +#include +#endif /*HAVE_DIRECT_H*/ +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Strip off any of a set of old suffixes (eg. [".v", ".jpg"]), add a single + * new suffix (eg. ".tif"). + */ +void +im__change_suffix( const char *name, char *out, int mx, + const char *new, const char **olds, int nolds ) +{ + char *p; + int i; + int len; + + /* Copy start string. + */ + im_strncpy( out, name, mx ); + + /* Drop all matching suffixes. + */ + while( (p = strrchr( out, '.' )) ) { + /* Found suffix - test against list of alternatives. Ignore + * case. + */ + for( i = 0; i < nolds; i++ ) + if( g_ascii_strcasecmp( p, olds[i] ) == 0 ) { + *p = '\0'; + break; + } + + /* Found match? If not, break from loop. + */ + if( *p ) + break; + } + + /* Add new suffix. + */ + len = strlen( out ); + im_strncpy( out + len, new, mx - len ); +} + +static char * +get_current_dir( void ) +{ + static char buffer[PATH_MAX]; + char *dir; + + /* We don't use getcwd(3) on SUNOS, because, it does a popen("pwd") + * and, if that wasn't bad enough, hangs in doing so. + */ +#if defined( sun ) && !defined( __SVR4 ) + dir = getwd( buffer ); +#else /* !sun */ + dir = getcwd( buffer, PATH_MAX ); +#endif /* !sun */ + + if( !dir ) { + buffer[0] = G_DIR_SEPARATOR; + buffer[1] = '\0'; + dir = buffer; + } + + return( dir ); +} + +/* Find the prefix part of a dir ... name is the name of this prog from argv0. + * + * dir name guess prefix + * + * /home/john/vips-7.6.4/bin/vips-7.6 vips-7.6 /home/john/vips-7.6.4 + * /usr/local/bin/ip ip /usr/local + * + * all other forms ... return NULL. + */ +static char * +extract_prefix( const char *dir, const char *name ) +{ + char edir[PATH_MAX]; + char vname[PATH_MAX]; + int i; + +#ifdef DEBUG + printf( "extract_prefix: trying for dir = \"%s\", name = \"%s\"\n", + dir, name ); +#endif /*DEBUG*/ + + /* Is dir relative? Prefix with cwd. + */ + if( !g_path_is_absolute( dir ) ) { + im_snprintf( edir, PATH_MAX, "%s" G_DIR_SEPARATOR_S "%s", + get_current_dir(), dir ); + } + else { + im_strncpy( edir, dir, PATH_MAX ); + } + + /* Chop off the trailing prog name, plus the trailing + * G_DIR_SEPARATOR_S. + */ + if( !im_ispostfix( edir, name ) ) + return( NULL ); + im_strncpy( vname, edir, PATH_MAX ); + vname[strlen( edir ) - strlen( name ) - 1] = '\0'; + + /* Remove any "/./", any trailing "/.", any trailing "/". + */ + for( i = 0; i < (int) strlen( vname ); i++ ) + if( im_isprefix( G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S, + vname + i ) ) + memcpy( vname + i, vname + i + 2, + strlen( vname + i + 2 ) + 1 ); + if( im_ispostfix( vname, G_DIR_SEPARATOR_S "." ) ) + vname[strlen( vname ) - 2] = '\0'; + if( im_ispostfix( vname, G_DIR_SEPARATOR_S ) ) + vname[strlen( vname ) - 1] = '\0'; + +#ifdef DEBUG + printf( "extract_prefix: canonicalised path = \"%s\"\n", vname ); +#endif /*DEBUG*/ + + /* Ought to be a "/bin" at the end now. + */ + if( !im_ispostfix( vname, G_DIR_SEPARATOR_S "bin" ) ) + return( NULL ); + vname[strlen( vname ) - strlen( G_DIR_SEPARATOR_S "bin" )] = '\0'; + +#ifdef DEBUG + printf( "extract_prefix: found \"%s\"\n", vname ); +#endif /*DEBUG*/ + + return( im_strdup( NULL, vname ) ); +} + +/* Search a path for a file ... we overwrite the PATH string passed in. + */ +static char * +scan_path( char *path, const char *name ) +{ + char *p, *q; + char *prefix; + + for( p = path; + (q = im_break_token( p, G_SEARCHPATH_SEPARATOR_S )); p = q ) { + char str[PATH_MAX]; + + /* Form complete path. + */ + im_snprintf( str, PATH_MAX, + "%s" G_DIR_SEPARATOR_S "%s", p, name ); + +#ifdef DEBUG + printf( "scan_path: looking in \"%s\" for \"%s\"\n", + p, name ); +#endif /*DEBUG*/ + + if( im_existsf( "%s", str ) && + (prefix = extract_prefix( str, name )) ) { + return( prefix ); + } + } + + return( NULL ); +} + +/* Look for a file along PATH. If we find it, look for an enclosing prefix. + */ +static char * +find_file( const char *name ) +{ + const char *path = g_getenv( "PATH" ); + char *prefix; + char full_path[PATH_MAX]; + + if( !path ) + return( NULL ); + +#ifdef DEBUG + printf( "im_guess_prefix: g_getenv( \"PATH\" ) == \"%s\"\n", path ); +#endif /*DEBUG*/ + +#ifdef OS_WIN32 + /* Windows always searches '.' first, so prepend cwd to path. + */ + im_snprintf( full_path, PATH_MAX, "%s" G_SEARCHPATH_SEPARATOR_S "%s", + get_current_dir(), path ); +#else /*!OS_WIN32*/ + im_strncpy( full_path, path, PATH_MAX ); +#endif /*OS_WIN32*/ + + if( (prefix = scan_path( full_path, name )) ) + return( prefix ); + + return( NULL ); +} + +/* Guess a value for the install PREFIX. + */ +static const char * +guess_prefix( const char *argv0, const char *name ) +{ + char *prefix; + + /* Try to guess from argv0. + */ + if( argv0 ) { + if( g_path_is_absolute( argv0 ) ) { + /* Must point to our executable. + */ + if( (prefix = extract_prefix( argv0, name )) ) { +#ifdef DEBUG + printf( "im_guess_prefix: found \"%s\" from " + "argv0\n", prefix ); +#endif /*DEBUG*/ + return( prefix ); + } + } + + /* Look along path for name. + */ + if( (prefix = find_file( name )) ) { +#ifdef DEBUG + printf( "im_guess_prefix: found \"%s\" from " + "PATH\n", prefix ); +#endif /*DEBUG*/ + return( prefix ); + } + } + +#ifdef HAVE_REALPATH + /* Try to guess from cwd. Only if this is a relative path, though. No + * realpath on winders, but fortunately it seems to always generate + * a full path in argv[0]. + */ + if( !g_path_is_absolute( argv0 ) ) { + char full_path[PATH_MAX]; + char resolved[PATH_MAX]; + + im_snprintf( full_path, PATH_MAX, "%s" G_DIR_SEPARATOR_S "%s", + get_current_dir(), argv0 ); + + if( realpath( full_path, resolved ) ) { + if( (prefix = extract_prefix( resolved, name )) ) { + +#ifdef DEBUG + printf( "im_guess_prefix: found \"%s\" " + "from cwd\n", prefix ); +#endif /*DEBUG*/ + return( prefix ); + } + } + } +#endif /*HAVE_REALPATH*/ + + /* Fall back to the configure-time prefix. + */ + return( IM_PREFIX ); +} + +/* Guess a value for the install PREFIX. + */ +const char * +im_guess_prefix( const char *argv0, const char *env_name ) +{ + const char *prefix; + const char *p; + char name[PATH_MAX]; + + /* Already set? + */ + if( (prefix = g_getenv( env_name )) ) { +#ifdef DEBUG + printf( "im_guess_prefix: found \"%s\" in environment\n", + prefix ); +#endif /*DEBUG*/ + return( prefix ); + } + + /* Get the program name from argv0. + */ + p = im_skip_dir( argv0 ); + + /* Add the exe suffix, if it's missing. + */ + if( strlen( IM_EXEEXT ) > 0 ) { + const char *olds[] = { IM_EXEEXT }; + + im__change_suffix( p, name, PATH_MAX, IM_EXEEXT, olds, 1 ); + } + else + im_strncpy( name, p, PATH_MAX ); + +#ifdef DEBUG + printf( "im_guess_prefix: argv0 = %s\n", argv0 ); + printf( "im_guess_prefix: name = %s\n", name ); + printf( "im_guess_prefix: cwd = %s\n", get_current_dir() ); +#endif /*DEBUG*/ + + prefix = guess_prefix( argv0, name ); + g_setenv( env_name, prefix, TRUE ); + + return( prefix ); +} diff --git a/libsrc/iofuncs/im_header.c b/libsrc/iofuncs/im_header.c new file mode 100644 index 00000000..9175afa1 --- /dev/null +++ b/libsrc/iofuncs/im_header.c @@ -0,0 +1,265 @@ +/* im_header_int, im_header_double, im_header_string: output various fields + * from the VIPS header + * + * 9/7/02 JC + * - first version + * 7/6/05 + * - now reads meta fields too + * - cleaned up + * - added im_header_exists(), im_header_map() + * 1/8/05 + * - now im_header_get_type() and im_header_get() rather than + * im_header_exists() + * 4/1/07 + * - removed Hist from standard fields ... now a separate function + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Name, offset pair. + */ +typedef struct _HeaderField { + const char *field; + glong offset; +} HeaderField; + +/* Built in fields and struct offsets. + */ +static HeaderField int_field[] = { + { "Xsize", G_STRUCT_OFFSET( IMAGE, Xsize ) }, + { "Ysize", G_STRUCT_OFFSET( IMAGE, Ysize ) }, + { "Bands", G_STRUCT_OFFSET( IMAGE, Bands ) }, + { "Bbits", G_STRUCT_OFFSET( IMAGE, Bbits ) }, + { "BandFmt", G_STRUCT_OFFSET( IMAGE, BandFmt ) }, + { "Coding", G_STRUCT_OFFSET( IMAGE, Coding ) }, + { "Type", G_STRUCT_OFFSET( IMAGE, Type ) }, + { "Xoffset", G_STRUCT_OFFSET( IMAGE, Xoffset ) }, + { "Yoffset", G_STRUCT_OFFSET( IMAGE, Yoffset ) } +}; + +/* These are actually floats :-( how annoying. We report them as doubles for + * consistency with the im_meta_*() functions. + */ +static HeaderField double_field[] = { + { "Xres", G_STRUCT_OFFSET( IMAGE, Xres ) }, + { "Yres", G_STRUCT_OFFSET( IMAGE, Yres ) } +}; + +static HeaderField string_field[] = { + { "filename", G_STRUCT_OFFSET( IMAGE, filename ) } +}; + +int +im_header_int( IMAGE *im, const char *field, int *out ) +{ + int i; + + for( i = 0; i < IM_NUMBER( int_field ); i++ ) + if( strcmp( field, int_field[i].field ) == 0 ) { + *out = G_STRUCT_MEMBER( int, im, + int_field[i].offset ); + break; + } + + if( i == IM_NUMBER( int_field ) && + im_meta_get_int( im, field, out ) ) { + im_error( "im_header_int", + _( "no such int field \"%s\"" ), field ); + return( -1 ); + } + + return( 0 ); +} + +int +im_header_double( IMAGE *im, const char *field, double *out ) +{ + int i; + + for( i = 0; i < IM_NUMBER( double_field ); i++ ) + if( strcmp( field, double_field[i].field ) == 0 ) { + *out = G_STRUCT_MEMBER( float, im, + double_field[i].offset ); + break; + } + + if( i == IM_NUMBER( double_field ) && + im_meta_get_double( im, field, out ) ) { + im_error( "im_header_double", + _( "no such double field \"%s\"" ), field ); + return( -1 ); + } + + return( 0 ); +} + +int +im_header_string( IMAGE *im, const char *field, char **out ) +{ + int i; + + for( i = 0; i < IM_NUMBER( string_field ); i++ ) + if( strcmp( field, string_field[i].field ) == 0 ) { + *out = G_STRUCT_MEMBER( char *, im, + string_field[i].offset ); + break; + } + + if( i == IM_NUMBER( string_field ) && + im_meta_get_string( im, field, out ) ) { + im_error( "im_header_string", + _( "no such string field \"%s\"" ), field ); + return( -1 ); + } + + return( 0 ); +} + +GType +im_header_get_type( IMAGE *im, const char *field ) +{ + int i; + GType type; + + for( i = 0; i < IM_NUMBER( int_field ); i++ ) + if( strcmp( field, int_field[i].field ) == 0 ) + return( G_TYPE_INT ); + for( i = 0; i < IM_NUMBER( double_field ); i++ ) + if( strcmp( field, double_field[i].field ) == 0 ) + return( G_TYPE_DOUBLE ); + for( i = 0; i < IM_NUMBER( string_field ); i++ ) + if( strcmp( field, string_field[i].field ) == 0 ) + return( G_TYPE_STRING ); + if( (type = im_meta_get_type( im, field )) ) + return( type ); + + return( 0 ); +} + +/* Fill value_copy with a copy of the value, -1 on error. value_copy must be + * zeroed but uninitialised. User must g_value_unset( value ). + */ +int +im_header_get( IMAGE *im, const char *field, GValue *value_copy ) +{ + int i; + + for( i = 0; i < IM_NUMBER( int_field ); i++ ) + if( strcmp( field, int_field[i].field ) == 0 ) { + g_value_init( value_copy, G_TYPE_INT ); + g_value_set_int( value_copy, + G_STRUCT_MEMBER( int, im, + int_field[i].offset ) ); + return( 0 ); + } + + for( i = 0; i < IM_NUMBER( double_field ); i++ ) + if( strcmp( field, double_field[i].field ) == 0 ) { + g_value_init( value_copy, G_TYPE_DOUBLE ); + g_value_set_double( value_copy, + G_STRUCT_MEMBER( float, im, + double_field[i].offset ) ); + return( 0 ); + } + + for( i = 0; i < IM_NUMBER( string_field ); i++ ) + if( strcmp( field, string_field[i].field ) == 0 ) { + g_value_init( value_copy, G_TYPE_STRING ); + g_value_set_static_string( value_copy, + G_STRUCT_MEMBER( char *, im, + string_field[i].offset ) ); + return( 0 ); + } + + if( !im_meta_get( im, field, value_copy ) ) + return( 0 ); + + return( -1 ); +} + +static void * +header_map_fn( Meta *meta, im_header_map_fn fn, void *a ) +{ + return( fn( meta->im, meta->field, &meta->value, a ) ); +} + +void * +im_header_map( IMAGE *im, im_header_map_fn fn, void *a ) +{ + int i; + GValue value = { 0 }; + void *result; + + for( i = 0; i < IM_NUMBER( int_field ); i++ ) { + im_header_get( im, int_field[i].field, &value ); + result = fn( im, int_field[i].field, &value, a ); + g_value_unset( &value ); + + if( result ) + return( result ); + } + + for( i = 0; i < IM_NUMBER( double_field ); i++ ) { + im_header_get( im, double_field[i].field, &value ); + result = fn( im, double_field[i].field, &value, a ); + g_value_unset( &value ); + + if( result ) + return( result ); + } + + for( i = 0; i < IM_NUMBER( string_field ); i++ ) { + im_header_get( im, string_field[i].field, &value ); + result = fn( im, string_field[i].field, &value, a ); + g_value_unset( &value ); + + if( result ) + return( result ); + } + + if( im->Meta_traverse && + (result = im_slist_map2( im->Meta_traverse, + (VSListMap2Fn) header_map_fn, fn, a )) ) + return( result ); + + return( NULL ); +} diff --git a/libsrc/iofuncs/im_histlin.c b/libsrc/iofuncs/im_histlin.c new file mode 100644 index 00000000..5e3fea4e --- /dev/null +++ b/libsrc/iofuncs/im_histlin.c @@ -0,0 +1,120 @@ +/* @(#) Appends one line of history consisting of a buffer of data, + * @(#) time, history and CR in history section of the image descriptor + * @(#) The history variable list must be declared properly + * @(#) by the calling function + * @(#) + * @(#) int im_histlin(variable_list) + * @(#) (variable_list) is (imagedescriptor, format, arg1, arg2, ...) + * @(#) format, arg1, arg2, ... are the same as in printf + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 16/01/1990 + * Modified on : 21/03/1991 + * 28/10/92 JC + * - if Hist is NULL, no longer returns error code. Now makes a history + * line (just the file name) and continues + * - does not overwrite the end of strdup buffers any more! + * - bugs in calls to time/ctime fixed + * - no longer free's ctime's static buffer! + * - frees old Hist correctly + * 22/12/94 + * - ANSIfied with stdarg + * 2/9/05 + * - no more first line of Hist means something special nonsense + * 4/1/07 + * - added im_history_get() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_histlin( IMAGE *im, const char *fmt, ... ) +{ + va_list args; + char line[4096]; + time_t timebuf; + + /* Format command. -40, to leave 26 for the ctime, three for the # and + * a bit. + */ + va_start( args, fmt ); + (void) im_vsnprintf( line, 4096 - 40, fmt, args ); + va_end( args ); + strcat( line, " # " ); + + /* Add the date. ctime always attaches a '\n', gah. + */ + time( &timebuf ); + strcat( line, ctime( &timebuf ) ); + line[strlen( line ) - 1] = '\0'; + +#ifdef DEBUG + printf( "im_histlin: adding:\n\t%s\nto history on image %p\n", + line, im ); +#endif /*DEBUG*/ + + im->history_list = g_slist_append( im->history_list, + im__gvalue_ref_string_new( line ) ); + + return( 0 ); +} + +/* Read an image's history. + */ +const char * +im_history_get( IMAGE *im ) +{ + if( !im->Hist ) + im->Hist = im__gslist_gvalue_get( im->history_list ); + + return( im->Hist ? im->Hist : "" ); +} diff --git a/libsrc/iofuncs/im_image.c b/libsrc/iofuncs/im_image.c new file mode 100644 index 00000000..d44adf3d --- /dev/null +++ b/libsrc/iofuncs/im_image.c @@ -0,0 +1,92 @@ +/* @(#) Make a memory area of pixels into a VIPS image ... we don't free() on + * @(#) im_close(), that's up to the caller ... format is BandFmt + * @(#) + * @(#) Usage: + * @(#) + * @(#) IMAGE * + * @(#) im_image( void *buffer, int width, int height, int bands, int format ) + * @(#) + * @(#) The function returns NULL on error. + * Written on: 11/7/00 + * Modified on: + * 20/3/01 JC + * - oops, broken for IM_BANDFMT_UCHAR + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_image( void *buffer, int width, int height, int bands, int format ) +{ + IMAGE *im; + + if( width <= 0 || height <= 0 || bands <= 0 || + format < 0 || format > IM_BANDFMT_DPCOMPLEX ) { + im_errormsg( "im_image: bad parameters" ); + return( NULL ); + } + + /* Make new output image for us. + */ + if( !(im = im_init( "untitled" )) ) + return( NULL ); + + /* Set header fields. + */ + im->Xsize = width; + im->Ysize = height; + im->Bands = bands; + im->BandFmt = format; + im->Bbits = im_bits_of_fmt( format ); + im->Coding = IM_CODING_NONE; + + if( bands == 1 ) + im->Type = IM_TYPE_B_W; + else if( bands == 3 ) + im->Type = IM_TYPE_RGB; + else + im->Type = IM_TYPE_MULTIBAND; + + im->data = (char *) buffer; + im->dtype = IM_SETBUF_FOREIGN; + + return( im ); +} diff --git a/libsrc/iofuncs/im_init.c b/libsrc/iofuncs/im_init.c new file mode 100644 index 00000000..65271879 --- /dev/null +++ b/libsrc/iofuncs/im_init.c @@ -0,0 +1,168 @@ +/* @(#) Initialise the IMAGE to impossible startup values. Install the + * @(#) filename. + * @(#) + * @(#) IMAGE *im_init( char *filename ) + * + * Copyright: Nicos Dessipris & Kirk Martinez, 1990 + * Written on: 13/02/1990 + * Modified on : 3/6/92 22/2/93 + * 15/4/93 J.Cupitt + * - init for partial image buffers added + * - init for type field + * - filename added + * 10/5/93 J.Cupitt + * - allocates space for IMAGE too, and returns new data + * 23/2/94 JC + * - ANSIfied, man page revised + * 16/8/94 JC + * - evalend callbacks added + * 28/11/94 JC + * - new compression fields added, thr added + * 24/10/95 JC + * - now tracks open images ... see also im_close() and debug.c + * 1/12/04 JC + * - added an im_init_world() to help old progs + * 30/9/05 + * - added sizeof_header + * 2/1/07 + * - init magic + * - init history_list + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_NEW + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Make a new IMAGE structure, set fields to an initial state. We set the + * filename field only. + */ +IMAGE * +im_init( const char *filename ) +{ + IMAGE *im; + + /* Pass in a nonsense name for argv0 ... this init world is only here + * for old programs which are missing an im_init_world() call. We must + * have threads set up before we can process. + */ + if( im_init_world( "vips" ) ) + im_error_clear(); + + if( !(im = IM_NEW( NULL, IMAGE )) ) + return( NULL ); + +#ifdef DEBUG_NEW + printf( "im_init: new IMAGE 0x%p, \"%s\"\n", im, filename ); +#endif /*DEBUG_NEW*/ + + im->Xsize = -1; + im->Ysize = -1; + im->Bands = -1; + im->Bbits = -1; + im->BandFmt = -1; + im->Coding = -1; + im->Type = -1; + im->Xres = 1.0; + im->Yres = 1.0; + im->Length = 0; + im->Compression = 0; + im->Level = 0; + im->Xoffset = 0; + im->Yoffset = 0; + + im->Hist = NULL; + + im->data = NULL; + im->time = NULL; + im->kill = 0; + + im->dtype = IM_NONE; + im->fd = -1; + im->baseaddr = NULL; + im->length = 0; + im->closefns = NULL; + im->evalfns = NULL; + im->evalendfns = NULL; + im->closing = 0; + im->close_pending = 0; + + /* Default to native order. + */ + im->magic = im_amiMSBfirst() ? IM_MAGIC_SPARC : IM_MAGIC_INTEL; + + im->start = NULL; + im->generate = NULL; + im->stop = NULL; + im->client1 = NULL; + im->client2 = NULL; + im->sslock = g_mutex_new(); + im->regions = NULL; + im->dhint = IM_SMALLTILE; + + im->Meta = NULL; + im->Meta_traverse = NULL; + + /* Default to the VIPS header size. Can be changed later. + */ + im->sizeof_header = IM_SIZEOF_HEADER; + + im->windows = NULL; + + im->parents = NULL; + im->children = NULL; + im->serial = 0; + + im->history_list = NULL; + + if( !(im->filename = im_strdup( NULL, filename )) ) { + im_close( im ); + return( NULL ); + } + + im__open_images = g_slist_prepend( im__open_images, im ); + + return( im ); +} diff --git a/libsrc/iofuncs/im_init_world.c b/libsrc/iofuncs/im_init_world.c new file mode 100644 index 00000000..dbe4fd3c --- /dev/null +++ b/libsrc/iofuncs/im_init_world.c @@ -0,0 +1,221 @@ +/* Start up the world of vips. + * + * 7/1/04 JC + * - 1st version + * 7/6/05 + * - g_type_init() too, so we can use gobject + * 2/9/06 + * - also set g_prg_name() and load plugins + * 8/12/06 + * - add liboil support + * 5/2/07 + * - stop a loop if we're called recursively during VIPS startup ... it + * can happen if (for example) im_guess_prefix() fails and tries to + * i18n an error message (thanks Christian) + * 8/6/07 + * - just warn if plugins fail to load correctly: too annoying to have + * VIPS refuse to start because of a dodgy plugin + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#ifdef HAVE_LIBOIL +#include +#endif /*HAVE_LIBOIL*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Use in various small places where we need a mutex and it's not worth + * making a private one. + */ +GMutex *im__global_lock = NULL; + +int +im_init_world( const char *argv0 ) +{ + static gboolean started = FALSE; + static gboolean done = FALSE; + char *prgname; + const char *prefix; + char name[256]; + + /* Two stage done handling: 'done' means we've completed, 'started' + * means we're currently initialising. Use this to prevent recursive + * invocation. + */ + if( done ) + /* Called more than once, we succeeded, just return OK. + */ + return( 0 ); + if( started ) + /* Recursive invocation, something has broken horribly. + * Hopefully the first init will handle it. + */ + return( 0 ); + started = TRUE; + + /* Need gobject etc. + */ + g_type_init(); + +#ifdef G_THREADS_ENABLED + if( !g_thread_supported() ) + g_thread_init( NULL ); +#endif /*G_THREADS_ENABLED*/ + + if( !im__global_lock ) + im__global_lock = g_mutex_new(); + + prgname = g_path_get_basename( argv0 ); + g_set_prgname( prgname ); + g_free( prgname ); + + /* Try to discover our prefix. + */ + if( !(prefix = im_guess_prefix( argv0, "VIPSHOME" )) ) + return( -1 ); + + /* Get i18n .mo files from $VIPSHOME/share/locale/. + */ + im_snprintf( name, 256, + /* + "%s" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "locale", + */ + "%s" G_DIR_SEPARATOR_S "share", + prefix ); + bindtextdomain( GETTEXT_PACKAGE, name ); + bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" ); + + /* Start up converters for builtin types. + */ + im__meta_init_types(); + + /* Load up any plugins in $VIPSHOME/lib. We don't error on failure, + * it's too annoying to have VIPS refuse to start because of a broken + * plugin. + */ + if( im_load_plugins( "%s/lib", prefix ) ) { + im_warn( "im_init_world", "%s", im_errorstring() ); + im_error_clear(); + } + + /* Start up the buffer cache. + */ + im__buffer_init(); + +#ifdef HAVE_LIBOIL +{ +#ifdef DEBUG + GTimer *timer = g_timer_new(); +#endif /*DEBUG*/ + + oil_init(); + +#ifdef DEBUG + /* 0.3 is only about 0.1s on my laptop, but this may take longer in + * future. + */ + printf( "oil_init: %gs\n", g_timer_elapsed( timer, NULL ) ); + g_timer_destroy( timer ); +#endif /*DEBUG*/ +} +#endif /*HAVE_LIBOIL*/ + + done = TRUE; + + return( 0 ); +} + +const char * +im__gettext( const char *msgid ) +{ + /* Pass in a nonsense name for argv0 ... this init path is only here + * for old programs which are missing an im_init_world() call. We need + * i18n set up before we can translate. + */ + if( im_init_world( "giant_banana" ) ) + im_error_clear(); + + return( dgettext( GETTEXT_PACKAGE, msgid ) ); +} + +const char * +im__ngettext( const char *msgid, const char *plural, unsigned long int n ) +{ + if( im_init_world( "giant_banana" ) ) + im_error_clear(); + + return( dngettext( GETTEXT_PACKAGE, msgid, plural, n ) ); +} + +static GOptionEntry option_entries[] = { + { "vips-concurrency", 'c', 0, G_OPTION_ARG_INT, &im__concurrency, + N_( "evaluate with N concurrent threads" ), "N" }, + { "vips-tile-width", 'c', 0, G_OPTION_ARG_INT, &im__tile_width, + N_( "set tile width to N (DEBUG)" ), "N" }, + { "vips-tile-height", 'c', 0, G_OPTION_ARG_INT, &im__tile_height, + N_( "set tile height to N (DEBUG)" ), "N" }, + { "vips-thinstrip-height", 'c', 0, + G_OPTION_ARG_INT, &im__thinstrip_height, + N_( "set thinstrip height to N (DEBUG)" ), "N" }, + { "vips-fatstrip-height", 'c', 0, + G_OPTION_ARG_INT, &im__fatstrip_height, + N_( "set fatstrip height to N (DEBUG)" ), "N" }, + { NULL } +}; + +/* The cmd-line options we support. + */ +GOptionGroup * +im_get_option_group( void ) +{ + static GOptionGroup *option_group = NULL; + + if( !option_group ) { + option_group = g_option_group_new( + "vips", _( "VIPS Options" ), _( "Show VIPS options" ), + NULL, NULL ); + g_option_group_add_entries( option_group, option_entries ); + } + + return( option_group ); +} diff --git a/libsrc/iofuncs/im_initdesc.c b/libsrc/iofuncs/im_initdesc.c new file mode 100644 index 00000000..7c6833cf --- /dev/null +++ b/libsrc/iofuncs/im_initdesc.c @@ -0,0 +1,81 @@ +/* @(#) Initialises an image descriptor to entered values + * @(#) fd, baseaddr, data and filename are not handled by this function + * @(#) The order of the args is the same as in vips/vips.h + * + * @(#) Right call: + * @(#) void im_initdesc(image, xsize, ysize, bands, bandbits, bandfmt, + coding, type, xres, yres) + * @(#) IMAGE *image; + * @(#) int xsize, ysize, bands, bandbits, bandfmt, coding, type; + * @(#) float xres, yres; + * HANDLESHEADER + * Copyright: Nicos Dessipris, 1991 + * Written on: 02/04/1991 + * Modified on : 3/6/92 Kirk Martinez + * 23/2/94 JC + * - ANSIfied + * 2/6/07 + * - ignore bandbits + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void +im_initdesc( IMAGE *image, + int xsize, int ysize, + int bands, int bandbits, int bandfmt, + int coding, int type, + float xres, float yres, + int xo, int yo ) +{ + image->Xsize = xsize; + image->Ysize = ysize; + image->Bands = bands; + /* bandbits is deprecated ... set to whatever the format requires. + */ + image->Bbits = im_bits_of_fmt( bandfmt ); + image->BandFmt = bandfmt; + image->Coding = coding; + image->Type = type; + image->Xres = xres; + image->Yres = yres; + image->Xoffset = xo; + image->Yoffset = yo; +} diff --git a/libsrc/iofuncs/im_iocheck.c b/libsrc/iofuncs/im_iocheck.c new file mode 100644 index 00000000..3fac63fc --- /dev/null +++ b/libsrc/iofuncs/im_iocheck.c @@ -0,0 +1,313 @@ +/* @(#) Function which checks the structures imagein and imageout + * @(#) It returns a valid value only if ip and op are set properly + * @(#) Cases of returned integer value + * @(#) + * @(#) int im_iocheck(imagein, imageout) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) Returns -1 on fail + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : + * 15/4/93 JC + * - im_incheck(), im_outcheck() added. + * - type field now checked. + * 10/6/93 JC + * - auto-fallback to old-style input added + * 6/6/95 JC + * - revised and improved fallback code + */ + +/* @(#) Try to make an IMAGE writeable. im_mmapin files become im_mmapinrw + * @(#) files, buffers are left alone and output files and partial images + * @(#) generate an error. + * @(#) + * @(#) int im_rwcheck( im ) + * @(#) IMAGE *im; + * @(#) + * @(#) Returns non-zero on error. + * @(#) + * Copyright: John Cupitt + * Written on: 17/6/92 + * Updated on: + * 15/4/93 + * - checks for partial images added + * - now uses type field + * 31/8/93 JC + * - returns ok for IM_MMAPINRW type files now too + * - returns -1 rather than 1 on error + * - ANSIfied + * 1/10/97 JC + * - moved here, and renamed im_rwcheck() + * 13/2/01 JC + * - im_image_sanity() checks added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif /*HAVE_SYS_FILE_H*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Convert a partial to a setbuf. + */ +static int +convert_ptob( IMAGE *im ) +{ + IMAGE *t1; + + /* Change to IM_SETBUF. First, make a memory buffer and copy into that. + */ + if( !(t1 = im_open( "im_incheck:1", "t" )) ) + return( -1 ); + if( im_copy( im, t1 ) ) { + im_close( t1 ); + return( -1 ); + } + + /* Copy new stuff in. We can't im__close( im ) and free stuff, as this + * would kill of lots of regions and cause dangling pointers + * elsewhere. + */ + im->dtype = IM_SETBUF; + im->data = t1->data; + t1->data = NULL; + + /* Close temp image. + */ + if( im_close( t1 ) ) + return( -1 ); + + return( 0 ); +} + +/* Convert an openin to a mmapin. + */ +static int +convert_otom( IMAGE *im ) +{ + /* just mmap() the whole thing. + */ + if( im_mapfile( im ) ) + return( -1 ); + im->data = im->baseaddr + im->sizeof_header; + im->dtype = IM_MMAPIN; + + return( 0 ); +} + +/* Check that an image is readable by old-style VIPS functions. + */ +int +im_incheck( IMAGE *im ) +{ + if( im_image_sanity( im ) ) + return( -1 ); + +#ifdef DEBUG_IO + printf( "im_incheck: old-style input for %s\n", im->filename ); +#endif/*DEBUG_IO*/ + + switch( im->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + /* Should have been written to. + */ + if( !im->data ) { + im_errormsg( "im_incheck: no image data" ); + return( -1 ); + } + + break; + + case IM_MMAPIN: + case IM_MMAPINRW: + /* Can read from all these, in principle anyway. + */ + break; + + case IM_PARTIAL: +#ifdef DEBUG_IO + printf( "im_incheck: converting partial image for old-style input\n" ); +#endif/*DEBUG_IO*/ + + /* Change to a setbuf, so our caller can use it. + */ + if( convert_ptob( im ) ) + return( -1 ); + + break; + + case IM_OPENIN: +#ifdef DEBUG_IO + printf( "im_incheck: converting openin image for old-style input\n" ); +#endif/*DEBUG_IO*/ + + /* Change to a MMAPIN. + */ + if( convert_otom( im ) ) + return( -1 ); + + break; + + case IM_OPENOUT: + /* Close file down and reopen as im_mmapin. + */ +#ifdef DEBUG_IO + printf( "im_incheck: auto-rewind of %s\n", im->filename ); +#endif/*DEBUG_IO*/ + if( im__close( im ) || im_openin( im ) ) { + im_errormsg( "im_incheck: auto-rewind for %s failed", + im->filename ); + return( -1 ); + } + + break; + + default: + im_errormsg( "im_incheck: image not readable" ); + return( -1 ); + } + + return( 0 ); +} + +/* Check that an image is writeable. + */ +int +im_outcheck( IMAGE *im ) +{ +#ifdef DEBUG_IO + printf( "im_outcheck: old-style output for %s\n", im->filename ); +#endif/*DEBUG_IO*/ + + switch( im->dtype ) { + case IM_PARTIAL: + /* Make sure nothing is attached. + */ + if( im->generate ) { + im_errormsg( "im_outcheck: image already written" ); + return( -1 ); + } + + /* Cannot do old-style write to PARTIAL. Turn to SETBUF. + */ + im->dtype = IM_SETBUF; + + /* Fall through to SETBUF case. + */ + + case IM_SETBUF: + /* Check that it has not been im_setupout(). + */ + if( im->data ) { + im_errormsg( "im_outcheck: image already written" ); + return( -1 ); + } + + break; + + case IM_OPENOUT: + case IM_SETBUF_FOREIGN: + /* Can write to this ok. + */ + break; + + default: + im_errormsg( "im_outcheck: image not writeable" ); + return( -1 ); + } + + return( 0 ); +} + +/* Check a pair of fds for IO. + */ +int +im_iocheck( IMAGE *in, IMAGE *out ) +{ + return( im_incheck( in ) || im_outcheck( out ) ); +} + +int +im_rwcheck( IMAGE *im ) +{ + /* Do an im_incheck(). This will rewind im_openout() files, and + * generate im_partial() files. + */ + if( im_incheck( im ) ) { + im_errormsg( "im_rwcheck: unable to rewind file" ); + return( -1 ); + } + + /* Look at the type. + */ + switch( im->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPINRW: + /* No action necessary. + */ + break; + + case IM_MMAPIN: + /* Try to remap read-write. + */ + if( im_remapfilerw( im ) ) + return( -1 ); + + break; + + default: + im_errormsg( "im_rwcheck: bad file type" ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_iterate.c b/libsrc/iofuncs/im_iterate.c new file mode 100644 index 00000000..36487437 --- /dev/null +++ b/libsrc/iofuncs/im_iterate.c @@ -0,0 +1,230 @@ +/* Manage pipelines of partial images. + * + * J.Cupitt, 17/4/93. + * 1/7/93 JC + * - adapted for partial v2 + * 9/5/94 + * - new thread stuff added, with a define to turn it off + * 21/11/94 JC + * - pw and ph wrong way round! + * 24/5/95 JC + * - redone, now works in pipelines! + * 30/8/96 JC + * - more sharing with im_generate() + * 2/3/98 JC + * - IM_ANY added + * 19/1/99 JC + * - oops, threads were broken :( + * 30/7/99 RP JC + * - threads reorganised for POSIX + * 25/10/03 JC + * - read via a buffer image so we work with mmap window images + * 27/11/06 + * - merge threadgroup stuff + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Loop over an image, preparing in parts with threads. + */ +static int +eval_to_image( im_threadgroup_t *tg, IMAGE *im ) +{ + int x, y; + Rect image; + + /* Set up. + */ + tg->inplace = 0; + + image.left = 0; + image.top = 0; + image.width = im->Xsize; + image.height = im->Ysize; + + /* Loop over or, attaching to all sub-parts in turn. + */ + for( y = 0; y < im->Ysize; y += tg->ph ) + for( x = 0; x < im->Xsize; x += tg->pw ) { + im_thread_t *thr; + Rect pos; + Rect clipped; + + /* thrs appear on idle when the child thread does + * threadgroup_idle_add and hits the 'go' semaphore. + */ + thr = im_threadgroup_get( tg ); + + /* Set the position we want to generate with this + * thread. + */ + pos.left = x; + pos.top = y; + pos.width = tg->pw; + pos.height = tg->ph; + im_rect_intersectrect( &image, &pos, &clipped ); + + thr->pos = clipped; + + /* Start worker going. + */ + im_threadgroup_trigger( thr ); + + /* Trigger any eval callbacks on our source image. + */ + im__handle_eval( im, tg->pw, tg->ph ); + + /* Check for errors. + */ + if( im_threadgroup_iserror( tg ) ) { + /* Don't kill threads yet ... we may want to + * get some error stuff out of them. + */ + im_threadgroup_wait( tg ); + return( -1 ); + } + } + + /* Wait for all threads to hit 'go' again. + */ + im_threadgroup_wait( tg ); + + /* Test for any errors. + */ + if( im_threadgroup_iserror( tg ) ) + return( -1 ); + + return( 0 ); +} + +static int +iterate( im_threadgroup_t *tg, IMAGE *im, + void *(*start)(), int (*generate)(), int (*stop)(), void *b, void *c ) +{ + int i; + int res; + +#ifdef DEBUG_IO + if( tg && tg->nthr > 1 ) + im_diagnostics( "im_iterate: using %d threads", tg->nthr ); +#endif /*DEBUG_IO*/ + + /* Call all the start functions, and pop in the sequence values. + */ + for( i = 0; i < tg->nthr; i++ ) { + if( start && !(tg->thr[i]->a = start( im, b, c )) ) { + im_error( "im_iterate", + _( "start function failed for image \"%s\"" ), + im->filename ); + return( -1 ); + } + tg->thr[i]->b = b; + tg->thr[i]->c = c; + } + + /* Loop and generate multi-thread. + */ + res = eval_to_image( tg, im ); + + /* Call all stop functions. + */ + for( i = 0; i < tg->nthr; i++ ) { + if( tg->thr[i]->a && stop ) { + /* Trigger the stop function. + */ + if( stop( tg->thr[i]->a, b, c ) ) + /* Drastic! + */ + im_error( "im_iterate", + _( "stop function failed " + "for image \"%s\"" ), + im->filename ); + tg->thr[i]->a = NULL; + } + } + + return( res ); +} + +/* Scan region over image in small pieces. + */ +int +im_iterate( IMAGE *im, + void *(*start)(), int (*generate)(), int (*stop)(), void *b, void *c ) +{ + IMAGE *t; + im_threadgroup_t *tg; + int res; + + if( im_image_sanity( im ) ) + return( -1 ); + + if( !(t = im_open( "im_iterate_buffer", "p" )) ) + return( -1 ); + if( im_copy( im, t ) ) { + im_close( t ); + return( -1 ); + } + + if( !(tg = im_threadgroup_create( t )) ) { + im_close( t ); + return( -1 ); + } + tg->work = generate; + tg->inplace = 0; + +#ifdef DEBUG_IO + if( tg && tg->nthr > 1 ) + im_diagnostics( "im_iterate: using %d threads", tg->nthr ); +#endif /*DEBUG_IO*/ + + res = iterate( tg, t, start, generate, stop, b, c ); + + im_threadgroup_free( tg ); + im_close( t ); + + return( res ); +} diff --git a/libsrc/iofuncs/im_makerw.c b/libsrc/iofuncs/im_makerw.c new file mode 100644 index 00000000..49342e70 --- /dev/null +++ b/libsrc/iofuncs/im_makerw.c @@ -0,0 +1,86 @@ +/* @(#) Try to make an IMAGE writeable. im_mmapin files become im_mmapinrw + * @(#) files, buffers are left alone and output files and partial images + * @(#) generate an error. + * @(#) + * @(#) int im_makerw( im ) + * @(#) IMAGE *im; + * @(#) + * @(#) Returns non-zero on error. + * @(#) + * Copyright: John Cupitt + * Written on: 17/6/92 + * Updated on: + * 15/4/93 + * - checks for partial images added + * - now uses type field + * 31/8/93 JC + * - returns ok for IM_MMAPINRW type files now too + * - returns -1 rather than 1 on error + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_makerw( IMAGE *im ) +{ + /* This will rewind im_openout() files, and generate im_partial() files. + */ + if( im_incheck( im ) ) { + im_errormsg( "im_makerw: unable to rewind file" ); + return( -1 ); + } + + switch( im->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPINRW: + break; + + case IM_MMAPIN: + if( im_remapfilerw( im ) ) + return( -1 ); + break; + + default: + im_errormsg( "im_makerw: bad file type" ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_mapfile.c b/libsrc/iofuncs/im_mapfile.c new file mode 100644 index 00000000..cce255fc --- /dev/null +++ b/libsrc/iofuncs/im_mapfile.c @@ -0,0 +1,351 @@ +/* @(#) Function which returns a char pointer at the beginning of the file + * @(#) corresponding to fd. + * @(#) + * @(#) int + * @(#) im_mapfile( im ) + * @(#) IMAGE im; + * @(#) + * @(#) As above, but map read-write. + * @(#) + * @(#) int + * @(#) im_mapfilerw( im ) + * @(#) IMAGE im; + * @(#) + * @(#) Return -1 on error, 0 for success. + * + * Copyright: Nicos Dessipris + * Wriiten on: 13/02/1990 + * Updated on: + * 10/5/93 J.Cupitt + * - im_mapfilerw() added + * 13/12/94 JC + * - ANSIfied + * 5/7/99 JC + * - better error if unable to map rw + * 31/3/02 JC + * - better mmap() fails error + * 19/9/02 JC + * - added im__mmap()/im__munmap() with windows versions + * 5/1/04 Lev Serebryakov + * - patched for freebsd compatibility + * 5/2/04 JC + * - now records length as well as base, so we unmap the right amount of + * memory even if files change behind our back + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif /*HAVE_SYS_MMAN_H*/ +#ifdef HAVE_SYS_FILE_H +#include +#endif /*HAVE_SYS_FILE_H*/ +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef OS_WIN32 +#ifndef S_ISREG +#define S_ISREG(m) (!!(m & _S_IFREG)) +#endif +#endif /*OS_WIN32*/ + +#include + +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void * +im__mmap( int fd, int writeable, size_t length, gint64 offset ) +{ + void *baseaddr; + +#ifdef DEBUG + printf( "im__mmap: length = %d, offset = %lld\n", length, offset ); +#endif /*DEBUG*/ + +#ifdef OS_WIN32 +{ + HANDLE hFile = (HANDLE) _get_osfhandle( fd ); + HANDLE hMMFile; + DWORD flProtect; + DWORD dwDesiredAccess; + DWORD dwFileOffsetHigh; + DWORD dwFileOffsetLow; + + /* woah, slightly gross + */ + int dws = sizeof( DWORD ); + int shift = 8 * dws; + gint64 mask = ((gint64) -1) >> shift; + + if( writeable ) { + flProtect = PAGE_READWRITE; + dwDesiredAccess = FILE_MAP_WRITE; + } + else { + flProtect = PAGE_READONLY; + dwDesiredAccess = FILE_MAP_READ; + } + + if( !(hMMFile = CreateFileMapping( hFile, + NULL, flProtect, 0, 0, NULL )) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to CreateFileMapping" ) ); + return( NULL ); + } + + dwFileOffsetHigh = (offset >> shift) & mask; + dwFileOffsetLow = offset & mask; + + if( !(baseaddr = (char *)MapViewOfFile( hMMFile, dwDesiredAccess, + dwFileOffsetHigh, dwFileOffsetLow, length )) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to MapViewOfFile" ) ); + CloseHandle( hMMFile ); + return( NULL ); + } + + /* Can close mapping now ... view stays until UnmapViewOfFile(). + + FIXME ... is this a performance problem? + + */ + CloseHandle( hMMFile ); +} +#else /*!OS_WIN32*/ +{ + int prot; + + if( writeable ) + prot = PROT_WRITE; + else + prot = PROT_READ; + + /* Casting gint64 to off_t should be safe, even on *nixes without + * LARGEFILE. + */ + + baseaddr = mmap( 0, length, prot, MAP_SHARED, fd, (off_t) offset ); + if( baseaddr == MAP_FAILED ) { + im_error_system( errno, "im_mapfile", _( "unable to mmap" ) ); + im_warn( "im_mapfile", _( "map failed (%s), " + "running very low on system resources, " + "expect a crash soon" ), strerror( errno ) ); + return( NULL ); + } +} +#endif /*OS_WIN32*/ + + return( baseaddr ); +} + +int +im__munmap( void *start, size_t length ) +{ +#ifdef OS_WIN32 + if( !UnmapViewOfFile( start ) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to UnmapViewOfFile" ) ); + return( -1 ); + } +#else /*!OS_WIN32*/ + if( munmap( start, length ) < 0 ) { + im_error_system( errno, "im_mapfile", + _( "unable to munmap file" ) ); + return( -1 ); + } +#endif /*OS_WIN32*/ + + return( 0 ); +} + +int +im_mapfile( IMAGE *im ) +{ + gint64 length; + struct stat st; + mode_t m; + + assert( !im->baseaddr ); + + /* Check the size of the file; if it is less than 64 bytes, then flag + * an error. + */ + if( (length = im_file_length( im->fd )) == -1 ) + return( -1 ); + if( fstat( im->fd, &st ) == -1 ) { + im_error( "im_mapfile", _( "unable to get file status" ) ); + return( -1 ); + } + m = (mode_t) st.st_mode; + if( length < 64 ) { + im_error( "im_mapfile", _( "file is less than 64 bytes" ) ); + return( -1 ); + } + if( !S_ISREG( m ) ) { + im_error( "im_mapfile", _( "not a regular file" ) ); + return( -1 ); + } + + if( !(im->baseaddr = im__mmap( im->fd, 0, length, 0 )) ) + return( -1 ); + + /* im__mmap() will fail for >2GB, so this is safe even for large + * files. + */ + im->length = length; + + return( 0 ); +} + +/* As above, but map read/write. + */ +int +im_mapfilerw( IMAGE *im ) +{ + gint64 length; + struct stat st; + mode_t m; + + assert( !im->baseaddr ); + + /* Check the size of the file if it is less than 64 bytes return + * make also sure that it is a regular file + */ + if( (length = im_file_length( im->fd )) == -1 ) + return( -1 ); + if( fstat( im->fd, &st ) == -1 ) { + im_error( "im_mapfilerw", _( "unable to get file status" ) ); + return( -1 ); + } + m = (mode_t) st.st_mode; + if( length < 64 || !S_ISREG( m ) ) { + im_error( "im_mapfile", _( "unable to read data" ) ); + return( -1 ); + } + + if( !(im->baseaddr = im__mmap( im->fd, 1, length, 0 )) ) + return( -1 ); + + /* im__mmap() will fail for >2GB, so this is safe even for large + * files. + */ + im->length = length; + + return( 0 ); +} + +/* From im_rwcheck() ... image needs to be a completely mapped read-only file, + * we try to remap it read-write. + */ +int +im_remapfilerw( IMAGE *image ) +{ + void *baseaddr; + +#ifdef OS_WIN32 +{ + HANDLE hFile = (HANDLE) _get_osfhandle( image->fd ); + HANDLE hMMFile; + + if( !(hMMFile = CreateFileMapping( hFile, + NULL, PAGE_READWRITE, 0, 0, NULL )) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to CreateFileMapping" ) ); + return( -1 ); + } + + if( !UnmapViewOfFile( image->baseaddr ) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to UnmapViewOfFile" ) ); + return( -1 ); + } + if( !(baseaddr = (char *)MapViewOfFileEx( hMMFile, FILE_MAP_WRITE, + 0, 0, 0, image->baseaddr )) ) { + im_error_system( GetLastError(), "im_mapfile", + _( "unable to MapViewOfFile" ) ); + CloseHandle( hMMFile ); + return( -1 ); + } + + /* Can close mapping now ... view stays until UnmapViewOfFile(). + + FIXME ... is this a performance problem? + + */ + CloseHandle( hMMFile ); +} +#else /*!OS_WIN32*/ +{ + assert( image->dtype == IM_MMAPIN ); + + baseaddr = mmap( image->baseaddr, image->length, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, + image->fd, 0 ); + if( baseaddr == (void *)-1 ) { + im_error( "im_mapfile", _( "unable to mmap: \"%s\" - %s" ), + image->filename, strerror( errno ) ); + return( -1 ); + } +} +#endif /*OS_WIN32*/ + + image->dtype = IM_MMAPINRW; + + if( baseaddr != image->baseaddr ) { + im_error( "im_mapfile", _( "unable to mmap \"%s\" to same " + "address" ), image->filename ); + image->baseaddr = baseaddr; + return( -1 ); + } + + return( 0 ); +} + diff --git a/libsrc/iofuncs/im_open.c b/libsrc/iofuncs/im_open.c new file mode 100644 index 00000000..085c6dfc --- /dev/null +++ b/libsrc/iofuncs/im_open.c @@ -0,0 +1,605 @@ +/* @(#) im_open: vips front end to file open functions +ARGS: filename and mode which is one of: +"r" read by mmap (im_mmapin) +"rw" read and write by mmap (im_mmapinrw) +"w" write for write (im_writeline) +"t" for temporary memory image +"p" for partial memory image +RETURNS: IMAGE pointer or 0 on error +Copyright: Kirk Martinez 29/5/92 + +Modified: + * 8/8/94 JC + * - ANSIfied + * - im_open_local added + * 16/8/94 JC + * - im_malloc() added + * 22/11/94 JC & KM + * - tiff added + * 1/2/95 JC + * - tiff removed again! + * - now just im_istiff(), im_tiff2vips() and im_vips2tiff() + * - applications have responsibility for making the translation + * 26/3/96 JC + * - im_open_local() now closes on close, not evalend + * 14/11/96 JC + * - tiff and jpeg added again + * - open for read used im_istiff() and im_isjpeg() to switch + * - open for write looks at suffix + * 23/4/97 JC + * - im_strdup() now allows NULL IMAGE parameter + * - subsample parameter added to im_tiff2vips() + * 29/10/98 JC + * - byte-swap stuff added + * 16/6/99 JC + * - 8x byte swap added for double/double complex + * - better error message if file does not exist + * - ignore case when testing suffix for save + * - fix im_mmapinrw() return test to stop coredump in edvips if + * unwritable + * 2/11/99 JC + * - malloc/strdup stuff moved to memory.c + * 5/8/00 JC + * - fixes for im_vips2tiff() changes + * 13/10/00 JC + * - ooops, missing 'else' + * 22/11/00 JC + * - ppm read/write added + * 12/2/01 JC + * - im__image_sanity() added + * 16/5/01 JC + * - now allows RW for non-native byte order, provided it's an 8-bit + * image + * 11/7/01 JC + * - im_tiff2vips() no longer has subsample option + * 25/3/02 JC + * - better im_open() error message + * 12/12/02 JC + * - sanity no longer returns errors, just warns + * 28/12/02 HB + * - Added PNG support + * 6/5/03 JC + * - added im_open_header() (from header.c) + * 22/5/03 JC + * - im__skip_dir() knows about ':' in filenames for vips2tiff etc. + * 27/11/03 JC + * - im_image_sanity() now insists x/y/bands all >0 + * 6/1/04 JC + * - moved util funcs out to util.c + * 18/2/04 JC + * - added an im_init_world() to help old progs + * 16/4/04 + * - oop, im_open() didn't know about input options in filenames + * 2/11/04 + * - im_open( "r" ) is now lazier ... for non-VIPS formats, we delay + * read until the first ->generate() + * - so im_open_header() is now a no-op + * 22/6/05 + * - if TIFF open fails, try going through libmagick + * 4/8/05 + * - added analyze read + * 30/9/05 + * - oops, lazy read error recovery didn't clear a pointer + * 1/5/06 + * - added OpenEXR read + * 9/6/06 + * - added CSV read/write + * 20/9/06 + * - test for NULL filename/mode, common if you forget to check argc + * (thanks bruno) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Suffix sets. + */ +static const char *im_suffix_vips[] = { + "v", "", + NULL +}; +static const char *im_suffix_tiff[] = { + "tif", "tiff", + NULL +}; +static const char *im_suffix_jpeg[] = { + "jpeg", "jpg", "jfif", "jpe", + NULL +}; +static const char *im_suffix_ppm[] = { + "ppm", "pbm", "pgm", + NULL +}; +static const char *im_suffix_png[] = { + "png", + NULL +}; +static const char *im_suffix_csv[] = { + "csv", + NULL +}; + +/* Open a VIPS image and byte-swap the image data if necessary. + */ +static IMAGE * +read_vips( const char *filename ) +{ + IMAGE *im, *im2; + + if( !(im = im_init( filename )) ) + return( NULL ); + if( im_openin( im ) ) { + im_close( im ); + return( NULL ); + } + + /* Already in native format? + */ + if( im_isMSBfirst( im ) == im_amiMSBfirst() ) + return( im ); + + /* Not native ... but maybe does not need swapping? + */ + if( im->Coding == IM_CODING_LABQ ) + return( im ); + if( im->Coding != IM_CODING_NONE ) { + im_close( im ); + im_error( "im_open", _( "unknown coding type" ) ); + return( NULL ); + } + if( im->BandFmt == IM_BANDFMT_CHAR || im->BandFmt == IM_BANDFMT_UCHAR ) + return( im ); + + /* Needs swapping :( make a little pipeline up to do this for us. + */ + if( !(im2 = im_open( filename, "p" )) ) + return( NULL ); + if( im_add_close_callback( im2, + (im_callback_fn) im_close, im, NULL ) ) { + im_close( im ); + im_close( im2 ); + return( NULL ); + } + if( im_copy_swap( im, im2 ) ) { + im_close( im2 ); + return( NULL ); + } + + return( im2 ); +} + +/* Delayed save: if we write to TIFF or to JPEG format, actually do the write + * to a "p" and on evalend do im_vips2tiff() or whatever. Track save + * parameters here. + */ +typedef struct { + int (*save_fn)(); /* Save function */ + IMAGE *im; /* Image to save */ + char *filename; /* Save args */ +} SaveBlock; + +/* From evalend callback: invoke a delayed save. + */ +static int +invoke_sb( SaveBlock *sb ) +{ + if( sb->save_fn( sb->im, sb->filename ) ) + return( -1 ); + + return( 0 ); +} + +/* Attach a SaveBlock to an image. + */ +static int +attach_sb( IMAGE *out, int (*save_fn)(), const char *filename ) +{ + SaveBlock *sb = IM_NEW( out, SaveBlock ); + + if( !sb ) + return( -1 ); + sb->im = out; + sb->save_fn = save_fn; + sb->filename = im_strdup( out, filename ); + + if( im_add_evalend_callback( out, + (im_callback_fn) invoke_sb, (void *) sb, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Lazy open: init a descriptor from a filename (just read the header) but + * delay actually decoding pixels until the first call to a start function. + */ + +typedef int (*OpenLazyFn)( const char *filename, IMAGE *im ); + +/* What we track during a delayed open. + */ +typedef struct _OpenLazy { + char *filename; + + OpenLazyFn read_pixels; /* Read in pixels with this */ + IMAGE *lazy_im; /* Image we read to .. copy from this */ +} OpenLazy; + +/* Our start function ... do the lazy open, if necessary, and return a region + * on the new image. + */ +static void * +open_lazy_start( IMAGE *out, OpenLazy *lazy, void *dummy ) +{ + if( !lazy->lazy_im ) { + if( !(lazy->lazy_im = im_open_local( out, + "open_lazy_start", "p" )) || + lazy->read_pixels( lazy->filename, lazy->lazy_im ) ) { + IM_FREEF( im_close, lazy->lazy_im ); + return( NULL ); + } + } + + return( im_region_create( lazy->lazy_im ) ); +} + +/* Just copy. + */ +static int +open_lazy_generate( REGION *or, REGION *ir ) +{ + Rect *r = &or->valid; + + /* Ask for input we need. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* Attach output region to that. + */ + if( im_region_region( or, ir, r, r->left, r->top ) ) + return( -1 ); + + return( 0 ); +} + +/* Lazy open ... init the header with the first OpenLazyFn, delay actually + * decoding pixels with the second OpenLazyFn until the first generate(). + */ +static int +open_lazy( OpenLazyFn read_header, OpenLazyFn read_pixels, + const char *filename, IMAGE *out ) +{ + OpenLazy *lazy = IM_NEW( out, OpenLazy ); + + if( !lazy || + !(lazy->filename = im_strdup( out, filename )) ) + return( -1 ); + lazy->read_pixels = read_pixels; + lazy->lazy_im = NULL; + + if( read_header( filename, out ) ) + return( -1 ); + + if( im_generate( out, + open_lazy_start, open_lazy_generate, im_stop_one, + lazy, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static IMAGE * +open_sub( OpenLazyFn read_header, OpenLazyFn read_pixels, const char *filename ) +{ + IMAGE *im; + + if( !(im = im_open( filename, "p" )) || + open_lazy( read_header, read_pixels, filename, im ) ) { + im_close( im ); + return( NULL ); + } + + return( im ); +} + +IMAGE * +im_open( const char *filename, const char *mode ) +{ + IMAGE *im; + + /* Pass in a nonsense name for argv0 ... this init world is only here + * for old programs which are missing an im_init_world() call. We must + * have threads set up before we can process. + */ + if( im_init_world( "vips" ) ) + im_error_clear(); + + if( !filename || !mode ) { + im_error( "im_open", _( "NULL filename or mode" ) ); + return( NULL ); + } + + switch( mode[0] ) { + case 'r': +{ + char name[FILENAME_MAX]; + char options[FILENAME_MAX]; + + /* Break any options off the name ... eg. "fred.tif:jpeg,tile" + * etc. + */ + im_filename_split( filename, name, options ); + + /* Check for other formats. + + FIXME ... should have a table to avoid all this + repetition + + */ + if( !im_existsf( "%s", name ) ) { + im_error( "im_open", + _( "\"%s\" is not readable" ), name ); + return( NULL ); + } + else if( im_istiff( name ) ) { + /* If TIFF open fails, try going through libmagick. + */ + if( !(im = open_sub( + im_tiff2vips_header, im_tiff2vips, + filename )) && + !(im = open_sub( + im_magick2vips_header, im_magick2vips, + filename )) ) + return( NULL ); + } + else if( im_isjpeg( name ) ) { + if( !(im = open_sub( + im_jpeg2vips_header, im_jpeg2vips, filename )) ) + return( NULL ); + } + else if( im_isexr( name ) ) { + if( !(im = open_sub( + im_exr2vips_header, im_exr2vips, filename )) ) + return( NULL ); + } + else if( im_isppm( name ) ) { + if( !(im = open_sub( + im_ppm2vips_header, im_ppm2vips, filename )) ) + return( NULL ); + } + else if( im_ispng( name ) ) { + if( !(im = open_sub( + im_png2vips_header, im_png2vips, filename )) ) + return( NULL ); + } + else if( im_filename_suffix_match( name, im_suffix_csv ) ) { + if( !(im = open_sub( + im_csv2vips_header, im_csv2vips, filename )) ) + return( NULL ); + } + else if( im_isvips( name ) ) { + if( mode[1] == 'w' ) { + /* Has to be native format for >8 bits. + */ + if( !(im = im_init( filename )) ) + return( NULL ); + if( im_openinrw( im ) ) { + im_close( im ); + return( NULL ); + } + if( im->Bbits != IM_BBITS_BYTE && + im_isMSBfirst( im ) != + im_amiMSBfirst() ) { + im_close( im ); + im_error( "im_open", _( "open for read-" + "write for native format " + "images only" ) ); + return( NULL ); + } + } + else + im = read_vips( filename ); + } + else if( im_isanalyze( name ) ) { + if( !(im = open_sub( + im_analyze2vips_header, im_analyze2vips, + filename )) ) + return( NULL ); + } + else if( im_ismagick( name ) ) { + /* Have this last as it can be very slow to detect + * failure. + */ + if( !(im = open_sub( + im_magick2vips_header, im_magick2vips, + filename )) ) + return( NULL ); + } + else { + im_error( "im_open", _( "\"%s\" is not " + "a supported format" ), filename ); + return( NULL ); + } +} + + break; + + case 'w': + /* Look at the suffix for format write. + */ + if( im_filename_suffix_match( filename, im_suffix_vips ) ) + im = im_openout( filename ); + else if( im_filename_suffix_match( filename, + im_suffix_tiff ) ) { + /* TIFF write. Save to a partial, and on evalend + * im_vips2tiff from that. + */ + if( !(im = im_open( "im_open:vips2tiff:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2tiff, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else if( im_filename_suffix_match( filename, + im_suffix_jpeg ) ) { + /* JPEG write. + */ + if( !(im = im_open( "im_open:vips2jpeg:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2jpeg, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else if( im_filename_suffix_match( filename, im_suffix_ppm ) ) { + /* ppm write. + */ + if( !(im = im_open( "im_open:vips2ppm:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2ppm, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else if( im_filename_suffix_match( filename, im_suffix_png ) ) { + /* png write. + */ + if( !(im = im_open( "im_open:vips2png:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2png, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else if( im_filename_suffix_match( filename, im_suffix_csv ) ) { + /* csv write. + */ + if( !(im = im_open( "im_open:vips2csv:1", "p" )) ) + return( NULL ); + if( attach_sb( im, im_vips2csv, filename ) ) { + im_close( im ); + return( NULL ); + } + } + else { + im_error( "im_open", _( "unknown suffix for save" ) ); + return( NULL ); + } + break; + + case 't': + im = im_setbuf( filename ); + break; + + case 'p': + im = im_partial( filename ); + break; + + default: + /* Getting here means bad mode + */ + im_error( "im_open", _( "bad mode \"%s\"" ), mode ); + return( NULL ); + } + +#ifdef DEBUG_IO + printf( "im_open: success for %s (%p)\n", im->filename, im ); +#endif /*DEBUG_IO*/ + + return( im ); +} + +/* Test an image for sanity. We could add many more tests here. + */ +static const char * +image_sanity( IMAGE *im ) +{ + if( !im ) + return( "NULL descriptor" ); + if( !im->filename ) + return( "NULL filename" ); + if( !g_slist_find( im__open_images, im ) ) + return( "not on open image list" ); + + if( im->Xsize <= 0 || im->Ysize <= 0 || im->Bands <= 0 ) + return( "bad dimensions" ); + if( im->BandFmt < -1 || im->BandFmt > IM_BANDFMT_DPCOMPLEX || + (im->Coding != -1 && + im->Coding != IM_CODING_NONE && + im->Coding != IM_CODING_LABQ) || + im->Type < -1 || im->Type > IM_TYPE_GREY16 || + im->dtype > IM_PARTIAL || im->dhint > IM_ANY ) + return( "bad enum value" ); + if( im->Xres < 0 || im->Xres < 0 ) + return( "bad resolution" ); + + return( NULL ); +} + +int +im_image_sanity( IMAGE *im ) +{ + const char *msg; + + if( (msg = image_sanity( im )) ) { + im_warn( "im_image_sanity", "\"%s\" (%p) %s", + im ? (im->filename ? im->filename : "") : "", + im, msg ); + im_printdesc( im ); + } + + return( 0 ); +} + +/* Just here for compatibility. + */ +IMAGE * +im_open_header( const char *file ) +{ + return( im_open( file, "r" ) ); +} diff --git a/libsrc/iofuncs/im_openin.c b/libsrc/iofuncs/im_openin.c new file mode 100644 index 00000000..1a948015 --- /dev/null +++ b/libsrc/iofuncs/im_openin.c @@ -0,0 +1,293 @@ +/* @(#) Open a VIPS image file for reading. The IMAGE should be as made by + * @(#) im_init(), or after im__close. + * @(#) + * @(#) If the file is small, we mmap() it + * @(#) and make an image of type IM_MMAP. If the file is large, we don't + * @(#) mmap(); instead we just open a file descriptor and wait for any regions + * @(#) defined on the image to make small mmap() windows. Also read the + * @(#) history. + * @(#) + * @(#) int + * @(#) im_openin( IMAGE *image ) + * @(#) + * @(#) As above, but always mmap() the whole file, and do it read/write. + * @(#) + * @(#) int + * @(#) im_openinrw( IMAGE *image ) + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 27/02/1991 + * 17/6/92 J.Cupitt + * - Now opens read-write if possible. This allows later calls to + * im_makerw. We mmap with PROT_READ, so there is no danger of + * scribbling over owned images. + * 16/4/93 J.Cupitt + * - adapted to use type field + * 10/5/93 J.Cupitt + * - split into im__mmapin() and im_mmapin() for im_openout() convenience + * - functions of im_mmapin.c and im_mmapinrw.c combined + * 7/9/93 JC + * - now sets dhint field + * 17/11/94 JC + * - checks length of compressed files too + * 19/8/98 JC + * - uses strerror() to print system error messages + * 28/10/98 JC + * - _INTEL and _SPARC auto byte-swap added + * 6/8/02 JC + * - redone for mmap() window stuff + * 13/3/06 JC + * - don't abort load if we can't get the XML + * 16/8/06 + * - more O_BINARY nonsense to help cygwin + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#ifdef HAVE_IO_H +#include +#endif + +/* Try to make an O_BINARY ... sometimes need the leading '_'. + */ +#ifdef BINARY_OPEN +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#endif /*_O_BINARY*/ +#endif /*!O_BINARY*/ +#endif /*BINARY_OPEN*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* mmap() whole vs. window threshold ... an int, so we can tune easily from a + * debugger. + */ +#ifdef DEBUG +int im__mmap_limit = 1; +#else +int im__mmap_limit = IM__MMAP_LIMIT; +#endif /*DEBUG*/ + +/* Sort of open for read for image files. + */ +int +im__open_image_file( const char *filename ) +{ + int fd; + + /* Try to open read-write, so that calls to im_makerw() will work. + * When we later mmap this file, we set read-only, so there + * is little danger of scrubbing over files we own. + */ +#ifdef BINARY_OPEN + if( (fd = open( filename, O_RDWR | O_BINARY )) == -1 ) { +#else /*BINARY_OPEN*/ + if( (fd = open( filename, O_RDWR )) == -1 ) { +#endif /*BINARY_OPEN*/ + /* Open read-write failed. Fall back to open read-only. + */ +#ifdef BINARY_OPEN + if( (fd = open( filename, O_RDONLY | O_BINARY )) == -1 ) { +#else /*BINARY_OPEN*/ + if( (fd = open( filename, O_RDONLY )) == -1 ) { +#endif /*BINARY_OPEN*/ + im_error( "im__open_image_file", + _( "unable to open \"%s\", %s" ), + filename, strerror( errno ) ); + return( -1 ); + } + } + + return( fd ); +} + +/* Predict the size of the header plus pixel data. Don't use off_t, + * it's sometimes only 32 bits (eg. on many windows build environments) and we + * want to always be 64 bit. + */ +gint64 +im__image_pixel_length( IMAGE *im ) +{ + gint64 psize; + + switch( im->Coding ) { + case IM_CODING_LABQ: + case IM_CODING_NONE: + psize = (gint64) IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize; + break; + + default: + psize = im->Length; + break; + } + + return( psize + im->sizeof_header ); +} + +/* Open the filename, read the header, some sanity checking. + */ +int +im__read_header( IMAGE *image ) +{ + /* We don't use im->sizeof_header here, but we know we're reading a + * VIPS image anyway. + */ + unsigned char header[IM_SIZEOF_HEADER]; + + gint64 length; + gint64 psize; + + image->dtype = IM_OPENIN; + if( (image->fd = im__open_image_file( image->filename )) == -1 ) + return( -1 ); + if( read( image->fd, header, IM_SIZEOF_HEADER ) != IM_SIZEOF_HEADER || + im__read_header_bytes( image, header ) ) { + im_error( "im_openin", + _( "unable to read header for \"%s\", %s" ), + image->filename, strerror( errno ) ); + return( -1 ); + } + + /* Predict and check the file size. + */ + psize = im__image_pixel_length( image ); + if( (length = im_file_length( image->fd )) == -1 ) + return( -1 ); + if( psize > length ) { + im_error( "im_openin", _( "unable to open \"%s\", %s" ), + image->filename, _( "file has been truncated" ) ); + return( -1 ); + } + + /* Set demand style. Allow the most permissive sort. + */ + image->dhint = IM_THINSTRIP; + + /* Set the history part of im descriptor. Don't return an error if this + * fails (due to eg. corrupted XML) because it's probably mostly + * harmless. + */ + if( im__readhist( image ) ) { + im_warn( "im_openin", _( "error reading XML: %s" ), + im_error_buffer() ); + im_error_clear(); + } + + return( 0 ); +} + +/* Open, then mmap() small images, leave large images to have a rolling mmap() + * window for each region. + */ +int +im_openin( IMAGE *image ) +{ + gint64 size; + +#ifdef DEBUG + char *str; + + if( (str = g_getenv( "IM_MMAP_LIMIT" )) ) { + im__mmap_limit = atoi( str ); + printf( "im_openin: setting maplimit to %d from environment\n", + im__mmap_limit ); + } +#endif /*DEBUG*/ + + if( im__read_header( image ) ) + return( -1 ); + + size = (gint64) IM_IMAGE_SIZEOF_LINE( image ) * image->Ysize + + image->sizeof_header; + if( size < im__mmap_limit ) { + if( im_mapfile( image ) ) + return( -1 ); + image->data = image->baseaddr + image->sizeof_header; + image->dtype = IM_MMAPIN; + +#ifdef DEBUG + printf( "im_openin: completely mmap()ing \"%s\": it's small\n", + image->filename ); +#endif /*DEBUG*/ + } + else { +#ifdef DEBUG + printf( "im_openin: delaying mmap() of \"%s\": it's big!\n", + image->filename ); +#endif /*DEBUG*/ + } + + return( 0 ); +} + +/* Open, then mmap() read/write. + */ +int +im_openinrw( IMAGE *image ) +{ + if( im__read_header( image ) ) + return( -1 ); + + if( im_mapfilerw( image ) ) + return( -1 ); + image->data = image->baseaddr + image->sizeof_header; + image->dtype = IM_MMAPINRW; + +#ifdef DEBUG + printf( "im_openin: completely mmap()ing \"%s\" read-write\n", + image->filename ); +#endif /*DEBUG*/ + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_openout.c b/libsrc/iofuncs/im_openout.c new file mode 100644 index 00000000..ba646e35 --- /dev/null +++ b/libsrc/iofuncs/im_openout.c @@ -0,0 +1,75 @@ +/* @(#) im_openout: associates an IMAGE with an image file for output + * @(#) IMAGE should be closed with im_close() + * @(#) Usage: + * @(#) IMAGE *im_openout(file_name) + * @(#) char *file_name; + * @(#) + * @(#) Returns *IMAGE or NULL on error. + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 26/04/1990 by KM + * 16/4/93 JC + * - uses new init, type style + * - memory leak fixed + * 11/5/93 JC + * - newer im_init() style + * 23/10/98 JC + * - new BINARY_OPEN define + * 4/7/01 JC + * - delay open() until im_setupout() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_openout( const char *file_name ) +{ + IMAGE *image; + + if( !(image = im_init( file_name )) ) + return( NULL ); + image->dtype = IM_OPENOUT; + + return( image ); +} diff --git a/libsrc/iofuncs/im_partial.c b/libsrc/iofuncs/im_partial.c new file mode 100644 index 00000000..d0492abc --- /dev/null +++ b/libsrc/iofuncs/im_partial.c @@ -0,0 +1,64 @@ +/* @(#) im_partial: initialise a partial IMAGE. Just set im->dtype. + * @(#) + * @(#) IMAGE * + * @(#) im_partial( file_name ) + * @(#) char *file_name; + * @(#) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_partial( const char *filename ) +{ + IMAGE *im = im_init( filename ); + + if( !im ) + return(NULL); + im->dtype = IM_PARTIAL; + + /* No need to set demand style - im_demand_hint() from application + * will do this. + */ + + return( im ); +} diff --git a/libsrc/iofuncs/im_piocheck.c b/libsrc/iofuncs/im_piocheck.c new file mode 100644 index 00000000..6e94c35d --- /dev/null +++ b/libsrc/iofuncs/im_piocheck.c @@ -0,0 +1,197 @@ +/* @(#) Function which checks the structures imagein and imageout and gets + * @(#) ready for partial input. If a function calls this in its setup stage, + * @(#) we assume it is partial-ready. If it calls im_iocheck(), we fall back + * @(#) to old-style behaviour. + * @(#) + * @(#) int + * @(#) im_piocheck( imagein, imageout ) + * @(#) IMAGE *imagein, *imageout; + * @(#) + * @(#) int + * @(#) im_pincheck( imagein ) + * @(#) IMAGE *imagein; + * @(#) + * @(#) int + * @(#) im_piocheck( imageout ) + * @(#) IMAGE *imageout; + * @(#) + * @(#) Returns -1 on fail + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 12/02/1990 + * Modified on : + * 15/4/93 J.Cupitt + * - im_incheck(), im_outcheck() added. + * - type field now checked. + * 10/6/93 J.Cupitt + * - im_iocheck() adapted to make im_piocheck() + * - auto-rewind feature added + * 27/10/95 JC + * - im_pincheck() on a setbuf now zaps generate function so as not to + * confuse any later calls to im_prepare() or im_prepare_inplace() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Check that an image is readable. + */ +int +im_pincheck( IMAGE *im ) +{ + if( im_image_sanity( im ) ) + return( -1 ); + +#ifdef DEBUG_IO + printf( "im_pincheck: enabling partial input for %s\n", im->filename ); +#endif /*DEBUG_IO*/ + + switch( im->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + /* Should have been written to. + */ + if( !im->data ) { + im_errormsg( "im_pincheck: no image data" ); + return( -1 ); + } + + /* Should be no generate functions now. + */ + im->start = NULL; + im->generate = NULL; + im->stop = NULL; + + break; + + case IM_PARTIAL: + /* Should have had generate functions attached. + */ + if( !im->generate ) { + im_errormsg( "im_pincheck: no image data" ); + return( -1 ); + } + + break; + + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENIN: + break; + + case IM_OPENOUT: + /* Close file down and reopen as im_mmapin. + */ +#ifdef DEBUG_IO + printf( "im_pincheck: auto-rewind of %s\n", im->filename ); +#endif/*DEBUG_IO*/ + if( im__close( im ) || im_openin( im ) ) { + im_errormsg( "im_pincheck: auto-rewind for %s failed", + im->filename ); + return( -1 ); + } + + break; + + default: + im_errormsg( "im_pincheck: image not readable" ); + return( -1 ); + } + + return( 0 ); +} + +/* Check that an image is writeable. + */ +int +im_poutcheck( IMAGE *im ) +{ + if( !im ) { + im_errormsg( "im_poutcheck: null image descriptor" ); + return( -1 ); + } + +#ifdef DEBUG_IO + printf( "im_pincheck: enabling partial output for %s\n", im->filename ); +#endif /*DEBUG_IO*/ + + switch( im->dtype ) { + case IM_SETBUF: + /* Check that it has not been im_setupout(). + */ + if( im->data ) { + im_errormsg( "im_poutcheck: image already written" ); + return( -1 ); + } + + break; + + case IM_PARTIAL: + /* Make sure nothing is attached. + */ + if( im->generate ) { + im_errormsg( "im_poutcheck: image already written" ); + return( -1 ); + } + + break; + + case IM_OPENOUT: + case IM_SETBUF_FOREIGN: + /* Okeydoke. Not much checking here. + */ + break; + + default: + im_errormsg( "im_poutcheck: image not writeable" ); + return( -1 ); + } + + return( 0 ); +} + +/* Check a pair of fds for IO. + */ +int +im_piocheck( IMAGE *in, IMAGE *out ) +{ + return( im_pincheck( in ) || im_poutcheck( out ) ); +} diff --git a/libsrc/iofuncs/im_prepare.c b/libsrc/iofuncs/im_prepare.c new file mode 100644 index 00000000..6871eb56 --- /dev/null +++ b/libsrc/iofuncs/im_prepare.c @@ -0,0 +1,384 @@ +/* Request an area of an image for input. + * + * J.Cupitt, 17/4/93. + * + * Modified: + * 22/11/94 JC + * - check for start and stop functions removed, as can now be NULL + * 22/2/95 JC + * - im_fill_copy() added + * 18/4/95 JC + * - kill flag added for compute cases + * 27/10/95 JC + * - im_fill_copy() now only uses im_generate() mechanism if it has to + * - im_fill_copy() renamed as im_prepare_inplace() + * 18/8/99 JC + * - oops ... cache stuff removed, interacted badly with inplace ops + * 26/3/02 JC + * - better error message for im_prepare() + * 9/8/02 JC + * - im_prepare_inplace() broke with mmap() windows ... im_prepare_to() + * replaces it and is nicer + * 30/9/05 + * - hmm, did not stop if a start function failed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Generate into a region. + */ +static int +fill_region( REGION *reg ) +{ + IMAGE *im = reg->im; + + /* Start new sequence, if necessary. + */ + if( im__call_start( reg ) ) + return( -1 ); + + /* Ask for evaluation. + */ + if( im->generate( reg, reg->seq, im->client1, im->client2 ) ) + return( -1 ); + + return( 0 ); +} + +int +im__test_kill( IMAGE *im ) +{ + /* Has kill been set for this image? If yes, abort evaluation. + */ + if( im->kill ) { + im_error( "im__test_kill", _( "killed for image \"%s\"" ), + im->filename ); + return( -1 ); + } + + return( 0 ); +} + +/* Make REGION reg ready for input of area r. For most image types, we can + * just im_attach, for PARTIAL though we may need to generate. + */ +int +im_prepare( REGION *reg, Rect *r ) +{ + IMAGE *im = reg->im; + + Rect save = *r; + + im__region_check_ownership( reg ); + + if( im__test_kill( im ) ) + return( -1 ); + + /* We use save for sanity checking valid: we test at the end that the + * pixels we have generated are indeed all the ones that were asked + * for. + * + * However, r may be clipped by the image size, so we need to clip + * save as well to make sure we don't fail the assert due to that. + */ +{ + Rect image; + + image.left = 0; + image.top = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( &save, &image, &save ); +} + +#ifdef DEBUG + printf( "im_prepare: left = %d, top = %d, width = %d, height = %d\n", + r->left, r->top, r->width, r->height ); +#endif /*DEBUG*/ + + switch( im->dtype ) { + case IM_PARTIAL: + if( im_region_fill( reg, r, + (im_region_fill_fn) fill_region, NULL ) ) + return( -1 ); + + break; + + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENIN: + /* Attach to existing buffer. + */ + if( im_region_image( reg, r ) ) + return( -1 ); + + break; + + default: + im_error( "im_prepare", _( "unable to input from a %s image" ), + im_dtype2char( im->dtype ) ); + return( -1 ); + } + + /* valid should now include all the pixels that were asked for. + */ + assert( im_rect_includesrect( ®->valid, &save ) ); + + return( 0 ); +} + +/* Copy from one region to another. Copy area r from inside reg to dest, + * positioning the area of pixels at x, y. + */ +void +im__copy_region( REGION *reg, REGION *dest, Rect *r, int x, int y ) +{ + int z; + int len = IM_IMAGE_SIZEOF_PEL( reg->im ) * r->width; + char *p = IM_REGION_ADDR( reg, r->left, r->top ); + char *q = IM_REGION_ADDR( dest, x, y ); + int plsk = IM_REGION_LSKIP( reg ); + int qlsk = IM_REGION_LSKIP( dest ); + +#ifdef DEBUG + /* Find the area we will write to in dest. + */ + Rect output; + + printf( "im__copy_region: sanity check\n" ); + + output.left = x; + output.top = y; + output.width = r->width; + output.height = r->height; + + /* Must be inside dest->valid. + */ + assert( im_rect_includesrect( &dest->valid, &output ) ); + + /* Check the area we are reading from in reg. + */ + assert( im_rect_includesrect( ®->valid, r ) ); +#endif /*DEBUG*/ + + for( z = 0; z < r->height; z++ ) { + memcpy( q, p, len ); + + p += plsk; + q += qlsk; + } +} + +/* We need to make pixels using reg's generate function, and write the result + * to dest. + */ +static int +im_prepare_to_generate( REGION *reg, REGION *dest, Rect *r, int x, int y ) +{ + IMAGE *im = reg->im; + char *p; + + if( !im->generate ) { + im_error( "im_prepare_to", _( "incomplete header" ) ); + return( -1 ); + } + + if( im_region_region( reg, dest, r, x, y ) ) + return( -1 ); + + /* Remember where reg is pointing now. + */ + p = IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ); + + /* Run sequence into reg. + */ + if( fill_region( reg ) ) + return( -1 ); + + /* The generate function may not have actually made any pixels ... it + * might just have redirected reg to point somewhere else. If it has, + * we need an extra copy operation. + */ + if( IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ) != p ) + im__copy_region( reg, dest, r, x, y ); + + return( 0 ); +} + +/* Like im_prepare(): fill reg with data, ready to be read from by our caller. + * Unlike im_prepare(), rather than allocating memory local to reg for the + * result, we guarantee that we will fill the pixels in dest at offset x, y. + * In other words, we generate an extra copy operation if necessary. + */ +int +im_prepare_to( REGION *reg, REGION *dest, Rect *r, int x, int y ) +{ + IMAGE *im = reg->im; + Rect image; + Rect wanted; + Rect clipped; + Rect clipped2; + Rect final; + + if( im__test_kill( im ) ) + return( -1 ); + + /* Sanity check. + */ + if( !dest->data || dest->im->BandFmt != reg->im->BandFmt || + dest->im->Bands != reg->im->Bands ) { + im_error( "im_prepare_to", _( "inappropriate region type" ) ); + return( -1 ); + } + + /* clip r first against the size of reg->im, then again against the + * memory we have available to write to on dest. Just like + * im_region_region() + */ + image.top = 0; + image.left = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + assert( clipped.left == r->left ); + assert( clipped.top == r->top ); + + wanted.left = x + (clipped.left - r->left); + wanted.top = y + (clipped.top - r->top); + wanted.width = clipped.width; + wanted.height = clipped.height; + + /* Test that dest->valid is large enough. + */ + if( !im_rect_includesrect( &dest->valid, &wanted ) ) { + im_error( "im_prepare_to", _( "dest too small" ) ); + return( -1 ); + } + + im_rect_intersectrect( &wanted, &dest->valid, &clipped2 ); + + /* Translate back to reg's coordinate space and set as valid. + */ + final.left = r->left + (clipped2.left - wanted.left); + final.top = r->top + (clipped2.top - wanted.top); + final.width = clipped2.width; + final.height = clipped2.height; + + x = clipped2.left; + y = clipped2.top; + + if( im_rect_isempty( &final ) ) { + im_error( "im_prepare_to", _( "valid clipped to nothing" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "im_prepare_to: left = %d, top = %d, width = %d, height = %d\n", + final.left, final.top, final.width, final.height ); +#endif /*DEBUG*/ + + /* Input or output image type? + */ + switch( im->dtype ) { + case IM_OPENOUT: + case IM_PARTIAL: + /* We are generating with a sequence. + */ + if( im_prepare_to_generate( reg, dest, &final, x, y ) ) + return( -1 ); + + break; + + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENIN: + /* Attach to existing buffer and copy to dest. + */ + if( im_region_image( reg, &final ) ) + return( -1 ); + im__copy_region( reg, dest, &final, x, y ); + + break; + + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + /* Could be either input or output. If there is a generate + * function, we are outputting. + */ + if( im->generate ) { + if( im_prepare_to_generate( reg, dest, &final, x, y ) ) + return( -1 ); + } + else { + if( im_region_image( reg, &final ) ) + return( -1 ); + im__copy_region( reg, dest, &final, x, y ); + } + + break; + + default: + im_error( "im_prepare_to", _( "unable to input from a " + "%s image" ), im_dtype2char( im->dtype ) ); + return( -1 ); + } + + return( 0 ); +} + +int +im_prepare_many( REGION **reg, Rect *r ) +{ + for( ; *reg; ++reg ) + if( im_prepare( *reg, r ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_printdesc.c b/libsrc/iofuncs/im_printdesc.c new file mode 100644 index 00000000..d865113b --- /dev/null +++ b/libsrc/iofuncs/im_printdesc.c @@ -0,0 +1,346 @@ +/* @(#) display VIPS IMAGE descriptor for debugging + * @(#) a valid IMAGE descriptor is needed + * @(#) HANDLESIMAGE + * @(#) Static strings must be modified if header files are changed + * @(#) void im_printdesc(image) + * @(#) IMAGE *image; + * + * Copyright: Nicos Dessipris + * Written on: 15/01/1990 + * Modified on : 3/6/92 Kirk Martinez + * 15/4/93 JC + * - partial regions dumped too + * - resolution bug fixed + * 11/5/93 JC + * - formatting changed for ctags + * 1/7/93 JC + * - adapted for partial v2 + * 15/11/94 JC + * - new Coding types added + * - new Type values added + * - new Compression type added + * 2/3/98 JC + * - IM_ANY added + * 5/11/98 JC + * - prints byte-order too + * 1/8/05 + * - prints meta header fields too + * 9/9/05 + * - oops, shouldn't print filename ourselves + * 4/1/07 + * - use new im_history_get() thing + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static const char *im_Type[] = { + "IM_TYPE_MULTIBAND", /* 0 */ + "IM_TYPE_B_W", /* 1 */ + "LUMINACE", /* 2 */ + "XRAY", /* 3 */ + "IR", /* 4 */ + "YUV", /* 5 */ + "RED_ONLY", /* 6 */ + "GREEN_ONLY", /* 7 */ + "BLUE_ONLY", /* 8 */ + "POWER_SPECTRUM", /* 9 */ + "IM_TYPE_HISTOGRAM", /* 10 */ + "LUT", /* 11 */ + "IM_TYPE_XYZ", /* 12 */ + "IM_TYPE_LAB", /* 13 */ + "CMC", /* 14 */ + "IM_TYPE_CMYK", /* 15 */ + "IM_TYPE_LABQ", /* 15 */ + "IM_TYPE_RGB", /* 17 */ + "IM_TYPE_UCS", /* 18 */ + "IM_TYPE_LCH", /* 19 */ + "IM_TYPE_LABS", /* 20 */ + "", /* 21 */ + "IM_TYPE_sRGB", /* 22 */ + "IM_TYPE_YXY", /* 23 */ + "IM_TYPE_FOURIER", /* 24 */ + "IM_TYPE_RGB16", /* 25 */ + "IM_TYPE_GREY16" /* 26 */ +}; + +static const char *im_BandFmt[] = { + "IM_BANDFMT_UCHAR", + "IM_BANDFMT_CHAR", + "IM_BANDFMT_USHORT", + "IM_BANDFMT_SHORT", + "IM_BANDFMT_UINT", + "IM_BANDFMT_INT", + "IM_BANDFMT_FLOAT", + "IM_BANDFMT_COMPLEX", + "IM_BANDFMT_DOUBLE", + "IM_BANDFMT_DPCOMPLEX" +}; + +static const char *im_Coding[] = { + "IM_CODING_NONE", + "COLQUANT8", + "IM_CODING_LABQ", + "IM_CODING_LABQ_COMPRESSED" +}; + +static const char *im_Compression[] = { + "NO_COMPRESSION", + "TCSF_COMPRESSION", + "JPEG_COMPRESSION" +}; + +static const char *im_dtype[] = { + "IM_NONE", + "IM_SETBUF", + "IM_SETBUF_FOREIGN", + "IM_OPENIN", + "IM_MMAPIN", + "IM_MMAPINRW", + "IM_OPENOUT", + "IM_PARTIAL" +}; + +static const char *im_dhint[] = { + "IM_SMALLTILE", + "IM_FATSTRIP", + "IM_THINSTRIP", + "IM_ANY" +}; + +/* Stuff to decode an enum. + */ +typedef struct _EnumTable { + const char *error; /* eg. "" */ + const char **names; /* eg. {"IM_CODING_NONE",..} */ + int nnames; +} EnumTable; + +static EnumTable enumType = { + N_( "" ), + im_Type, + IM_NUMBER( im_Type ) +}; + +static EnumTable enumBandFmt = { + N_( "" ), + im_BandFmt, + IM_NUMBER( im_BandFmt ) +}; + +static EnumTable enumCoding = { + N_( "" ), + im_Coding, + IM_NUMBER( im_Coding ) +}; + +static EnumTable enumCompression = { + N_( "" ), + im_Compression, + IM_NUMBER( im_Compression ) +}; + +static EnumTable enumdtype = { + N_( "" ), + im_dtype, + IM_NUMBER( im_dtype ) +}; + +static EnumTable enumdhint = { + N_( "" ), + im_dhint, + IM_NUMBER( im_dhint ) +}; + +static const char * +enum2char( EnumTable *etable, int n ) +{ + if( n < 0 || n > etable->nnames ) + return( _( etable->error ) ); + else + return( etable->names[n] ); +} + +static int +char2enum( EnumTable *etable, const char *name ) +{ + int i; + + for( i = 0; i < etable->nnames; i++ ) + if( g_ascii_strcasecmp( etable->names[i], name ) == 0 ) + return( i ); + + im_error( "char2enum", _( etable->error ) ); + + return( -1 ); +} + + +/* Prettyprint various header fields. + */ +const char *im_Type2char( int n ) + { return( enum2char( &enumType, n ) ); } +const char *im_BandFmt2char( int n ) + { return( enum2char( &enumBandFmt, n ) ); } +const char *im_Coding2char( int n ) + { return( enum2char( &enumCoding, n ) ); } +const char *im_Compression2char( int n ) + { return( enum2char( &enumCompression, n ) ); } +const char *im_dtype2char( im_desc_type n ) + { return( enum2char( &enumdtype, n ) ); } +const char *im_dhint2char( im_demand_type n ) + { return( enum2char( &enumdhint, n ) ); } + +int im_char2Type( const char *str ) + { return( char2enum( &enumType, str ) ); } +int im_char2BandFmt( const char *str ) + { return( char2enum( &enumBandFmt, str ) ); } +int im_char2Coding( const char *str ) + { return( char2enum( &enumCoding, str ) ); } +int im_char2Compression( const char *str ) + { return( char2enum( &enumCompression, str ) ); } +im_desc_type im_char2dtype( const char *str ) + { return( char2enum( &enumdtype, str ) ); } +im_demand_type im_char2dhint( const char *str ) + { return( char2enum( &enumdhint, str ) ); } + +static void * +print_field_fn( IMAGE *im, const char *field, GValue *value ) +{ + const char *extra; + char *str_value; + + str_value = g_strdup_value_contents( value ); + printf( "%s: %s", field, str_value ); + g_free( str_value ); + + /* Replace NULL static strings with "(null)". + */ +#define NN( X ) ((X) ? (X) : "(null)") + + /* Look for known enums and decode them. + */ + extra = NULL; + if( strcmp( field, "Coding" ) == 0 ) + extra = NN( im_Coding2char( g_value_get_int( value ) ) ); + else if( strcmp( field, "BandFmt" ) == 0 ) + extra = NN( im_BandFmt2char( g_value_get_int( value ) ) ); + else if( strcmp( field, "Type" ) == 0 ) + extra = NN( im_Type2char( g_value_get_int( value ) ) ); + else if( strcmp( field, "Compression" ) == 0 ) + extra = NN( im_Compression2char( g_value_get_int( value ) ) ); + + if( extra ) + printf( " - %s", extra ); + + printf( "\n" ); + + return( NULL ); +} + +static void * +print_region( REGION *reg, void *a, void *b ) +{ + printf( "Region defined for area at %dx%d size %dx%d\n", + reg->valid.left, reg->valid.top, + reg->valid.width, reg->valid.height ); + if( reg->seq ) + printf( "sequence in progress on region\n" ); + if( reg->buffer ) + printf( "local memory allocated\n" ); + + return( NULL ); +} + +void +im_printdesc( IMAGE *image ) +{ + if( !image ) { + printf( "NULL descriptor\n" ); + return; + } + + printf( "IMAGE* %p\n", image ); + + if( im_isMSBfirst( image ) ) + printf( "SPARC (MSB first) " ); + else + printf( "Intel (LSB first) " ); + printf( "byte order image, on a " ); + if( im_amiMSBfirst() ) + printf( "SPARC (MSB first) " ); + else + printf( "Intel (LSB first) " ); + printf( "byte order machine\n" ); + + (void) im_header_map( image, (im_header_map_fn) print_field_fn, NULL ); + + printf( "Hist: %s", im_history_get( image ) ); + + /* Print other (non-header) fields. + */ + if( image->generate ) + printf( "user generate function attached\n" ); + if( image->closefns ) + printf( "user close callbacks attached\n" ); + if( image->evalfns ) + printf( "user eval callbacks attached\n" ); + if( image->evalendfns ) + printf( "user evalend callbacks attached\n" ); + if( image->regions ) { + printf( "%d regions present\n", + g_slist_length( image->regions ) ); + im_slist_map2( image->regions, + (VSListMap2Fn) print_region, NULL, NULL ); + } + if( image->kill ) + printf( "kill flag set\n" ); + if( image->closing ) + printf( "closing flag set\n" ); + if( image->close_pending ) + printf( "close_pending flag set\n" ); + +#ifdef DEBUG + /* Can't get these with im_header_get(), so only show for debugging. + */ + printf( "dhint: %s\n", im_dhint2char( image->dhint ) ); + printf( "dtype: %s\n", im_dtype2char( image->dtype ) ); +#endif /*DEBUG*/ +} diff --git a/libsrc/iofuncs/im_printlines.c b/libsrc/iofuncs/im_printlines.c new file mode 100644 index 00000000..4f2c5caf --- /dev/null +++ b/libsrc/iofuncs/im_printlines.c @@ -0,0 +1,150 @@ +/* @(#) Function which prints in stdout the values of a picture + * @(#) + * @(#) For debuging only + * @(#) is either memory mapped or in a buffer. + * @(#) + * @(#) void im_printlines( in ) + * @(#) IMAGE *in; + * @(#) + * + * Copyright: 1991 N. Dessipris + * + * Author: N. Dessipris + * Written on: 18/03/1991 + * Modified on: + * 15/4/93 J.Cupitt + * - returns int, not void now, so error messages work + * - detects im->data invalid. + * 23/7/93 JC + * - im_incheck() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Useful: Call a macro with the name, type pairs for all VIPS functions. + */ +#define im_for_all_types() \ + case IM_BANDFMT_UCHAR: loopuc(unsigned char); break; \ + case IM_BANDFMT_CHAR: loop(char); break; \ + case IM_BANDFMT_USHORT: loop(unsigned short); break; \ + case IM_BANDFMT_SHORT: loop(short); break; \ + case IM_BANDFMT_UINT: loop(unsigned int); break; \ + case IM_BANDFMT_INT: loop(int); break; \ + case IM_BANDFMT_FLOAT: loop(float); break; \ + case IM_BANDFMT_DOUBLE: loop(double); break; \ + case IM_BANDFMT_COMPLEX: loopcmplx(float); break; \ + case IM_BANDFMT_DPCOMPLEX: loopcmplx(double); break; + +int +im_printlines( IMAGE *in ) +{ + if( im_incheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_errormsg( "im_printlines: input must be uncoded" ); + return( -1 ); + } + if( !in->data ) { + im_errormsg( "im_debugim: unsuitable image type" ); + return( -1 ); + } + +/* What type? First define the loop we want to perform for all types. */ +#define loopuc(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + fprintf(stderr, "line:%5d\n", y); \ + for ( x=0; xXsize; x++ ) {\ + fprintf(stderr, "%5d", x); \ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr, "\t%4d", (TYPE)*p++ );\ + } \ + fprintf(stderr, "\n"); \ + } \ + } \ + } + +#define loop(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + fprintf(stderr, "line:%5d\n", y); \ + for ( x=0; xXsize; x++ ) {\ + fprintf(stderr, "%5d", x); \ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr, "\t%f", (double)*p++ );\ + } \ + fprintf(stderr, "\n"); \ + } \ + } \ + } + +#define loopcmplx(TYPE) \ + { TYPE *p = (TYPE *) in->data; \ + int x, y, z; \ + \ + for ( y=0; yYsize; y++ ) {\ + fprintf(stderr, "line:%5d\n", y); \ + for ( x=0; xXsize; x++ ) {\ + fprintf(stderr, "%5d", x); \ + for ( z=0; zBands; z++ ) {\ + fprintf(stderr,"\t%f",(double)*p++);\ + fprintf(stderr,"\t%f",(double)*p++);\ + } \ + fprintf(stderr, "\n");\ + } \ + } \ + } + +/* Now generate code for all types. */ + switch( in->BandFmt ) { + im_for_all_types(); + default: { + im_errormsg( "im_printlines: unknown input format" ); + return( -1 ); + } + } + + return( 0 ); + +} diff --git a/libsrc/iofuncs/im_readhist.c b/libsrc/iofuncs/im_readhist.c new file mode 100644 index 00000000..4e29c985 --- /dev/null +++ b/libsrc/iofuncs/im_readhist.c @@ -0,0 +1,753 @@ +/* @(#) Reads one line of description and the history of the filename + * @(#) This is done by replacing the ending .v of the filename with .desc + * @(#) and trying to read the new file. If the file ending in .desc + * @(#) does exist it is read and put into the Hist pointer of the + * @(#) image descriptor + * @(#) If the .desc file does not exist or if the input file is not + * @(#) ending with .v the Hist pointer in initialised to "filename\n" + * @(#) and history is kept from the current processing stage. + * @(#) + * @(#) int im_readhist(image) + * @(#) IMAGE *image; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * Copyright: Nicos Dessipris + * Written on: 15/01/1990 + * Modified on : + * 28/10/92 JC + * - no more wild freeing! + * - behaves itself, thank you + * 13/1/94 JC + * - array-bounds write found and fixed + * 26/10/98 JC + * - binary open for stupid systems + * 24/9/01 JC + * - slight clean up + * 6/8/02 JC + * - another cleanup + * 11/7/05 + * - now read XML from after the image data rather than a separate + * annoying file + * - added im__writehist() to write XML to the image + * 5/10/05 + * - added wrappers for seek/truncate with native win32 calls for long + * file support + * 3/1/07 + * - set history_list instead + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif /*HAVE_SYS_FILE_H*/ +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef HAVE_IO_H +#include +#endif /*HAVE_IO_H*/ +#include +#include + +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our XML namespace. + */ +#define NAMESPACE "http://www.vips.ecs.soton.ac.uk/vips" + +/* Need our own seek(), since lseek() on win32 can't do long files. + */ +static int +im__seek( int fd, gint64 pos ) +{ +#ifdef OS_WIN32 +{ + HANDLE hFile = (HANDLE) _get_osfhandle( fd ); + LARGE_INTEGER p; + + p.QuadPart = pos; + if( !SetFilePointerEx( hFile, p, NULL, FILE_BEGIN ) ) { + im_error_system( GetLastError(), "im__seek", + _( "unable to seek" ) ); + return( -1 ); + } +} +#else /*!OS_WIN32*/ + if( lseek( fd, pos, SEEK_SET ) == (off_t) -1 ) { + im_error( "im__seek", _( "unable to seek" ) ); + return( -1 ); + } +#endif /*OS_WIN32*/ + + return( 0 ); +} + +/* Need our own ftruncate(), since ftruncate() on win32 can't do long files. + + DANGER ... this moves the file pointer to the end of file on win32, + but not on *nix; don't make any assumptions about the file pointer + position after calling this + + */ +static int +im__ftruncate( int fd, gint64 pos ) +{ +#ifdef OS_WIN32 +{ + HANDLE hFile = (HANDLE) _get_osfhandle( fd ); + LARGE_INTEGER p; + + p.QuadPart = pos; + if( im__seek( fd, pos ) ) + return( -1 ); + if( !SetEndOfFile( hFile ) ) { + im_error_system( GetLastError(), "im__ftruncate", + _( "unable to truncate" ) ); + return( -1 ); + } +} +#else /*!OS_WIN32*/ + if( ftruncate( fd, pos ) ) { + im_error_system( errno, "im__ftruncate", + _( "unable to truncate" ) ); + return( -1 ); + } +#endif /*OS_WIN32*/ + + return( 0 ); +} + +/* Read a chunk of an fd into memory. Add a '\0' at the end. + */ +static char * +read_chunk( int fd, gint64 offset, size_t length ) +{ + char *buf; + + if( im__seek( fd, offset ) ) + return( NULL ); + if( !(buf = im_malloc( NULL, length + 1 )) ) + return( NULL ); + if( read( fd, buf, length ) != length ) { + im_free( buf ); + im_error( "im_readhist", _( "unable to read history" ) ); + return( NULL ); + } + buf[length] = '\0'; + + return( buf ); +} + +/* Does it look like an image has an extension block? + */ +int +im__has_extension_block( IMAGE *im ) +{ + gint64 length; + gint64 psize; + + psize = im__image_pixel_length( im ); + if( (length = im_file_length( im->fd )) == -1 ) + return( 0 ); + + return( length - psize > 0 ); +} + +/* Read everything after the pixels into memory. + */ +void * +im__read_extension_block( IMAGE *im, int *size ) +{ + gint64 length; + gint64 psize; + void *buf; + + psize = im__image_pixel_length( im ); + if( (length = im_file_length( im->fd )) == -1 ) + return( NULL ); + if( length - psize > 10 * 1024 * 1024 ) { + im_error( "im_readhist", + _( "more than a 10 megabytes of XML? " + "sufferin' succotash!" ) ); + return( NULL ); + } + if( length - psize == 0 ) + return( NULL ); + if( !(buf = read_chunk( im->fd, psize, length - psize )) ) + return( NULL ); + if( size ) + *size = length - psize; + +#ifdef DEBUG + printf( "im__read_extension_block: read %d bytes from %s\n", + (int) (length - psize), im->filename ); + printf( "data: \"%s\"\n", (char *) buf ); +#endif /*DEBUG*/ + + return( buf ); +} + +/* Read everything after the pixels into memory. + + FIXME ... why can't we use xmlParserInputBufferCreateFd and parse + directly from the fd rather than having to read the stupid thing into + memory + + the libxml API docs are impossible to decipher + + */ +static xmlDoc * +read_xml( IMAGE *im ) +{ + void *buf; + int size; + xmlDoc *doc; + xmlNode *node; + + if( !(buf = im__read_extension_block( im, &size )) ) + return( NULL ); + if( !(doc = xmlParseMemory( buf, size )) ) { + im_free( buf ); + return( NULL ); + } + im_free( buf ); + if( !(node = xmlDocGetRootElement( doc )) || + !node->nsDef || + !im_isprefix( NAMESPACE, (char *) node->nsDef->href ) ) { + im_error( "im__readhist", _( "incorrect namespace in XML" ) ); + xmlFreeDoc( doc ); + return( NULL ); + } + +#ifdef DEBUG + printf( "read_xml: namespace == %s\n", node->nsDef->href ); +#endif /*DEBUG*/ + + return( doc ); +} + +/* Find the first child node with a name. + */ +static xmlNode * +get_node( xmlNode *base, const char *name ) +{ + xmlNode *i; + + for( i = base->children; i; i = i->next ) + if( strcmp( (char *) i->name, name ) == 0 ) + return( i ); + + return( NULL ); +} + +/* Read a string property to a buffer. TRUE for success. + */ +static int +get_sprop( xmlNode *xnode, const char *name, char *buf, int sz ) +{ + char *value = (char *) xmlGetProp( xnode, (xmlChar *) name ); + + if( !value ) + return( 0 ); + + im_strncpy( buf, value, sz ); + IM_FREEF( xmlFree, value ); + + return( 1 ); +} + +/* Chop history into lines, add each one as a refstring. + */ +static void +set_history( IMAGE *im, char *history ) +{ + GSList *history_list; + char *p, *q; + + /* There can be history there already if we're rewinding. + */ + IM_FREEF( im__gslist_gvalue_free, im->history_list ); + + history_list = NULL; + + for( p = history; *p; p = q ) { + if( (q = strchr( p, '\n' )) ) + *q = '\0'; + else + q = p + strlen( p ); + + history_list = g_slist_prepend( history_list, + im__gvalue_ref_string_new( p ) ); + } + + im->history_list = g_slist_reverse( history_list ); +} + +/* Load header fields. + */ +static int +rebuild_header_builtin( IMAGE *im, xmlNode *i ) +{ + char name[256]; + + if( get_sprop( i, "name", name, 256 ) ) { + if( strcmp( name, "Hist" ) == 0 ) { + char *history; + + /* Have to take (another) copy, since we need to free + * with xmlFree(). + */ + history = (char *) xmlNodeGetContent( i ); + set_history( im, history ); + xmlFree( history ); + } + } + + return( 0 ); +} + +/* Load meta fields. + */ +static int +rebuild_header_meta( IMAGE *im, xmlNode *i ) +{ + char name[256]; + char type[256]; + + if( get_sprop( i, "name", name, 256 ) && + get_sprop( i, "type", type, 256 ) ) { + GType gtype = g_type_from_name( type ); + + /* Can we convert from IM_SAVE_STRING to type? + */ + if( gtype && + g_value_type_transformable( + IM_TYPE_SAVE_STRING, gtype ) ) { + char *content; + GValue save_value = { 0 }; + GValue value = { 0 }; + + content = (char *) xmlNodeGetContent( i ); + g_value_init( &save_value, IM_TYPE_SAVE_STRING ); + im_save_string_set( &save_value, content ); + xmlFree( content ); + + g_value_init( &value, gtype ); + if( !g_value_transform( &save_value, &value ) ) { + g_value_unset( &save_value ); + im_error( "im__readhist", _( "error " + "transforming from save format" ) ); + return( -1 ); + } + if( im_meta_set( im, name, &value ) ) { + g_value_unset( &save_value ); + g_value_unset( &value ); + return( -1 ); + } + g_value_unset( &save_value ); + g_value_unset( &value ); + } + } + + return( 0 ); +} + +static xmlDoc * +get_xml( IMAGE *im ) +{ + if( im_header_get_type( im, IM_META_XML ) ) { + xmlDoc *doc; + + if( im_meta_get_area( im, IM_META_XML, (void *) &doc ) ) + return( NULL ); + + return( doc ); + } + + return( NULL ); +} + +/* Rebuild header fields that depend on stuff saved in xml. + */ +static int +rebuild_header( IMAGE *im ) +{ + xmlDoc *doc; + + if( (doc = get_xml( im )) ) { + xmlNode *root; + xmlNode *block; + + if( !(root = xmlDocGetRootElement( doc )) ) + return( -1 ); + if( (block = get_node( root, "header" )) ) { + xmlNode *i; + + for( i = block->children; i; i = i->next ) + if( strcmp( (char *) i->name, "field" ) == 0 ) + if( rebuild_header_builtin( im, i ) ) + return( -1 ); + } + if( (block = get_node( root, "meta" )) ) { + xmlNode *i; + + for( i = block->children; i; i = i->next ) + if( strcmp( (char *) i->name, "field" ) == 0 ) + if( rebuild_header_meta( im, i ) ) + return( -1 ); + } + } + + return( 0 ); +} + +/* Called at the end of im__read_header ... get any XML after the pixel data + * and read it in. + */ +int +im__readhist( IMAGE *im ) +{ + /* Junk any old xml meta. + */ + if( im_header_get_type( im, IM_META_XML ) ) + im_meta_set_area( im, IM_META_XML, NULL, NULL ); + + if( im__has_extension_block( im ) ) { + xmlDoc *doc; + + if( !(doc = read_xml( im )) ) + return( -1 ); + if( im_meta_set_area( im, IM_META_XML, + (im_callback_fn) xmlFreeDoc, doc ) ) { + xmlFreeDoc( doc ); + return( -1 ); + } + } + + if( rebuild_header( im ) ) + return( -1 ); + + return( 0 ); +} + +#define MAX_STRSIZE (32768) /* Max size of text for stack strings */ + +static int +set_prop( xmlNode *node, const char *name, const char *fmt, ... ) +{ + va_list ap; + char value[MAX_STRSIZE]; + + va_start( ap, fmt ); + (void) im_vsnprintf( value, MAX_STRSIZE, fmt, ap ); + va_end( ap ); + + if( !xmlSetProp( node, (xmlChar *) name, (xmlChar *) value ) ) { + im_error( "im_writehist", _( "unable to set property \"%s\" " + "to value \"%s\"." ), + name, value ); + return( -1 ); + } + + return( 0 ); +} + +static int +set_sprop( xmlNode *node, const char *name, const char *value ) +{ + if( value && set_prop( node, name, "%s", value ) ) + return( -1 ); + + return( 0 ); +} + +static int +set_field( xmlNode *node, + const char *name, const char *type, const char *content ) +{ + xmlNode *field; + + if( !(field = xmlNewChild( node, NULL, (xmlChar *) "field", NULL )) || + set_sprop( field, "type", type ) || + set_sprop( field, "name", name ) ) + return( -1 ); + xmlNodeSetContent( field, (xmlChar *) content ); + + return( 0 ); +} + +static void * +save_fields_meta( Meta *meta, xmlNode *node ) +{ + GType type = G_VALUE_TYPE( &meta->value ); + + /* If we can transform to IM_TYPE_SAVE_STRING and back, we can save and + * restore. + */ + if( g_value_type_transformable( type, IM_TYPE_SAVE_STRING ) && + g_value_type_transformable( IM_TYPE_SAVE_STRING, type ) ) { + GValue save_value = { 0 }; + + g_value_init( &save_value, IM_TYPE_SAVE_STRING ); + if( !g_value_transform( &meta->value, &save_value ) ) { + im_error( "im__writehist", + _( "error transforming to save format" ) ); + return( node ); + } + if( set_field( node, meta->field, g_type_name( type ), + im_save_string_get( &save_value ) ) ) { + g_value_unset( &save_value ); + return( node ); + } + g_value_unset( &save_value ); + } + + return( NULL ); +} + +static int +save_fields( IMAGE *im, xmlNode *node ) +{ + xmlNode *this; + + /* Save header fields. + */ + if( !(this = xmlNewChild( node, NULL, (xmlChar *) "header", NULL )) ) + return( -1 ); + if( set_field( this, "Hist", + g_type_name( IM_TYPE_REF_STRING ), im_history_get( im ) ) ) + return( -1 ); + + if( !(this = xmlNewChild( node, NULL, (xmlChar *) "meta", NULL )) ) + return( -1 ); + if( im->Meta_traverse && + im_slist_map2( im->Meta_traverse, + (VSListMap2Fn) save_fields_meta, this, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im__write_extension_block( IMAGE *im, void *buf, int size ) +{ + gint64 length; + gint64 psize; + + psize = im__image_pixel_length( im ); + if( (length = im_file_length( im->fd )) == -1 ) + return( -1 ); + if( length - psize < 0 ) { + im_error( "im__write_extension_block", + _( "file has been truncated" ) ); + return( -1 ); + } + + if( im__ftruncate( im->fd, psize ) || + im__seek( im->fd, psize ) ) + return( -1 ); + if( im__write( im->fd, buf, size ) ) + return( -1 ); + +#ifdef DEBUG + printf( "im__write_extension_block: written %d bytes of XML to %s\n", + size, im->filename ); +#endif /*DEBUG*/ + + return( 0 ); +} + +#ifdef DEBUG +/* Return a string of n characters. Buffer is zapped each time! + */ +const char * +rpt( char ch, int n ) +{ + int i; + static char buf[200]; + + n = IM_MIN( 190, n ); + + for( i = 0; i < n; i++ ) + buf[i] = ch; + buf[i] = '\0'; + + return( buf ); +} + +/* Return a string of n spaces. Buffer is zapped each time! + */ +const char * +spc( int n ) +{ + return( rpt( ' ', n ) ); +} + +static void +prettify_tree_sub( xmlNode *xnode, int indent ) +{ + xmlNode *txt; + xmlNode *next; + + for(;;) { + next = xnode->next; + + /* According to memprof, this leaks :-( If you cut it out into + * a separate prog though, it's OK + + FIXME ... how odd + + */ + txt = xmlNewText( "\n" ); + xmlAddPrevSibling( xnode, txt ); + txt = xmlNewText( spc( indent ) ); + xmlAddPrevSibling( xnode, txt ); + + if( xnode->children ) + prettify_tree_sub( xnode->children, indent + 2 ); + + if( !next ) + break; + + xnode = next; + } + + txt = xmlNewText( spc( indent - 2 ) ); + xmlAddNextSibling( xnode, txt ); + txt = xmlNewText( "\n" ); + xmlAddNextSibling( xnode, txt ); +} + +/* Walk an XML document, adding extra blank text elements so that it's easier + * to read. Don't call me twice! + */ +void +prettify_tree( xmlDoc *xdoc ) +{ + xmlNode *xnode = xmlDocGetRootElement( xdoc ); + + prettify_tree_sub( xnode, 0 ); +} +#endif /*DEBUG*/ + +/* Append XML to output fd. + */ +int +im__writehist( IMAGE *im ) +{ + xmlDoc *doc; + char namespace[256]; + char *dump; + int dump_size; + + assert( im->dtype == IM_OPENOUT ); + assert( im->fd != -1 ); + + if( !(doc = xmlNewDoc( (xmlChar *) "1.0" )) ) + return( -1 ); + + im_snprintf( namespace, 256, "%s/%d.%d.%d", + NAMESPACE, + IM_MAJOR_VERSION, IM_MINOR_VERSION, IM_MICRO_VERSION ); + if( !(doc->children = xmlNewDocNode( doc, + NULL, (xmlChar *) "root", NULL )) || + set_sprop( doc->children, "xmlns", namespace ) || + save_fields( im, doc->children ) ) { + im_error( "im__writehist", _( "xml save error" ) ); + xmlFreeDoc( doc ); + return( -1 ); + } + + /* Bizarre double-cast stops a bogus gcc 4.1 compiler warning. + */ + xmlDocDumpMemory( doc, (xmlChar **) ((char *) &dump), &dump_size ); + if( !dump ) { + im_error( "im__writehist", _( "xml save error" ) ); + xmlFreeDoc( doc ); + return( -1 ); + } + + if( im__write_extension_block( im, dump, dump_size ) ) { + xmlFreeDoc( doc ); + xmlFree( dump ); + return( -1 ); + } + +#ifdef DEBUG +{ + char *dump2; + int dump_size2; + + /* Uncomment to have XML pretty-printed. Can be annoying during + * debugging tho' + */ + prettify_tree( doc ); + + xmlDocDumpMemory( doc, (xmlChar **) &dump2, &dump_size2 ); + if( !dump2 ) { + im_error( "im__writehist", _( "xml save error" ) ); + xmlFreeDoc( doc ); + xmlFree( dump ); + return( -1 ); + } + + printf( "im__writehist: saved XML is: \"%s\"", dump2 ); + xmlFree( dump2 ); +} +#endif /*DEBUG*/ + + xmlFreeDoc( doc ); + xmlFree( dump ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_render.c b/libsrc/iofuncs/im_render.c new file mode 100644 index 00000000..a3d3bc1f --- /dev/null +++ b/libsrc/iofuncs/im_render.c @@ -0,0 +1,1181 @@ +/* render an image in the background as a set of tiles + * + * don't read from mask after closing out + * + * JC, 30 sep 03 + * + * 22/10/03 JC + * - now uses threadgroup kill system, avoiding race condition + * 2/2/04 JC + * - cache failed for large images + * 8/4/04 + * - touch reused tiles so they don't get reused again too soon ... helps + * stop thrashing when we've many pending paints and lots of threads + * 15/4/04 + * - added im_cache() convenience function + * 26/1/05 + * - added im_render_fade() ... fade tiles display for nip2 + * - mask can now be NULL for no mask output + * 11/2/05 + * - tidies + * 27/2/05 + * - limit the number of simultaneous renders + * - kill threadgroups when no dirties left + * - max == -1 means unlimited cache size + * - 'priority' marks non-suspendable renders + * 1/4/05 + * - rewritten for a few global threads instead, and a job queue ... + * should be simpler & more reliable + * 23/4/07 + * - oop, race condition fixed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. +#define DEBUG +#define DEBUG_REUSE +#define DEBUG_TG +#define DEBUG_MAKE +#define DEBUG_PAINT + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +/* Need Sleep() on win32. + */ +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef HAVE_THREADS +static const int have_threads = 1; +#else /*!HAVE_THREADS*/ +static const int have_threads = 0; +#endif /*HAVE_THREADS*/ + +/* A manager thread. We have a fixed number of these taking jobs off the list + * of current renders with dirty tiles, doing a tile, and putting the render + * back. + */ +typedef struct _RenderThread { + GThread *gthread; + struct _Render *render; /* The last render we worked on */ +} RenderThread; + +/* Notify caller through this. + */ +typedef void (*notify_fn)( IMAGE *, Rect *, void * ); + +/* The states a tile can be in. + */ +typedef enum { + TILE_DIRTY, /* On the dirty list .. contains no pixels */ + TILE_WORKING, /* Currently being worked on */ + TILE_PAINTED_FADING, /* Painted, on fade list */ + TILE_PAINTED /* Painted, ready for reuse */ +} TileState; + +/* A tile in our cache. + */ +typedef struct { + struct _Render *render; + + Rect area; /* Place here (unclipped) */ + REGION *region; /* REGION with the pixels */ + + int access_ticks; /* Time of last use for LRU flush */ + int time; /* Time when we finished painting */ + + TileState state; +} Tile; + +/* + + FIXME ... should have an LRU queue rather than this thing with times + + FIXME ... could also hash from tile xy to tile pointer + + */ + +/* Per-call state. + */ +typedef struct _Render { + /* Reference count this, since we use these things from several + * threads. Can't easily use the gobject ref count system, since we + * need a lock around operations. + */ + int ref_count; + GMutex *ref_count_lock; + + /* Parameters. + */ + IMAGE *in; /* Image we render */ + IMAGE *out; /* Write tiles here on demand */ + IMAGE *mask; /* Set valid pixels here */ + int width, height; /* Tile size */ + int max; /* Maximum number of tiles */ + int fps; /* FPS for fade in */ + int steps; /* Steps fade occurs in */ + int priority; /* Larger numbers done sooner */ + notify_fn notify; /* Tell caller about paints here */ + void *client; + + /* Make readers single thread with this. No point allowing + * multi-thread read. + */ + GMutex *read_lock; + + /* Tile cache. + */ + GSList *cache; /* List of all our tiles */ + int ntiles; /* Number of cache tiles */ + int access_ticks; /* Inc. on each access ... used for LRU */ + + /* List of tiles which are to be painted. + */ + GMutex *dirty_lock; /* Lock before we read/write the dirty list */ + GSList *dirty; /* Tiles which need painting */ + + /* List of painted tiles being faded in. Plus a fade-in thread. + */ + GMutex *fade_lock; /* Lock before we read/write the fade list */ + GSList *fade; /* Tiles which are being faded */ + GThread *fade_gthread; /* Fade tiles in thread */ + int time; /* Increment each frame */ + int fade_kill; /* Set to ask fade thread to exit */ + + /* Render thread stuff. + */ + im_threadgroup_t *tg; /* Render with this threadgroup */ + int render_kill; /* This render is dying */ +} Render; + +/* Number of RenderThread we create. + */ +static const int render_thread_max = 1; + +static GSList *render_thread_all = NULL; + +/* Number of renders with dirty tiles. RenderThreads queue up on this. + */ +static im_semaphore_t render_dirty_sem; + +/* All the renders with dirty tiles. + */ +static GMutex *render_dirty_lock = NULL; +static GSList *render_dirty_all = NULL; + +static void +render_dirty_remove( Render *render ) +{ + g_mutex_lock( render_dirty_lock ); + + if( g_slist_find( render_dirty_all, render ) ) { + render_dirty_all = g_slist_remove( render_dirty_all, render ); + + /* We know this can't block, since there is at least 1 item in + * the render_dirty_all list (us). Except that + * render_dirty_get() does a _down() before it locks, so this + * could block if we run inbetween :-( possible deadlock. + */ + im_semaphore_down( &render_dirty_sem ); + } + + g_mutex_unlock( render_dirty_lock ); +} + +static void * +tile_free( Tile *tile ) +{ +#ifdef DEBUG_MAKE + printf( "tile_free\n" ); +#endif /*DEBUG_MAKE*/ + + if( tile->region ) { + (void) im_region_free( tile->region ); + tile->region = NULL; + } + + im_free( tile ); + + return( NULL ); +} + +static int +render_free( Render *render ) +{ +#ifdef DEBUG_MAKE + printf( "render_free: %p\n", render ); +#endif /*DEBUG_MAKE*/ + + assert( render->ref_count == 0 ); + + render_dirty_remove( render ); + + /* Kill fade thread. + */ + if( render->fade_gthread ) { + render->fade_kill = 1; + (void) g_thread_join( render->fade_gthread ); + render->fade_gthread = NULL; + render->fade_kill = 0; + } + + IM_FREEF( im_threadgroup_free, render->tg ); + + /* Free cache. + */ + im_slist_map2( render->cache, + (VSListMap2Fn) tile_free, NULL, NULL ); + IM_FREEF( g_slist_free, render->cache ); + render->ntiles = 0; + IM_FREEF( g_slist_free, render->dirty ); + IM_FREEF( g_slist_free, render->fade ); + + g_mutex_free( render->ref_count_lock ); + g_mutex_free( render->dirty_lock ); + g_mutex_free( render->read_lock ); + g_mutex_free( render->fade_lock ); + + im_free( render ); + + return( 0 ); +} + +/* Ref and unref a Render ... free on last unref. + */ +static int +render_ref( Render *render ) +{ + g_mutex_lock( render->ref_count_lock ); + assert( render->ref_count != 0 ); + render->ref_count += 1; + g_mutex_unlock( render->ref_count_lock ); + + return( 0 ); +} + +static int +render_unref( Render *render ) +{ + int kill; + + g_mutex_lock( render->ref_count_lock ); + assert( render->ref_count > 0 ); + render->ref_count -= 1; + kill = render->ref_count == 0; + g_mutex_unlock( render->ref_count_lock ); + + if( kill ) + render_free( render ); + + return( 0 ); +} + +/* Wait for a render with dirty tiles. + */ +static Render * +render_dirty_get( void ) +{ + Render *render; + + /* Wait for a render with dirty tiles. + */ + im_semaphore_down( &render_dirty_sem ); + + g_mutex_lock( render_dirty_lock ); + + /* Just take the head of the jobs list ... we sort when we add. If + * render_dirty_remove() is called between our semaphore letting us in + * and the _lock(), render_dirty_all can be NULL. + */ + render = NULL; + if( render_dirty_all ) { + render = (Render *) render_dirty_all->data; + + assert( render->ref_count == 1 ); + + /* Ref the render to make sure it can't die while we're + * working on it. + */ + render_ref( render ); + + render_dirty_all = g_slist_remove( render_dirty_all, render ); + } + + g_mutex_unlock( render_dirty_lock ); + + return( render ); +} + +/* Do a single tile. Take a dirty tile from the dirty list, fill with pixels, + * and add to the fade list. + */ +static void +render_dirty_process( Render *render ) +{ + Tile *tile; + + /* Take a tile off the dirty list. + */ + g_mutex_lock( render->dirty_lock ); + if( render->dirty ) { + tile = (Tile *) render->dirty->data; + assert( tile->state == TILE_DIRTY ); + render->dirty = g_slist_remove( render->dirty, tile ); + tile->state = TILE_WORKING; + } + else + tile = NULL; + g_mutex_unlock( render->dirty_lock ); + + if( tile ) { + int result; + + result = -1; + if( !render->tg ) { + render->tg = im_threadgroup_create( render->in ); + +#ifdef DEBUG_TG + printf( "render_paint_tile: " + "%p starting threadgroup\n", + render ); +#endif /*DEBUG_TG*/ + } + + if( render->tg ) { +#ifdef DEBUG_PAINT + printf( "render_fill_tile: " + "%p paint of tile %dx%d\n", + render, + tile->area.left, tile->area.top ); +#endif /*DEBUG_PAINT*/ + + /* We're writing to the tile region, but we didn't + * make it. + */ + im__region_take_ownership( tile->region ); + + result = im_prepare_thread( render->tg, + tile->region, &tile->area ); + } + + /* + + FIXME ... nice if we did something with the error return + + */ +#ifdef DEBUG_PAINT + if( result ) + printf( "render_fill_tile: " + "im_prepare_thread() failed!\n\t%s\n", + im_error_buffer() ); +#endif /*DEBUG_PAINT*/ + + g_mutex_lock( render->fade_lock ); + + render->fade = g_slist_prepend( render->fade, tile ); + tile->time = render->time; + tile->state = TILE_PAINTED_FADING; + + /* Hand tile over to another thread. + */ + im__region_no_ownership( tile->region ); + + g_mutex_unlock( render->fade_lock ); + } +} + +static int +render_dirty_sort( Render *a, Render *b ) +{ + return( b->priority - a->priority ); +} + +/* Add to the jobs list, if it has work to be done. + */ +static void +render_dirty_put( Render *render ) +{ + g_mutex_lock( render_dirty_lock ); + + if( render->dirty && !render->render_kill ) { + if( !g_slist_find( render_dirty_all, render ) ) { + render_dirty_all = g_slist_prepend( render_dirty_all, + render ); + render_dirty_all = g_slist_sort( render_dirty_all, + (GCompareFunc) render_dirty_sort ); + im_semaphore_up( &render_dirty_sem ); + } + } + else { + /* Coming off the jobs list ... shut down the render pipeline. + */ +#ifdef DEBUG_TG + printf( "render_dirty_put: %p stopping threadgroup\n", render ); +#endif /*DEBUG_TG*/ + IM_FREEF( im_threadgroup_free, render->tg ); + } + + g_mutex_unlock( render_dirty_lock ); +} + +/* Main loop for RenderThreads. + */ +static void * +render_thread_main( void *client ) +{ + /* Could use this if we want per-thread state in the future. + RenderThread *thread = (RenderThread *) client; + */ + + for(;;) { + Render *render; + + if( (render = render_dirty_get()) ) { + render_dirty_process( render ); + render_dirty_put( render ); + + assert( render->ref_count == 1 || + render->ref_count == 2 ); + + /* _get() does a ref to make sure we keep the render + * alive during processing ... unref before we loop. + * This can kill off the render. + */ + render_unref( render ); + } + } +} + +/* Create our set of RenderThread. Assume we're single-threaded here. + */ +static int +render_thread_create( void ) +{ + int len = g_slist_length( render_thread_all ); + int i; + + if( !have_threads ) + return( 0 ); + + /* 1st time through only. + */ + if( !render_dirty_lock ) { + render_dirty_lock = g_mutex_new(); + im_semaphore_init( &render_dirty_sem, 0, "render_dirty_sem" ); + } + + for( i = len; i < render_thread_max; i++ ) { + RenderThread *thread = IM_NEW( NULL, RenderThread ); + + thread->gthread = NULL; + thread->render = NULL; + + if( !(thread->gthread = g_thread_create_full( + render_thread_main, thread, + IM__DEFAULT_STACK_SIZE, TRUE, FALSE, + G_THREAD_PRIORITY_NORMAL, NULL )) ) { + im_free( thread ); + im_error( "im_render", _( "unable to create thread" ) ); + return( -1 ); + } + + render_thread_all = g_slist_prepend( render_thread_all, + thread ); + } + + return( 0 ); +} + +#ifdef DEBUG +static char * +tile_state_name( TileState state ) +{ + switch( state ) { + case TILE_DIRTY: return( "TILE_DIRTY" ); + case TILE_WORKING: return( "TILE_WORKING" ); + case TILE_PAINTED_FADING: return( "TILE_PAINTED_FADING" ); + case TILE_PAINTED: return( "TILE_PAINTED" ); + + default: + assert( FALSE ); + } +} + +static void * +tile_print( Tile *tile ) +{ + printf( "tile: %dx%d, access_ticks = %d, time = %d,\n" + "\tstate = %s\n", + tile->area.left, tile->area.top, + tile->access_ticks, tile->time, + tile_state_name( tile->state ) ); + + return( NULL ); +} +#endif /*DEBUG*/ + +/* Come here for the fade work thread. + */ +static void * +render_fade( void *client ) +{ + Render *render = (Render *) client; + + for(;;) { + GSList *p; + GSList *next; + +#ifdef OS_WIN32 + Sleep( 1000 / render->fps ); +#else /*!OS_WIN32*/ + usleep( 1000000 / render->fps ); +#endif /*!OS_WIN32*/ + + if( render->fade_kill ) + break; + + /* Increment frame counter. Used to set bg/fg ratio for fade. + */ + render->time += 1; + + g_mutex_lock( render->fade_lock ); + for( p = render->fade; p; p = next ) { + Tile *tile = (Tile *) p->data; + + assert( tile->state == TILE_PAINTED_FADING ); + + /* We remove the current element as we scan :-( so get + * next beforehand. + */ + next = p->next; + + if( render->time - tile->time > render->steps ) { + /* Tile is solid ... comes off the fade list. + */ + render->fade = g_slist_remove( render->fade, + tile ); + tile->state = TILE_PAINTED; + } + + /* Ask client to update. + */ + if( render->notify ) + render->notify( render->out, + &tile->area, render->client ); + } + g_mutex_unlock( render->fade_lock ); + } + + return( NULL ); +} + +static Render * +render_new( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, + int fps, int steps, + int priority, + notify_fn notify, void *client ) +{ + Render *render; + + /* Don't use auto-free for render, we do our own lifetime management + * with _ref() and _unref(). + */ + if( !(render = IM_NEW( NULL, Render )) ) + return( NULL ); + + render->ref_count = 1; + render->ref_count_lock = g_mutex_new(); + + render->in = in; + render->out = out; + render->mask = mask; + render->width = width; + render->height = height; + render->max = max; + render->fps = fps; + render->steps = steps; + render->priority = priority; + render->notify = notify; + render->client = client; + + render->read_lock = g_mutex_new(); + + render->cache = NULL; + render->ntiles = 0; + render->access_ticks = 0; + + render->dirty_lock = g_mutex_new(); + render->dirty = NULL; + + render->fade_lock = g_mutex_new(); + render->fade = NULL; + render->fade_gthread = NULL; + render->time = 0; + render->fade_kill = 0; + + render->tg = NULL; + render->render_kill = FALSE; + + if( im_add_close_callback( out, + (im_callback_fn) render_unref, render, NULL ) ) { + (void) render_unref( render ); + return( NULL ); + } + + /* Only need the fade thread for fps != 0. + */ + if( have_threads && fps && + !(render->fade_gthread = g_thread_create_full( + render_fade, render, + IM__DEFAULT_STACK_SIZE, TRUE, FALSE, + G_THREAD_PRIORITY_NORMAL, NULL )) ) { + im_error( "im_render", _( "unable to create thread" ) ); + return( NULL ); + } + + return( render ); +} + +/* Make a Tile. + */ +static Tile * +tile_new( Render *render ) +{ + Tile *tile; + +#ifdef DEBUG_MAKE + printf( "tile_new\n" ); +#endif /*DEBUG_MAKE*/ + + /* Don't use auto-free: we need to make sure we free the tile after + * Render. + */ + if( !(tile = IM_NEW( NULL, Tile )) ) + return( NULL ); + + tile->render = render; + tile->region = NULL; + tile->area.left = 0; + tile->area.top = 0; + tile->area.width = 0; + tile->area.height = 0; + tile->access_ticks = render->access_ticks; + tile->time = 0; + tile->state = TILE_WORKING; + + if( !(tile->region = im_region_create( render->in )) ) { + (void) tile_free( tile ); + return( NULL ); + } + + render->cache = g_slist_prepend( render->cache, tile ); + render->ntiles += 1; + + return( tile ); +} + +static void * +tile_test_area( Tile *tile, Rect *area ) +{ + if( im_rect_equalsrect( &tile->area, area ) ) + return( tile ); + + return( NULL ); +} + +/* Search the cache for a tile, NULL if not there. Could be *much* faster. If + * we find a tile, bump to front. + */ +static Tile * +render_tile_lookup( Render *render, Rect *area ) +{ + Tile *tile; + + tile = (Tile *) im_slist_map2( render->cache, + (VSListMap2Fn) tile_test_area, area, NULL ); + + /* We've looked at a tile ... bump to end of LRU and front of dirty. + */ + if( tile ) { + tile->access_ticks = render->access_ticks; + render->access_ticks += 1; + + g_mutex_lock( render->dirty_lock ); + if( tile->state == TILE_DIRTY ) { +#ifdef DEBUG + printf( "tile_bump_dirty: bumping tile %dx%d\n", + tile->area.left, tile->area.top ); +#endif /*DEBUG*/ + + render->dirty = g_slist_remove( render->dirty, tile ); + render->dirty = g_slist_prepend( render->dirty, tile ); + } + g_mutex_unlock( render->dirty_lock ); + } + + return( tile ); +} + +/* Add a tile to the dirty list. + */ +static void +tile_set_dirty( Tile *tile, Rect *area ) +{ + Render *render = tile->render; + +#ifdef DEBUG_PAINT + printf( "tile_set_dirty: adding tile %dx%d to dirty\n", + area->left, area->top ); +#endif /*DEBUG_PAINT*/ + + assert( tile->state == TILE_WORKING ); + + /* Touch the ticks ... we want to make sure this tile will not be + * reused too soon, so it gets a chance to get painted. + */ + tile->access_ticks = render->access_ticks; + render->access_ticks += 1; + + g_mutex_lock( render->dirty_lock ); + + tile->state = TILE_DIRTY; + tile->area = *area; + render->dirty = g_slist_prepend( render->dirty, tile ); + + /* Someone else will write to it now. + */ + im__region_no_ownership( tile->region ); + + /* Can't unlock render->dirty_lock here, we need to render_dirty_put() + * before the tile is processed. + */ + + if( render->notify && have_threads ) + /* Add to the list of renders with dirty tiles. One of our bg + * threads will pick it up and paint it. + */ + render_dirty_put( render ); + else { + /* No threads, or no notify ... paint the tile ourselves, + * sychronously. No need to notify the client, since they'll + * never see black tiles. + */ +#ifdef DEBUG_PAINT + printf( "tile_set_dirty: painting tile %dx%d synchronously\n", + area->left, area->top ); +#endif /*DEBUG_PAINT*/ + + if( !render->tg ) + render->tg = im_threadgroup_create( render->in ); + + /* We're writing to the tile region, but we didn't make it. + */ + im__region_take_ownership( tile->region ); + + if( render->tg ) + im_prepare_thread( render->tg, + tile->region, &tile->area ); + + /* Can't fade in synchronous mode .. straight to painted. + */ + tile->state = TILE_PAINTED; + render->dirty = g_slist_remove( render->dirty, tile ); + } + + g_mutex_unlock( render->dirty_lock ); +} + +static void * +tile_test_clean_ticks( Tile *this, Tile **best ) +{ + if( this->state == TILE_PAINTED ) + if( !*best || this->access_ticks < (*best)->access_ticks ) + *best = this; + + return( NULL ); +} + +/* Pick a painted tile to reuse. Search for LRU (slow!). + */ +static Tile * +render_tile_get_painted( Render *render ) +{ + Tile *tile; + + tile = NULL; + im_slist_map2( render->cache, + (VSListMap2Fn) tile_test_clean_ticks, &tile, NULL ); + + if( tile ) { + assert( tile->state == TILE_PAINTED ); + +#ifdef DEBUG_REUSE + printf( "render_tile_get_painted: reusing painted %p\n", tile ); + + g_mutex_lock( render->dirty_lock ); + assert( !g_slist_find( render->dirty, tile ) ); + g_mutex_unlock( render->dirty_lock ); + + g_mutex_lock( render->fade_lock ); + assert( !g_slist_find( render->fade, tile ) ); + g_mutex_unlock( render->fade_lock ); +#endif /*DEBUG_REUSE*/ + + tile->state = TILE_WORKING; + } + + return( tile ); +} + +/* Take a tile off the end of the dirty list. + */ +static Tile * +render_tile_get_dirty( Render *render ) +{ + Tile *tile; + + g_mutex_lock( render->dirty_lock ); + if( !render->dirty ) + tile = NULL; + else { + tile = (Tile *) g_slist_last( render->dirty )->data; + render->dirty = g_slist_remove( render->dirty, tile ); + tile->state = TILE_WORKING; + } + g_mutex_unlock( render->dirty_lock ); + +#ifdef DEBUG_REUSE + if( tile ) + printf( "render_tile_get_dirty: reusing dirty %p\n", tile ); +#endif /*DEBUG_REUSE*/ + + return( tile ); +} + +static Tile * +render_tile_get( Render *render, Rect *area ) +{ + Tile *tile; + + /* Got this tile already? + */ + if( (tile = render_tile_lookup( render, area )) ) { +#ifdef DEBUG_PAINT + printf( "render_tile_get: found %dx%d in cache\n", + area->left, area->top ); +#endif /*DEBUG_PAINT*/ + + return( tile ); + } + + /* Have we fewer tiles than teh max? Can just make a new tile. + */ + if( render->ntiles < render->max || render->max == -1 ) { + if( !(tile = tile_new( render )) ) + return( NULL ); + } + else { + /* Need to reuse a tile. Try for an old painted tile first, + * then if that fails, reuse a dirty tile. Don't search the + * fading list, though we could maybe scavenge there. + */ + if( !(tile = render_tile_get_painted( render )) && + !(tile = render_tile_get_dirty( render )) ) + return( NULL ); + +#ifdef DEBUG_REUSE + printf( "(render_tile_get: was at %dx%d, moving to %dx%d)\n", + tile->area.left, tile->area.top, + area->left, area->top ); +#endif /*DEBUG_REUSE*/ + } + +#ifdef DEBUG_PAINT + printf( "render_tile_get: sending %dx%d for calc\n", + area->left, area->top ); +#endif /*DEBUG_PAINT*/ + + tile_set_dirty( tile, area ); + + return( tile ); +} + +/* Copy what we can from the tile into the region. + */ +static void +tile_copy( Tile *tile, REGION *to ) +{ + Rect ovlap; + int y; + int len; + + /* Find common pixels. + */ + im_rect_intersectrect( &tile->area, &to->valid, &ovlap ); + assert( !im_rect_isempty( &ovlap ) ); + len = IM_IMAGE_SIZEOF_PEL( to->im ) * ovlap.width; + + /* If the tile is painted, copy over the pixels. Otherwise, fill with + * zero. + */ + if( tile->state == TILE_PAINTED || + tile->state == TILE_PAINTED_FADING ) { +#ifdef DEBUG_PAINT + printf( "tile_copy: copying calculated pixels for %dx%d\n", + tile->area.left, tile->area.top ); +#endif /*DEBUG_PAINT*/ + + for( y = ovlap.top; y < IM_RECT_BOTTOM( &ovlap ); y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( tile->region, + ovlap.left, y ); + PEL *q = (PEL *) IM_REGION_ADDR( to, ovlap.left, y ); + + memcpy( q, p, len ); + } + } + else { +#ifdef DEBUG_PAINT + printf( "tile_copy: zero filling for %dx%d\n", + tile->area.left, tile->area.top ); +#endif /*DEBUG_PAINT*/ + + for( y = ovlap.top; + y < IM_RECT_BOTTOM( &ovlap ); y++ ) { + PEL *q = (PEL *) + IM_REGION_ADDR( to, ovlap.left, y ); + + memset( q, 0, len ); + } + } +} + +/* Loop over the output region, filling with data from cache. + */ +static int +region_fill( REGION *out, void *seq, Render *render ) +{ + Rect *r = &out->valid; + int x, y; + + /* Find top left of tiles we need. + */ + int xs = (r->left / render->width) * render->width; + int ys = (r->top / render->height) * render->height; + + /* Only allow one reader. No point threading this, calculation is + * decoupled anyway. + */ + g_mutex_lock( render->read_lock ); + + /* + + FIXME ... if r fits inside a single tile, could skip the copy. + + */ + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += render->height ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += render->width ) { + Rect area; + Tile *tile; + + area.left = x; + area.top = y; + area.width = render->width; + area.height = render->height; + + if( (tile = render_tile_get( render, &area )) ) + tile_copy( tile, out ); + } + + g_mutex_unlock( render->read_lock ); + + return( 0 ); +} + +/* Paint the state of the 'painted' flag for a tile. Fade too. + */ +static void +tile_paint_mask( Tile *tile, REGION *to ) +{ + int mask; + + Rect ovlap; + int y; + int len; + + /* Find common pixels. + */ + im_rect_intersectrect( &tile->area, &to->valid, &ovlap ); + assert( !im_rect_isempty( &ovlap ) ); + len = IM_IMAGE_SIZEOF_PEL( to->im ) * ovlap.width; + + if( tile->state == TILE_PAINTED_FADING ) { + Render *render = tile->render; + int age = render->time - tile->time; + double opacity = IM_CLIP( 0, (double) age / render->steps, 1 ); + + mask = 255 * pow( opacity, 4 ); + } + else if( tile->state == TILE_PAINTED ) + mask = 255; + else + mask = 0; + + for( y = ovlap.top; y < IM_RECT_BOTTOM( &ovlap ); y++ ) { + PEL *q = (PEL *) IM_REGION_ADDR( to, ovlap.left, y ); + + memset( q, mask, len ); + } +} + +/* The mask image is 255 .. 0 for the state of painted for each tile. + */ +static int +mask_fill( REGION *out, void *seq, Render *render ) +{ + Rect *r = &out->valid; + int x, y; + + /* Find top left of tiles we need. + */ + int xs = (r->left / render->width) * render->width; + int ys = (r->top / render->height) * render->height; + + g_mutex_lock( render->read_lock ); + + for( y = ys; y < IM_RECT_BOTTOM( r ); y += render->height ) + for( x = xs; x < IM_RECT_RIGHT( r ); x += render->width ) { + Rect area; + Tile *tile; + + area.left = x; + area.top = y; + area.width = render->width; + area.height = render->height; + + if( (tile = render_tile_get( render, &area )) ) + tile_paint_mask( tile, out ); + } + + g_mutex_unlock( render->read_lock ); + + return( 0 ); +} + +int +im_render_fade( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, + int fps, int steps, + int priority, + notify_fn notify, void *client ) +{ + Render *render; + + /* Make sure the bg work threads are ready. + */ + if( render_thread_create() ) + return( -1 ); + + if( width <= 0 || height <= 0 || max < -1 || fps <= 0 || steps < 0 ) { + im_error( "im_render", _( "bad parameters" ) ); + return( -1 ); + } + if( im_pincheck( in ) || im_poutcheck( out ) ) + return( -1 ); + if( mask ) { + if( im_poutcheck( mask ) || + im_cp_desc( mask, in ) ) + return( -1 ); + + mask->Bands = 1; + mask->BandFmt = IM_BANDFMT_UCHAR; + mask->Bbits = IM_BBITS_BYTE; + mask->Type = IM_TYPE_B_W; + mask->Coding = IM_CODING_NONE; + } + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) || + im_demand_hint( mask, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + if( !(render = render_new( in, out, mask, + width, height, max, fps, steps, priority, notify, client )) ) + return( -1 ); + +#ifdef DEBUG_MAKE + printf( "im_render: max = %d, %p\n", max, render ); +#endif /*DEBUG_MAKE*/ + + if( im_generate( out, NULL, region_fill, NULL, render, NULL ) ) + return( -1 ); + if( mask && + im_generate( mask, NULL, mask_fill, NULL, render, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +im_render( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, notify_fn notify, void *client ) +{ + return( im_render_fade( in, out, mask, + width, height, max, 10, 0, 0, notify, client ) ); +} + +int +im_cache( IMAGE *in, IMAGE *out, int width, int height, int max ) +{ + if( im_render( in, out, NULL, width, height, max, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_setbox.c b/libsrc/iofuncs/im_setbox.c new file mode 100644 index 00000000..1ba8fde5 --- /dev/null +++ b/libsrc/iofuncs/im_setbox.c @@ -0,0 +1,64 @@ +/* @(#) Copies the coordinates of a box to an IMAGE_BOX + * @(#) + * @(#) Right call: + * @(#) void im_setbox(pbox, xst, yst, xsiz, ysiz, ch_select) + * @(#) IMAGE_BOX *pbox; + * @(#) int xst, yst, xsiz, ysiz, ch_select; + * @(#) ch_select could be 0, 1, 2 or 3 corresponding to + * @(#) a, r , g or b respectively. + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 04/04/1990 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void im_setbox(pbox, xst, yst, xsiz, ysiz, ch_select) +IMAGE_BOX *pbox; +int xst, yst, xsiz, ysiz, ch_select; +{ + pbox->xstart = xst; + pbox->ystart = yst; + pbox->xsize = xsiz; + pbox->ysize = ysiz; + pbox->chsel = ch_select; +} diff --git a/libsrc/iofuncs/im_setbuf.c b/libsrc/iofuncs/im_setbuf.c new file mode 100644 index 00000000..43f3b091 --- /dev/null +++ b/libsrc/iofuncs/im_setbuf.c @@ -0,0 +1,73 @@ +/* @(#) im_setbuf: initialise a buffer IMAGE + * @(#) Initialise the data pointer of the image descriptor to an arbitrary + * @(#) non NULL value and copies the file_name onto the filename of the + * @(#) image structure. + * @(#) + * @(#) Right call: + * @(#) IMAGE *im_setbuf(file_name) + * @(#) char *file_name; + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 13/02/1990 + * Modified on : 25/04/1990 KM, 20/03/1991 ND + * 16/4/93 J.Cupitt + * - support for type field added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +IMAGE * +im_setbuf( const char *file_name ) +{ + IMAGE *im; + + if( !(im = im_init( file_name )) ) + return( NULL ); + im->dtype = IM_SETBUF; + + /* Set demand style. Allow the most permissive sort. + */ + im->dhint = IM_ANY; + + return( im ); +} diff --git a/libsrc/iofuncs/im_setupout.c b/libsrc/iofuncs/im_setupout.c new file mode 100644 index 00000000..a930b498 --- /dev/null +++ b/libsrc/iofuncs/im_setupout.c @@ -0,0 +1,159 @@ +/* @(#) + * @(#) Function which sets up the output as follows + * @(#) If the output is a buffer, it allocates memory according to image sizes + * @(#) If the output is a file, it write a header + * @(#) to the file pointed by image.fd + * @(#) If the output is a partial image, then `magically' turn this from an + * @(#) im_partial() image into an im_setbuf() image. If im_setupout() is + * @(#) called, we take it as a sign that we are dealing with pre-partial + * @(#) images code. + * @(#) When exiting, image.fd points at the end of the header info. + * @(#) expecting raw image data. + * @(#) No description or history is involved + * @(#) Called by all im_funcs + * @(#) + * @(#) int im_setupout(image) + * @(#) IMAGE *image; + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * Copyright: Nicos Dessipris + * Written on: 16/01/1990 + * Modified on : 04/04/1990, 28/02/1991 + * 15/4/93 JC + * - partial image support added + * 18/6/93 JC + * - ANSIfied + * 4/7/01 JC + * - OPENOUT open delayed until here + * 21/8/01 ruven + * - stat/file needed + * 22/8/05 + * * - less stupid header write + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#ifdef HAVE_SYS_FILE_H +#include +#endif /*HAVE_SYS_FILE_H*/ +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Open mode for write ... on some systems, have to set BINARY too. + */ +#ifdef BINARY_OPEN +#define MODE (O_WRONLY | O_CREAT | O_TRUNC | O_BINARY) +#else +#define MODE (O_WRONLY | O_CREAT | O_TRUNC) +#endif /*BINARY_OPEN*/ + +int +im_setupout( IMAGE *im ) +{ + if( im_image_sanity( im ) ) + return( -1 ); + if( im->Xsize <= 0 || im->Ysize <= 0 || im->Bands <= 0 ) { + im_error( "im_setupout", _( "bad dimensions" ) ); + return( -1 ); + } + + if( im->dtype == IM_PARTIAL ) { + /* Make it into a im_setbuf() image. + */ +#ifdef DEBUG_IO + printf( "im_setupout: old-style output for %s\n", + im->filename ); +#endif /*DEBUG_IO*/ + im->dtype = IM_SETBUF; + } + + switch( im->dtype ) { + case IM_MMAPINRW: + case IM_SETBUF_FOREIGN: + /* No action. + */ + break; + + case IM_SETBUF: + /* Allocate memory. + */ + if( im->data ) { + /* Sanity failure! + */ + im_error( "im_setupout", _( "called twice!" ) ); + return( -1 ); + } + if( !(im->data = im_malloc( NULL, + IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize )) ) + return( -1 ); + + break; + + case IM_OPENOUT: + { + /* Don't use im->sizeof_header here, but we know we're + * writing a VIPS image anyway. + */ + unsigned char header[IM_SIZEOF_HEADER]; + + if( (im->fd = open( im->filename, MODE, 0666 )) < 0 ) { + im_error( "im_setupout", + _( "unable to write to \"%s\"" ), + im->filename ); + return( -1 ); + } + if( im__write_header_bytes( im, header ) || + im__write( im->fd, header, IM_SIZEOF_HEADER ) ) + return( -1 ); + + break; + } + + default: + im_error( "im_setupout", _( "bad image descriptor" ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_unmapfile.c b/libsrc/iofuncs/im_unmapfile.c new file mode 100644 index 00000000..1c5c6df2 --- /dev/null +++ b/libsrc/iofuncs/im_unmapfile.c @@ -0,0 +1,71 @@ +/* @(#) Function which unmaps a file memory mapped by mapfile() + * @(#) The argument baseaddress should be the pointer returned by mapfile(); + * @(#) The function finds the size of the file from + * @(#) + * @(#) int im_unmapfile(fd, baseaddress) + * @(#) int fd; + * @(#) char *baseaddress; + * @(#) + * @(#) Returns 0 on success and -1 on error. + * @(#) + * Copyright: Nicos Dessipris + * Wriiten on: 13/02/1990 + * Updated on: + * 18/4/97 JC + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_unmapfile( IMAGE *im ) +{ + if( im__munmap( im->baseaddr, im->length ) ) + return( -1 ); + im->baseaddr = NULL; + im->length = 0; + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_updatehist.c b/libsrc/iofuncs/im_updatehist.c new file mode 100644 index 00000000..2e2481f3 --- /dev/null +++ b/libsrc/iofuncs/im_updatehist.c @@ -0,0 +1,80 @@ +/* @(#) + * @(#) int + * @(#) im_updatehist( IMAGE *out, const char *name, int argc, char *argv[] ) + * @(#) + * @(#) Returns either 0 (success) or -1 (fail) + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 16/01/1990 + * Modified on : 28/10/1992 J. Cupitt + * - now calls im_histlin, much simpler + * - many bugs in old version ... + * 22/8/05 + * - pass argv0 separately + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define IM_MAX_LINE (4096) + +int +im_updatehist( IMAGE *out, const char *name, int argc, char *argv[] ) +{ + int i; + char txt[IM_MAX_LINE]; + VBuf buf; + + im_buf_init_static( &buf, txt, IM_MAX_LINE ); + im_buf_appends( &buf, name ); + + for( i = 0; i < argc; i++ ) { + im_buf_appends( &buf, " " ); + im_buf_appends( &buf, argv[i] ); + } + + if( im_histlin( out, "%s", im_buf_all( &buf ) ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_wrapmany.c b/libsrc/iofuncs/im_wrapmany.c new file mode 100644 index 00000000..23909f46 --- /dev/null +++ b/libsrc/iofuncs/im_wrapmany.c @@ -0,0 +1,202 @@ +/* Wrap-up a buffer processing function as a PIO VIPS function. + * + * Given a NULL-terminated list of input images all of the same size, an + * output image and a buffer processing function, make a PIO image processing + * operation. + * + * int im_wrapmany( IMAGE **in, IMAGE *out, + * im_wrapmany_fn fn, void *a, void *b ) + * + * where im_wrapmany_fn has type: + * + * process_buffer( void **in, void *out, int n, + * void *a, void *b ) + * + * in is a NULL-terminated array of input buffers, out is an output buffer, n + * is the number of pixels (note! not band-elements) and a and b are extra + * arguments carried for the function + * + * Modified: + * 1/8/95 JC + * - buffer functions now get their own copies of the input pointer + * array + * 28/7/97 JC + * - amazing error ... only worked if ir and or had same valid + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct { + im_wrapmany_fn fn; /* Function we call */ + void *a, *b; /* User values for function */ +} UserBundle; + +/* Maximum number of input images -- why not? + */ +#define IM_MAX_INPUT_IMAGES (64) + +/* Convert a REGION. + */ +static int +process_region( REGION *or, REGION **ir, IMAGE *im, UserBundle *bun ) +{ + PEL *p[IM_MAX_INPUT_IMAGES], *q; + int i, y; + + /* Prepare all input regions and make buffer pointers. + */ + for( i = 0; ir[i]; i++ ) { + if( im_prepare( ir[i], &or->valid ) ) + return( -1 ); + p[i] = (PEL *) IM_REGION_ADDR( ir[i], + or->valid.left, or->valid.top ); + } + p[i] = NULL; + q = (PEL *) IM_REGION_ADDR( or, or->valid.left, or->valid.top ); + + /* Convert linewise. + */ + for( y = 0; y < or->valid.height; y++ ) { + PEL *p1[IM_MAX_INPUT_IMAGES]; + + /* Make a copy of p[] which the buffer function can mess up if + * it wants. + */ + for( i = 0; ir[i]; i++ ) + p1[i] = p[i]; + + /* Bizarre double-cast stops a bogus gcc 4.1 compiler warning. + */ + bun->fn( (void **) ((void *)p1), q, + or->valid.width, bun->a, bun->b ); + + /* Move pointers on. + */ + for( i = 0; ir[i]; i++ ) + p[i] += IM_REGION_LSKIP( ir[i] ); + q += IM_REGION_LSKIP( or ); + } + + return( 0 ); +} + +/* Make a copy of an array of input images. + */ +static IMAGE ** +dupims( IMAGE *out, IMAGE **in ) +{ + IMAGE **new; + int i, n; + + /* Count input images. + */ + for( n = 0; in[n]; n++ ) + ; + + /* Allocate new array. + */ + if( !(new = IM_ARRAY( out, n + 1, IMAGE * )) ) + return( NULL ); + + /* Copy. + */ + for( i = 0; i < n; i++ ) + new[i] = in[i]; + new[n] = NULL; + + return( new ); +} + +/* Wrap up as a partial. + */ +int +im_wrapmany( IMAGE **in, IMAGE *out, im_wrapmany_fn fn, void *a, void *b ) +{ + UserBundle *bun = IM_NEW( out, UserBundle ); + int i, n; + + /* Count input images. + */ + for( n = 0; in[n]; n++ ) + ; + if( n >= IM_MAX_INPUT_IMAGES - 1 ) { + im_error( "im_wrapmany", _( "too many input images" ) ); + return( -1 ); + } + + /* Save args. + */ + if( !bun || !(in = dupims( out, in )) ) + return( -1 ); + bun->fn = fn; + bun->a = a; + bun->b = b; + + /* Check descriptors --- make sure that our caller has done this + * correctly. + */ + for( i = 0; i < n; i++ ) { + if( in[i]->Xsize != out->Xsize || in[i]->Ysize != out->Ysize ) { + im_error( "im_wrapmany", + _( "descriptors differ in size" ) ); + return( -1 ); + } + + /* Check io style. + */ + if( im_piocheck( in[i], out ) ) + return( -1 ); + } + + /* Hint demand style. Being a buffer processor, we are happiest with + * thin strips. + */ + if( im_demand_hint_array( out, IM_THINSTRIP, in ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_many, process_region, im_stop_many, in, bun ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_wrapone.c b/libsrc/iofuncs/im_wrapone.c new file mode 100644 index 00000000..7f997a37 --- /dev/null +++ b/libsrc/iofuncs/im_wrapone.c @@ -0,0 +1,121 @@ +/* As im_wrapmany, but just allow one input and one output. + * + * The types become: + * + * int im_wrapone( IMAGE *in, IMAGE *out, + * im_wrapone_fn fn, void *a, void *b ) + * + * where im_wrapone_fn has type: + * + * process_buffer( void *in, void *out, int n, + * void *a, void *b ) + * 28/7/97 JC + * - amazing error ... failed if or and ir were different sizes + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct { + im_wrapone_fn fn; /* Function we call */ + void *a, *b; /* User values for function */ +} UserBundle; + +/* Build or->valid a line at a time from ir. + */ +static int +process_region( REGION *or, REGION *ir, IMAGE *im, UserBundle *bun ) +{ + PEL *p, *q; + int y; + + /* Prepare input region and make buffer pointers. + */ + if( im_prepare( ir, &or->valid ) ) + return( -1 ); + p = (PEL *) IM_REGION_ADDR( ir, or->valid.left, or->valid.top ); + q = (PEL *) IM_REGION_ADDR( or, or->valid.left, or->valid.top ); + + /* Convert linewise. + */ + for( y = 0; y < or->valid.height; y++ ) { + bun->fn( p, q, or->valid.width, bun->a, bun->b ); + p += IM_REGION_LSKIP( ir ); + q += IM_REGION_LSKIP( or ); + } + + return( 0 ); +} + +/* Wrap up as a partial. + */ +int +im_wrapone( IMAGE *in, IMAGE *out, im_wrapone_fn fn, void *a, void *b ) +{ + UserBundle *bun = IM_NEW( out, UserBundle ); + + /* Save args. + */ + if( !bun ) + return( -1 ); + bun->fn = fn; + bun->a = a; + bun->b = b; + + /* Check descriptors. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + + /* Hint demand style. Being a buffer processor, we are happiest with + * thin strips. + */ + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im_start_one, process_region, im_stop_one, + in, bun ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/im_writeline.c b/libsrc/iofuncs/im_writeline.c new file mode 100644 index 00000000..37325acd --- /dev/null +++ b/libsrc/iofuncs/im_writeline.c @@ -0,0 +1,113 @@ +/* @(#) Functions which writes the yth buffer line to either the output file + * @(#) or the output buffer. + * @(#) It is the responsibility of the user to create a buffer line + * @(#) and write the data to it before calling this function. + * @(#) No checking is carried out for image + * @(#) + * @(#) int im_writeline(ypos, image, linebuffer) + * @(#) int ypos; + * @(#) IMAGE *image; + * @(#) char *linebuffer; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: Nicos Dessipris + * Written on: 04/04/1990 + * Modified on : + * 15/4/93 JC + * - support for partial images + * 13/12/93 JC + * - now triggers eval callbacks for the output image. + * 26/3/02 JC + * - better error messages + * 31/10/03 JC + * - stop early on kill + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#ifdef HAVE_IO_H +#include +#endif /*HAVE_IO_H*/ + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_writeline( int ypos, IMAGE *image, PEL *linebuffer ) +{ + int linesize = IM_IMAGE_SIZEOF_LINE( image ); + char *tmp; + + /* Possible cases for output: FILE or SETBUF. + */ + switch( image->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + tmp = image->data + ypos * linesize; + memcpy( tmp, linebuffer, linesize ); + + break; + + case IM_OPENOUT: + if( im__write( image->fd, linebuffer, linesize ) ) + return( -1 ); + + break; + + default: + im_errormsg( "im_writeline: unable to output to a %s image", + im_dtype2char( image->dtype ) ); + return( -1 ); + } + + /* Trigger evaluation callbacks for this image. + */ + if( im__handle_eval( image, image->Xsize, 1 ) ) + return( -1 ); + if( im__test_kill( image ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/man3/IM_ARRAY.3 b/libsrc/iofuncs/man3/IM_ARRAY.3 new file mode 100644 index 00000000..2ed9a74b --- /dev/null +++ b/libsrc/iofuncs/man3/IM_ARRAY.3 @@ -0,0 +1,80 @@ +.TH IM_ARRAY 3 "11 April 1993" +.SH NAME +IM_ARRAY, IM_NEW, IM_NUMBER \- memory allocation macros +.SH SYNOPSIS + +#include +.br +#include + +type-name *IM_NEW( IMAGE *im, type-name ) +.br +type-name *IM_ARRAY( IMAGE *im, int number, type-name ) +.br +int IM_NUMBER( array ) + +.SH DESCRIPTION + +NEW, NUMBER and ARRAY are macros built on im_malloc(3) which make memory +allocation slightly easier. Given a type name, NEW returns a pointer to a +piece of memory large enough to hold an object of that type. ARRAY works as +NEW, but allocates space for a number of objects. Given an array, NUMBER +returns the number of elements in that array. + + #define IM_NEW(IM,A) ((A *)im_malloc((IM),sizeof(A))) + #define IM_NUMBER(R) (sizeof(R)/sizeof(R[0])) + #define IM_ARRAY(IM,N,T) ((T *)im_malloc((IM),(N) * sizeof(T))) + +Both IM_ARRAY and IM_NEW take an image descriptor as their first +parameter. Memory is allocated local to this descriptor, that is, when the +descriptor is closed, the memory is automatically freed for you. If you +pass NULL instead of an image descriptor, memory is allocated globally and +is not automatically freed. + +(NOTE: in versions of VIPS before 7.3, NEW(3) and ARRAY(3) did not have the +initial IMAGE parameter. If you are converting an old VIPS7.2 program, you +will need to add a NULL parameter to the start of all NEW(3) and ARRAY(3) +parameter lists.) + +Both functions return NULL on error, setting im_errorstring. + +Example: + + #include + #include + + /* A structure we want to carry about. + */ + typedef struct { + ... + } Wombat; + + /* A static array of them. + */ + static Wombat swarm[] = { + { ... }, + { ... }, + { ... } + }; + static int swarm_size = IM_NUMBER( swarm ); + + int + transform_wombat( IMAGE *in, IMAGE *out ) + { + /* Allocate space for a Wombat. + */ + Wombat *mar = IM_NEW( out, Wombat ); + + /* Allocate space for a copy of swarm. + */ + Wombat *mar = IM_ARRAY( out, swarm_size, Wombat ); + + .... + } + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_malloc(3), im_open_local(3). +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_ADDR.3 b/libsrc/iofuncs/man3/IM_IMAGE_ADDR.3 new file mode 100644 index 00000000..bdbc8709 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_ADDR.3 @@ -0,0 +1,66 @@ +.TH MACROS 3 "11 April 1990" +.SH NAME +IM_IMAGE_ADDR, IM_IMAGE_SIZEOF_ELEMENT, IM_IMAGE_SIZEOF_PEL, +IM_IMAGE_SIZEOF_LINE, IM_IMAGE_N_ELEMENTS \- +macros for images +.SH SYNOPSIS +.B #include + +int IM_IMAGE_SIZEOF_ELEMENT( im ) +.br +IMAGE *im; + +int IM_IMAGE_SIZEOF_PEL( im ) +.br +IMAGE *im; + +int IM_IMAGE_SIZEOF_LINE( im ) +.br +IMAGE *im; + +int IM_IMAGE_N_ELEMENTS( im ) +.br +IMAGE *im; + +char *IM_IMAGE_ADDR( im, x, y ) +.br +IMAGE *im; +.br +int x; +.br +int y; + +.SH DESCRIPTION +These macros help to simplify address arithmetic for images. + +IM_IMAGE_SIZEOF_ELEMENT(3) returns sizeof( one band element ). + +IM_IMAGE_SIZEOF_PEL(3) returns sizeof( one pel ). + +IM_IMAGE_SIZEOF_LINE(3) returns sizeof( one horizontal line of pels ). + +IM_IMAGE_N_ELEMENTS(3) returns the number of band elements across a horizontal line. + +IM_IMAGE_ADDR(3) returns a pointer to the pixel at position (x,y) in the +image. The point (x,y) should lie within the image. + +If the macro DEBUG has been defined, then IM_IMAGE_ADDR(3) will also +perform bounds checking. If you ask for the address of a pel outside the +image, +then IM_IMAGE_ADDR(3) will print an error message of the form: + + IM_IMAGE_ADDR: point out of bounds, file "test.c", line 18 + (point x=50, y=0 + should have been within Rect left=0, top=0, width=50, height=50) + +and call abort(3). + +DEBUG needs to be defined *before* vips.h is included. Either define DEBUG +with -D in your Makefile, or have a #define DEBUG right at the top of your +file. +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_malloc(3), im_open_local(3). +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_N_ELEMENTS.3 b/libsrc/iofuncs/man3/IM_IMAGE_N_ELEMENTS.3 new file mode 100644 index 00000000..0e4c07b3 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_N_ELEMENTS.3 @@ -0,0 +1 @@ +.so man3/IM_IMAGE_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_ELEMENT.3 b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_ELEMENT.3 new file mode 100644 index 00000000..0e4c07b3 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_ELEMENT.3 @@ -0,0 +1 @@ +.so man3/IM_IMAGE_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_LINE.3 b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_LINE.3 new file mode 100644 index 00000000..0e4c07b3 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_LINE.3 @@ -0,0 +1 @@ +.so man3/IM_IMAGE_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_PEL.3 b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_PEL.3 new file mode 100644 index 00000000..0e4c07b3 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_IMAGE_SIZEOF_PEL.3 @@ -0,0 +1 @@ +.so man3/IM_IMAGE_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_MAX.3 b/libsrc/iofuncs/man3/IM_MAX.3 new file mode 100644 index 00000000..b83855ef --- /dev/null +++ b/libsrc/iofuncs/man3/IM_MAX.3 @@ -0,0 +1 @@ +.so man3/IM_RINT.3 diff --git a/libsrc/iofuncs/man3/IM_MIN.3 b/libsrc/iofuncs/man3/IM_MIN.3 new file mode 100644 index 00000000..b83855ef --- /dev/null +++ b/libsrc/iofuncs/man3/IM_MIN.3 @@ -0,0 +1 @@ +.so man3/IM_RINT.3 diff --git a/libsrc/iofuncs/man3/IM_NEW.3 b/libsrc/iofuncs/man3/IM_NEW.3 new file mode 100644 index 00000000..c891c6dd --- /dev/null +++ b/libsrc/iofuncs/man3/IM_NEW.3 @@ -0,0 +1 @@ +.so man3/IM_ARRAY.3 diff --git a/libsrc/iofuncs/man3/IM_NUMBER.3 b/libsrc/iofuncs/man3/IM_NUMBER.3 new file mode 100644 index 00000000..c891c6dd --- /dev/null +++ b/libsrc/iofuncs/man3/IM_NUMBER.3 @@ -0,0 +1 @@ +.so man3/IM_ARRAY.3 diff --git a/libsrc/iofuncs/man3/IM_RECT_BOTTOM.3 b/libsrc/iofuncs/man3/IM_RECT_BOTTOM.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RECT_BOTTOM.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/IM_RECT_HCENTRE.3 b/libsrc/iofuncs/man3/IM_RECT_HCENTRE.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RECT_HCENTRE.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/IM_RECT_RIGHT.3 b/libsrc/iofuncs/man3/IM_RECT_RIGHT.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RECT_RIGHT.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/IM_RECT_VCENTRE.3 b/libsrc/iofuncs/man3/IM_RECT_VCENTRE.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RECT_VCENTRE.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/IM_REGION_ADDR.3 b/libsrc/iofuncs/man3/IM_REGION_ADDR.3 new file mode 100644 index 00000000..206f8c0a --- /dev/null +++ b/libsrc/iofuncs/man3/IM_REGION_ADDR.3 @@ -0,0 +1,65 @@ +.TH MACROS 3 "11 April 1990" +.SH NAME +IM_REGION_ADDR, +IM_REGION_LSKIP, +IM_REGION_N_ELEMENTS, IM_REGION_SIZEOF_LINE \- +macros for regions +.SH SYNOPSIS +.B #include +.br +.B #include + +int IM_REGION_LSKIP( reg ) +.br +REGION *reg; + +int IM_REGION_N_ELEMENTS( reg ) +.br +REGION *reg; + +int IM_REGION_SIZEOF_LINE( reg ) +.br +REGION *reg; + +char *IM_REGION_ADDR( reg, x, y ) +.br +REGION *reg; +.br +int x, y; + +.SH DESCRIPTION +These macros help to simplify address arithmetic for regions. + +IM_REGION_LSKIP(3) returns the number of *bytes* you should add to move +down a scan line. Remember that if your pointer has been cast to the type +of the image pels, this will not be the correct amount to add! The value +lskip returns can be changed by a call to im_prepare(3). + +IM_REGION_N_ELEMENTS(3) returns the number of band elements across the region. + +IM_REGION_SIZEOF_LINE(3) returns sizeof( horizontal line across region ). + +IM_REGION_ADDR(3) returns a pointer to the pixel at position (x,y) in the +image on which reg has been defined. The point (x,y) should lie within the +valid area for this region. + +If the macro DEBUG has been defined, then IM_REGION_ADDR(3) will also +perform bounds checking. If you ask for the address of a pel outside the rect +reg->valid, then IM_REGION_ADDR(3) will print an error message of the form: + + IM_REGION_ADDR: point out of bounds, file "test.c", line 18 + (point x=50, y=0 + should have been within Rect left=0, top=0, width=50, height=50) + +and call abort(3). + +DEBUG needs to be defined *before* region.h is included. Either define DEBUG +with -D in your Makefile, or have a #define DEBUG right at the top of your +file. + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_malloc(3), im_open_local(3). +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/IM_REGION_LSKIP.3 b/libsrc/iofuncs/man3/IM_REGION_LSKIP.3 new file mode 100644 index 00000000..9fc98c69 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_REGION_LSKIP.3 @@ -0,0 +1 @@ +.so man3/IM_REGION_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_REGION_N_ELEMENTS.3 b/libsrc/iofuncs/man3/IM_REGION_N_ELEMENTS.3 new file mode 100644 index 00000000..9fc98c69 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_REGION_N_ELEMENTS.3 @@ -0,0 +1 @@ +.so man3/IM_REGION_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_REGION_SIZEOF_LINE.3 b/libsrc/iofuncs/man3/IM_REGION_SIZEOF_LINE.3 new file mode 100644 index 00000000..9fc98c69 --- /dev/null +++ b/libsrc/iofuncs/man3/IM_REGION_SIZEOF_LINE.3 @@ -0,0 +1 @@ +.so man3/IM_REGION_ADDR.3 diff --git a/libsrc/iofuncs/man3/IM_RINT.3 b/libsrc/iofuncs/man3/IM_RINT.3 new file mode 100644 index 00000000..210c399c --- /dev/null +++ b/libsrc/iofuncs/man3/IM_RINT.3 @@ -0,0 +1,36 @@ +.TH MACROS 3 "11 April 1990" +.SH NAME +IM_RINT, IM_MAX, IM_MIN \- misc math macros +.SH SYNOPSIS + +.B #include +.br +.B #include + +int IM_RINT( float ) +.br +any IM_MAX( any, any ) +.br +any IM_MIN( any, any ) + +.SH DESCRIPTION + +These macros provide some simple but fast math functions --- IM_MAX(3) +returns the maximum of its two arguments, IM_MIN(3) the smallest, and +IM_RINT(3) rounds a float or double to the nearest integer. + +Beware: these macros may evaluate their argument more than once, so you MUST +NOT use ++,--, or a function call in their argument lists. + +They are defined as: + + #define IM_MAX(A,B) ((A)>(B)?(A):(B)) + #define IM_MIN(A,B) ((A)<(B)?(A):(B)) + #define IM_RINT(R) ((int)((R)>0?((R)+0.5):((R)-0.5))) + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_malloc(3), im_open_local(3). +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/Makefile.am b/libsrc/iofuncs/man3/Makefile.am new file mode 100644 index 00000000..576eaa52 --- /dev/null +++ b/libsrc/iofuncs/man3/Makefile.am @@ -0,0 +1,165 @@ +man_MANS = \ + IM_ARRAY.3 \ + IM_IMAGE_ADDR.3 \ + IM_IMAGE_N_ELEMENTS.3 \ + IM_IMAGE_SIZEOF_ELEMENT.3 \ + IM_IMAGE_SIZEOF_LINE.3 \ + IM_IMAGE_SIZEOF_PEL.3 \ + IM_MAX.3 \ + IM_MIN.3 \ + IM_NEW.3 \ + IM_NUMBER.3 \ + IM_REGION_ADDR.3 \ + IM_REGION_LSKIP.3 \ + IM_REGION_N_ELEMENTS.3 \ + IM_REGION_SIZEOF_LINE.3 \ + IM_RINT.3 \ + IM_RECT_RIGHT.3 \ + IM_RECT_BOTTOM.3 \ + IM_RECT_HCENTRE.3 \ + IM_RECT_VCENTRE.3 \ + error_exit.3 \ + im_BandFmt2char.3 \ + im_Coding2char.3 \ + im_Compression2char.3 \ + im_Type2char.3 \ + im_add_close_callback.3 \ + im_add_eval_callback.3 \ + im_add_evalend_callback.3 \ + im_allocate_input_array.3 \ + im_amiMSBfirst.3 \ + im_binfile.3 \ + im_bits_of_fmt.3 \ + im_char2BandFmt.3 \ + im_char2Coding.3 \ + im_char2Compression.3 \ + im_char2Type.3 \ + im_concurrency_set.3 \ + im_concurrency_get.3 \ + im_error_buffer.3 \ + im_verror.3 \ + im_error_clear.3 \ + im_close.3 \ + im_cp_desc.3 \ + im_cp_descv.3 \ + im_cp_desc_array.3 \ + im_debugim.3 \ + im_demand_hint.3 \ + im_demand_hint_array.3 \ + im_diag.3 \ + im_error.3 \ + im_generate.3 \ + im_guess_prefix.3 \ + im_header.3 \ + im_header_double.3 \ + im_header_int.3 \ + im_header_string.3 \ + im_header_get.3 \ + im_header_get_type.3 \ + im_header_map.3 \ + im_histlin.3 \ + im_history_get.3 \ + im_image.3 \ + im_image_sanity.3 \ + im_incheck.3 \ + im_init.3 \ + im_init_world.3 \ + im_get_option_group.3 \ + im_initdesc.3 \ + im_iocheck.3 \ + im_isMSBfirst.3 \ + im_iscomplex.3 \ + im_isfile.3 \ + im_isfloat.3 \ + im_isint.3 \ + im_isscalar.3 \ + im_isjpeg.3 \ + im_ispartial.3 \ + im_ispng.3 \ + im_isppm.3 \ + im_istifftiled.3 \ + im_isuint.3 \ + im_isvips.3 \ + im_iterate.3 \ + im_list_add.3 \ + im_list_append.3 \ + im_list_eq.3 \ + im_list_fix.3 \ + im_list_fold.3 \ + im_list_free.3 \ + im_list_index.3 \ + im_list_insert.3 \ + im_list_len.3 \ + im_list_map.3 \ + im_render.3 \ + im_render_fade.3 \ + im_cache.3 \ + im_list_map_rev.3 \ + im_list_member.3 \ + im_list_pos.3 \ + im_list_remove.3 \ + im_makerw.3 \ + im_malloc.3 \ + im_free.3 \ + im_mmapin.3 \ + im_mmapinrw.3 \ + im_open.3 \ + im_open_local.3 \ + im_open_local_array.3 \ + im_openout.3 \ + im_outcheck.3 \ + im_partial.3 \ + im_pincheck.3 \ + im_piocheck.3 \ + im_poutcheck.3 \ + im_prepare.3 \ + im_prepare_many.3 \ + im_prepare_to.3 \ + im_printdesc.3 \ + im_printlines.3 \ + im_rect_dup.3 \ + im_rect_equalsrect.3 \ + im_rect_includespoint.3 \ + im_rect_includesrect.3 \ + im_rect_intersectrect.3 \ + im_rect_isempty.3 \ + im_rect_marginadjust.3 \ + im_rect_normalise.3 \ + im_rect_unionrect.3 \ + im_region_create.3 \ + im_region_free.3 \ + im_region_image.3 \ + im_region_buffer.3 \ + im_region_position.3 \ + im_region_region.3 \ + im_setbuf.3 \ + im_setupout.3 \ + im_start_many.3 \ + im_start_one.3 \ + im_stop_many.3 \ + im_stop_one.3 \ + im_updatehist.3 \ + im_version.3 \ + im_version_string.3 \ + im_warn.3 \ + im_wrapmany.3 \ + im_wrapone.3 \ + im_writeline.3 \ + im_meta.3 \ + im_meta_get.3 \ + im_meta_get_area.3 \ + im_meta_get_double.3 \ + im_meta_get_int.3 \ + im_meta_get_string.3 \ + im_meta_get_type.3 \ + im_meta_get_blob.3 \ + im_meta_set.3 \ + im_meta_set_area.3 \ + im_meta_set_double.3 \ + im_meta_set_int.3 \ + im_meta_set_blob.3 \ + im_meta_set_string.3 \ + im_invalidate.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/iofuncs/man3/error_exit.3 b/libsrc/iofuncs/man3/error_exit.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/error_exit.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_BandFmt2char.3 b/libsrc/iofuncs/man3/im_BandFmt2char.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_BandFmt2char.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_Coding2char.3 b/libsrc/iofuncs/man3/im_Coding2char.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_Coding2char.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_Compression2char.3 b/libsrc/iofuncs/man3/im_Compression2char.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_Compression2char.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_Type2char.3 b/libsrc/iofuncs/man3/im_Type2char.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_Type2char.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_add_close_callback.3 b/libsrc/iofuncs/man3/im_add_close_callback.3 new file mode 100644 index 00000000..0be9c41f --- /dev/null +++ b/libsrc/iofuncs/man3/im_add_close_callback.3 @@ -0,0 +1 @@ +.so man3/im_malloc.3 diff --git a/libsrc/iofuncs/man3/im_add_eval_callback.3 b/libsrc/iofuncs/man3/im_add_eval_callback.3 new file mode 100644 index 00000000..0be9c41f --- /dev/null +++ b/libsrc/iofuncs/man3/im_add_eval_callback.3 @@ -0,0 +1 @@ +.so man3/im_malloc.3 diff --git a/libsrc/iofuncs/man3/im_add_evalend_callback.3 b/libsrc/iofuncs/man3/im_add_evalend_callback.3 new file mode 100644 index 00000000..0be9c41f --- /dev/null +++ b/libsrc/iofuncs/man3/im_add_evalend_callback.3 @@ -0,0 +1 @@ +.so man3/im_malloc.3 diff --git a/libsrc/iofuncs/man3/im_allocate_input_array.3 b/libsrc/iofuncs/man3/im_allocate_input_array.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_allocate_input_array.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_amiMSBfirst.3 b/libsrc/iofuncs/man3/im_amiMSBfirst.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_amiMSBfirst.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_binfile.3 b/libsrc/iofuncs/man3/im_binfile.3 new file mode 100644 index 00000000..1c7d7f93 --- /dev/null +++ b/libsrc/iofuncs/man3/im_binfile.3 @@ -0,0 +1,42 @@ +.TH IM_BINFILE 3 "11 April 1990" +.SH NAME +im_binfile, im_image \- wrap a raw binary file inside an IMAGE descriptor +.SH SYNOPSIS +.B #include + +IMAGE *im_binfile( name, xs, ys, b, off ) +.br +char *in; +.br +IMAGE *out; +.br +int xs, ys, b, off; + +IMAGE * +.br +im_image( void *buffer, int width, int height, int bands, int format ) + +.SH DESCRIPTION +.B im_binfile(3) +maps the file named, and returns an image descriptor which looks +very like the sort of thing that +.B im_mmapin(3) +returns. + +The parameters specify the image width, height, number of bands and offset +in bytes from the start of the file. + +.B im_image(3) +makes an IMAGE deriptor from an area of pixels in memory. The memory +buffer will not be freed when the IMAGE is closed, use +.B im_add_close_callback() +if you want this. + +.SH RETURN VALUE +The functions return NULL on error. +.SH SEE ALSO +im_mmapin(3), im_openout(3), im_setbuf(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_bits_of_fmt.3 b/libsrc/iofuncs/man3/im_bits_of_fmt.3 new file mode 100644 index 00000000..f4ee23ed --- /dev/null +++ b/libsrc/iofuncs/man3/im_bits_of_fmt.3 @@ -0,0 +1,16 @@ +.TH IM_BITS_OF_FMT 3 "2 June 2005" +.SH NAME +im_bits_of_fmt \- return number of bits of band format +.SH SYNOPSIS +.B #include + +.B int im_bits_of_fmt( int bandfmt ) + +.SH DESCRIPTION +.B im_bits_of_fmt(3) +calculates the number of bits of band format. + +.SH RETURN VALUE +On success this function returns the number of bits of band format and +-1 is returned on error. + diff --git a/libsrc/iofuncs/man3/im_cache.3 b/libsrc/iofuncs/man3/im_cache.3 new file mode 100644 index 00000000..b7647daa --- /dev/null +++ b/libsrc/iofuncs/man3/im_cache.3 @@ -0,0 +1 @@ +.so man3/im_render.3 diff --git a/libsrc/iofuncs/man3/im_char2BandFmt.3 b/libsrc/iofuncs/man3/im_char2BandFmt.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_char2BandFmt.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_char2Coding.3 b/libsrc/iofuncs/man3/im_char2Coding.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_char2Coding.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_char2Compression.3 b/libsrc/iofuncs/man3/im_char2Compression.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_char2Compression.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_char2Type.3 b/libsrc/iofuncs/man3/im_char2Type.3 new file mode 100644 index 00000000..72471e9c --- /dev/null +++ b/libsrc/iofuncs/man3/im_char2Type.3 @@ -0,0 +1 @@ +.so man3/im_printdesc.3 diff --git a/libsrc/iofuncs/man3/im_close.3 b/libsrc/iofuncs/man3/im_close.3 new file mode 100644 index 00000000..d7e21787 --- /dev/null +++ b/libsrc/iofuncs/man3/im_close.3 @@ -0,0 +1,35 @@ +.TH IM_CLOSE 3 "11 April 1990" +.SH NAME +im_close \- close an image descriptor +.SH SYNOPSIS +#include + +int im_close(image) +.br +IMAGE *image; +.SH DESCRIPTION +im_close(3) frees all the resources attached to the image descriptor. This may +involve closing files, freeing memory buffers, triggering close callback +lists, unmapping files, freeing regions, and so on. If all this succeeds, then +the function returns zero. If something goes wrong, the function returns +non-zero and sets im_errormsg(3). If im_close(3) fails, the image descriptor is +left in an undefined state. + +In the case that the image descriptor corresponds to a file opened by +im_openout(3) that has been written to, the function sets an output +description file as follows: If the output image filename is terminated with +".v", the string held by the Hist member of the image descriptor is copied to +a corresponding file ending with ".desc" in the same directory. In all other +cases, an output .desc file is not created. + +If a NULL pointer is passed to im_close(3), it returns successfully +immediately. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH COPYRIGHT +N. Dessipris, K. Martinez, J. Cupitt +.SH SEE ALSO +im_mmapin(3), im_openin(3), im_openout(3), im_setbuf(3), im_open(3), +im_incheck(3), im_pincheck(3). +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_concurrency_get.3 b/libsrc/iofuncs/man3/im_concurrency_get.3 new file mode 100644 index 00000000..9c8864dd --- /dev/null +++ b/libsrc/iofuncs/man3/im_concurrency_get.3 @@ -0,0 +1 @@ +.so man3/im_concurrency_set.3 diff --git a/libsrc/iofuncs/man3/im_concurrency_set.3 b/libsrc/iofuncs/man3/im_concurrency_set.3 new file mode 100644 index 00000000..5b9938f6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_concurrency_set.3 @@ -0,0 +1,32 @@ +.TH IM_CONCURRENCY 3 "8 January 2007" +.SH NAME +im_concurrency_set, im_concurrency_get \- set and get the number of threads to +use for evaluation +.SH SYNOPSIS +.B #include + +void im_concurrency_set( int concurrency ) +.br +int im_concurrency_get( void ) + +.SH DESCRIPTION + +.B im_concurrency_set(3) +and +.B im_concurrency_get(3) +set and get the number of parallel threads VIPS should use to calculate +pixels. + +A value of zero (the default) means to get the number of threads +from the environment variable IM_CONCURRENCY. If that is not set, the number +of threads defaults to one. + +Setting the number of threads only affects image evaluations which start after +that point, it will not change the behaviour of existing evaluations. + +Most command-line vips programs support the --vips-concurrency flag, which can +also be used to set the concurrency. + +.SH SEE ALSO +im_get_option_group(3), +`VIPS manual,' in accompanying documentation. diff --git a/libsrc/iofuncs/man3/im_cp_desc.3 b/libsrc/iofuncs/man3/im_cp_desc.3 new file mode 100644 index 00000000..89627abb --- /dev/null +++ b/libsrc/iofuncs/man3/im_cp_desc.3 @@ -0,0 +1,48 @@ +.TH IM_CP_DESC 3 "11 April 1990" +.SH NAME +im_cp_desc, im_append_Hist \- copy most of an image descriptor to another +image descriptor. +.SH SYNOPSIS +.B #include + +int im_cp_desc_array( IMAGE *out, IMAGE *in[] ); + +int im_cp_descv( IMAGE *out, IMAGE *in1, ... ); + +.B int im_cp_desc( IMAGE *out, IMAGE *in ); + +.B int im_append_Hist( IMAGE *out, IMAGE *in ); + +.SH DESCRIPTION +.B im_cp_desc_array(3) +takes an output image and a NULL-terminated list of input images, which must +contain at least one input image. + +It copies the fields describing the size, bands, type, resolution, and +coding from the first input image to the output image. History and meta from +all images are copied over. If two input images have the same meta tag, the +earlier one takes precedence. + +.B im_cp_descv(3) +is a varargs conveniece function for +.B im_cp_desc_array(3). + +.B im_cp_desc(3) +is a convenience function which calls +.B im_cp_descv( image1, image2, NULL ). + +.B im_append_Hist(3) +appends the history attached to image2 to the end of the history on image1. It +is used by image processing functions which take more than one image as input, +and which need to make sure that all the input history appears in the output. + +The first line of image2 history is not copied, as this conventionally holds +background information which is not part of the file history. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_cp_desc_array.3 b/libsrc/iofuncs/man3/im_cp_desc_array.3 new file mode 100644 index 00000000..33ff1a6b --- /dev/null +++ b/libsrc/iofuncs/man3/im_cp_desc_array.3 @@ -0,0 +1 @@ +.so man3/im_cp_desc.3 diff --git a/libsrc/iofuncs/man3/im_cp_descv.3 b/libsrc/iofuncs/man3/im_cp_descv.3 new file mode 100644 index 00000000..33ff1a6b --- /dev/null +++ b/libsrc/iofuncs/man3/im_cp_descv.3 @@ -0,0 +1 @@ +.so man3/im_cp_desc.3 diff --git a/libsrc/iofuncs/man3/im_debugim.3 b/libsrc/iofuncs/man3/im_debugim.3 new file mode 100644 index 00000000..c2712a8a --- /dev/null +++ b/libsrc/iofuncs/man3/im_debugim.3 @@ -0,0 +1,31 @@ +.TH IM_DEBUG 3 "22 April 1991" +.SH NAME +im_debug, im_printlines \- print raw image data pointed by an image descriptor +.SH SYNOPSIS +.B #include + +.B void im_debugim( image ) +.br +.B IMAGE *image; + +.B void im_printlines( image ) +.br +.B IMAGE *image; +.SH DESCRIPTION +.B im_debugim(3) +prints to the standard error output raw data pointed by image sequentially. +Data are printed as float numbers and the function interprets the input format +properly. If input is complex then for each pixel the real part followed +by the imaginary is printed. It can be used for debugging preferably on small +images. If input is uchar then the printed values are integers. + +.B im_printlines +printes in the standard error output the no of line followed by CR, followed +by the line pixel values. For each pixel, the x location +followed by the pixel value(s) is printed. The location and the pixels value(s) +are separated by a tab. Each pixel is printed on a separate line. +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_demand_hint.3 b/libsrc/iofuncs/man3/im_demand_hint.3 new file mode 100644 index 00000000..0b7623e0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_demand_hint.3 @@ -0,0 +1,132 @@ +.TH IM_IOCHECK 3 "11 April 1990" +.SH NAME +im_demand_hint \- hint on demand style for im_generate(3) +.SH SYNOPSIS +#include +.br +#include + +int im_demand_hint( im, hint, in1, in2, ..., NULL ) +.br +IMAGE *im, *in1, *in2, ...; +.br +im_demand_type hint; + +int im_demand_hint_array( im, hint, in ) +.br +IMAGE *im, **in; +.br +im_demand_type hint; +.SH DESCRIPTION +.B im_demand_hint(3) +suggests to im_generate(3) the sorts of demand with which this image processing +operation would be happiest. + +.B im +is the image this operation is generating. +.B hint +is the demand style this operation would like (see below), and +.B in1 ... +is a NULL-terminated list of the image upon which this output image directly +depends, that is, the images which this operation will call im_prepare(3) for. + +This list of parent images is necessary, as im_demand_hint(3) needs to know +what demand style this operation's ancestors have requested. If an ancestor of +this operation has specified a very restrictive demand style, then this +operation must fall back to that restrictive style and ignore the hint given +in this call to im_demand_hint(3). + +VIPS currently supports three demand styles. More may be added in the future. +These demand styles are given below in order of increasing restrictiveness. +When demanding output from a pipeline, im_generate(3) will use the most +restrictive of the styles requested by the operations in the pipeline. + +IM_THINSTRIP +.br +This operation would like to output strips the width of the image and a few +pels high. This is option suitable for point-to-point operations, such as +those in the arithmetic package. + +This option is only efficient for cases where each output pel depends upon the +pel in the corresponding position in the input image. + +IM_FATSTRIP +.br +This operation would like to output strips the width of the image and as high +as possible. This option is suitable for area operations which do not +violently transform coordinates, such as im_conv(3). + +IM_SMALLTILE +.br +This is the most general demand format, and is the default. Output is +demanded in small (around 100x100 pel) sections. This style works reasonably +efficiently, even for bizzare operations like 45 degree rotate. + +IM_ANY +.br +This image is not being demand-read from a disc file (even indirectly) so any +demand style is OK. It's used for things like +.B im_black(3) +where the pixels are calculated. + +.B +im_demand_hint_array(3) +works exactly as im_demand_hint(3), but expects a pointer to a NULL-terminated +array of parent images as its third argument. You may use +im_allocate_input_array(3), if you wish, to build this structure. + +As an example, here is part of the code for im_invert(3). In this operation, +each output pel depends upon the corresponding input pel. In other words, +there is no coordinate transformation in im_prepare(3). This style of +operation is most efficient with IM_THINSTRIP IO. + +int im_invert( IMAGE *in, IMAGE *out ) +.br +{ +.br + if( in->Coding != NOCODING ) { +.br + im_errormsg( "im_invert: input coded" ); +.br + return( -1 ); +.br + } +.br + if( in->BandFmt != FMTUCHAR ) { +.br + im_errormsg( "im_invert: input not UCHAR" ); +.br + return( -1 ); +.br + } +.br + if( im_piocheck( in, out ) ) +.br + return( -1 ); +.br + if( im_cp_desc( out, in ) ) +.br + return( -1 ); +.br + if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) +.br + return( -1 ); +.br + if( im_generate( out, +.br + im_start_one, inv_gen, im_stop_one, in, NULL ) ) +.br + return( -1 ); +.br + return( 0 ); +.br +} + +.SH RETURN VALUE +All functions returns 0 on success and non-zero on error. +.SH SEE ALSO +im_generate(3), im_prepare(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 3/9/93 diff --git a/libsrc/iofuncs/man3/im_demand_hint_array.3 b/libsrc/iofuncs/man3/im_demand_hint_array.3 new file mode 100644 index 00000000..124bcb4a --- /dev/null +++ b/libsrc/iofuncs/man3/im_demand_hint_array.3 @@ -0,0 +1 @@ +.so man3/im_demand_hint.3 diff --git a/libsrc/iofuncs/man3/im_diag.3 b/libsrc/iofuncs/man3/im_diag.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_diag.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_error.3 b/libsrc/iofuncs/man3/im_error.3 new file mode 100644 index 00000000..884328a5 --- /dev/null +++ b/libsrc/iofuncs/man3/im_error.3 @@ -0,0 +1,84 @@ +.TH IM_ERRORMSG 3 "22 April 1991" +.SH NAME +im_error_buffer, im_verror, im_error, im_error_clear, im_warn, im_diag, +error_exit \- handle error messages from VIPS +.SH SYNOPSIS +.B #include +.B #include + +.B const char *im_error_buffer( void ) + +.B void im_verror( const char *domain, const char *fmt, va_list ap ) + +.B void im_error( const char *domain, const char *fmt, ... ) + +.B void im_error_system( int errno, const char *domain, const char *fmt, ... ) + +.B void im_error_clear() + +.B void im_warn( const char *domain, const char *fmt, ... ) + +.B void im_diag( const char *domain, const char *fmt, ... ) + +.B void error_exit( const char *fmt, ... ) + +.SH DESCRIPTION +.B im_error(3) +formats its arguments as printf and appends the string, with a newline, to +the error buffer. The +.B domain +argument indicates the error source and should not be marked for translation. + +For example, the call: + + im_error( "mystuff", _( "bad argument %d" ), a ); + +might appear in the error buffer as: + + mystuff: bad argument 12 + +.B im_verror(3) +works exactly as +.B im_error(3) +but takes stdarg arguments. + +.B im_error_system(3) +works exactly as +.B im_error(3) +but additionally will translate and append a system error code. + +.B im_error_buffer(3) +returns a pointer to the start of the error buffer. + +.B im_error_clear(3) +empties the error buffer. + +.B error_exit(3) +formats its arguments as printf and sends the result to the error output, +together with the contents of the error log, before terminating with an error +status. + +.B im_warn(3) +works as +.B im_error(), +but output is sent to the list of warnings. + +If an environment variable IM_WARNING exists, messages are suppressed. +Warnings should be used for non-critical recoverable errors such as values +being clipped. + +.B im_diag(3) +works as +.B im_error(), +but output is sent to the list of diagnosic errors. + +If an environment variable IM_DIAGNOSTICS exists, messages are suppressed. +Diagnostics should be used to give extra feedback about the result of the +operation. +.SH SEE ALSO +error_exit(3), im_intro(3). +.SH COPYRIGHT +.br +Birkbeck College +.SH AUTHOR +N. Dessipris \- 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_error_buffer.3 b/libsrc/iofuncs/man3/im_error_buffer.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_error_buffer.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_error_clear.3 b/libsrc/iofuncs/man3/im_error_clear.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_error_clear.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_free.3 b/libsrc/iofuncs/man3/im_free.3 new file mode 100644 index 00000000..0be9c41f --- /dev/null +++ b/libsrc/iofuncs/man3/im_free.3 @@ -0,0 +1 @@ +.so man3/im_malloc.3 diff --git a/libsrc/iofuncs/man3/im_generate.3 b/libsrc/iofuncs/man3/im_generate.3 new file mode 100644 index 00000000..0b183a4e --- /dev/null +++ b/libsrc/iofuncs/man3/im_generate.3 @@ -0,0 +1,215 @@ +.TH IM_GENERATE 3 "11 April 1993" +.SH NAME +im_generate, im_start_one, im_stop_one, im_allocate_input_array, +im_start_many, im_stop_many \- generate image pixels +.SH SYNOPSIS +.B #include +.br +.B #include + +void *im_start_one( out, in ) +.br +IMAGE *out, *in; + +int im_stop_one( reg ) +.br +REGION *reg; + +IMAGE **im_allocate_input_array( IMAGE *out, ... ) + +void *im_start_many( out, in ) +.br +IMAGE *out, **in; + +int im_stop_many( REGION **out ) +.br +REGION **out; + +int im_generate( im, + start_fn, gen_fn, stop_fn, void *a, void *b ) +.br +IMAGE *im; +.br +void *(*start_fn)(); +.br +int (*gen_fn)(); +.br +int (*stop_fn)(); +.br +void *a, void *b; + +where, typically, + +void *start_fn( im, a, b ) +.br +IMAGE *im; +.br +void *a, *b; + +int gen_fn( or, seq, a, b ) +.br +REGION *or; +.br +void *seq; +.br +void *a, *b; + +int stop_fn( seq, a, b ) +.br +void *seq; +.br +void *a, *b; +.SH DESCRIPTION +.B im_generate(3), +with its supporting convenience functions, is used for +PIO image output. See also +.B im_wrapone(3) +for an easy alternative to +.B im_generate(3) +for simple image +processing operations. + +.B im_start_one(3) +and +.B im_stop_one(3) +are convenience functions, useful for +simple one-image-in, one-image-out operations. +.B im_start_one(3) +assumes the +first of the two user arguments (a, above) is the input image. It creates a +REGION on this image and returns a pointer to the region as a sequence value. + +.B im_stop_one(3) +assumes the sequence value is a REGION pointer, and frees it. + +.B im_allocate_input_array(3) takes as arguments the output image and a list of +input images, terminated with a NULL. It allocates a NULL-terminated array to +hold the images, and attaches a close callback to the output image to free +that array. Example: + + IMAGE *in, *in2, *in3, *in4; + IMAGE **arry; + + if( !(arry = im_allocate_input_array( out, + in1, in2, in3, in4, NULL )) ) + return( -1 ); + +builds the structure + + IMAGE *arry[] = { in1, in2, in3, in4, NULL }; + +and makes sure it will be freed. + +.B im_start_many(3) +and +.B im_stop_many(3) +work exactly as +.B im_start_one(3) +and +.B im_stop_one(3), +but with NULL-terminated arrays of IMAGEs and REGIONs. +They are useful for many-images-in, one-image-out operations. +.B im_start_many(3) +assumes that the first of the two user arguments is a pointer +to a NULL-terminates array of IMAGEs. It builds and returns as the sequence +value a NULL-terminated array of REGIONs. + +.B im_stop_many(3) +assumes the sequence value is a pointer to a NULL-terminated +array of REGIONs. It frees all the regions in turn. See +.B im_add(3) +for an +example of this pair of functions in action. + +.B im_generate(3) +looks at the type of im and acts accordingly: + + IM_PARTIAL: the start, process and stop functions are attached to the +region, and im_generate returns immediately. See +.B im_prepare(3). + + IM_SETBUF: memory for the output image is created and sequences +started to fill it. It is an error to write to the same buffer twice. + + IM_MMAPINRW: sequences are started, and asked to fill the image in patches. + + IM_OPENOUT: The output file is created and a header written to disc. A +buffer +large enough to hold GENERATE_TILE_HEIGHT complete horizontal lines is +created, and sequences started to fill this buffer. When the buffer has been +filled, the whole set of lines are flushed to disc in a single write(2) +operation, and work starts on the next set of lines. + +Any other image type is an error. +.B im_generate(3) +returns 0 for complete +success, and non-zero on failure. + + static int + wombat_gen( or, ir, in ) + REGION *or, *ir; + IMAGE *in; + { + ... process! + + return( 0 ); + } + + int + im_wombat( in, out ) + IMAGE *in, *out; + { + if( im_iocheck( in, out ) ) + return( -1 ); + + ... check parametersm check image descriptors + ... for type-compatibility, etc. etc. + + if( im_cp_desc( out, in ) ) + return( -1 ); + + ... set fields in out for the type of image you + ... wish to write + + if( im_generate( out, + im_start_one, wombat_gen, im_stop_one, + in, NULL ) ) + return( -1 ); + + return( 0 ); + } + +See also the source to +.B im_invert(3), +.B im_exptra(3), +and, if you are brave, +.B im_conv(3) +or +.B im_add(3). + +On machines with several CPUs, +.B im_generate(3) +and +.B im_iterate(3) +automatically parallelise programs. You can set the desired +concurrency level with the environment variable IM_CONCURRENCY, for example + + example% export IM_CONCURRENCY=2 + example% lintra 2.0 fred.v 0.0 fred2.v + +will run lintra with enough concurrency to keep 2 CPUs fully occupied. +If IM_CONCURRENCY is not set, then it defaults to 1. See also +im_concurrency_set(3). + +Most programs which use VIPS will also let you use the command-line argument +--vips-concurrency to set parallelisation, see im_get_option_group(3). + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_wrapone(3), im_add_eval_callback(3), im_iterate(3), im_piocheck(3), +im_concurrency_set(3), +im_get_option_group(3), +`VIPS manual,' in accompanying documentation. +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_get_option_group.3 b/libsrc/iofuncs/man3/im_get_option_group.3 new file mode 100644 index 00000000..73c527a0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_get_option_group.3 @@ -0,0 +1 @@ +.so man3/im_init_world.3 diff --git a/libsrc/iofuncs/man3/im_guess_prefix.3 b/libsrc/iofuncs/man3/im_guess_prefix.3 new file mode 100644 index 00000000..785d56b6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_guess_prefix.3 @@ -0,0 +1,29 @@ +.TH IM_VIPSHOME 3 "8 March 2001" +.SH NAME +im_guess_prefix \- try to guess install directory +.SH SYNOPSIS +.B #include + +.B const char *im_guess_prefix( const char *argv0, +.B const char *env_name ) + +.SH DESCRIPTION +.B im_guess_prefix(3) +tries to guess the install directory. You should pass in the value of +argv[0] (the name your program was run as) as a clue to help it out, plus +the name of the environment variable you let the user override your package +install area with (eg. "VIPSHOME"). + +On success, +.B im_guess_prefix(3) +returns the prefix it discovered, and as a side effect, sets the environment +variable (if it's not set). + +.B im_guess_prefix(3) +is useful for programs in the VIPS distribution and contrib which need to be +able to find data files. + +Don't free the return string! + +.SH RETURN VALUE +The function returns NULL on failure. diff --git a/libsrc/iofuncs/man3/im_header.3 b/libsrc/iofuncs/man3/im_header.3 new file mode 100644 index 00000000..88c4a435 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header.3 @@ -0,0 +1,57 @@ +.TH IM_HEADER 3 "7 May 2002" +.SH NAME +im_header_int, im_header_double, im_header_string, im_header_get, +im_header_get_type, im_header_map \- read fields from the image header +.SH SYNOPSIS +#include + +int im_header_int( IMAGE *image, const char *field, int *out ) +.br +int im_header_double( IMAGE *image, const char *field, double *out ) +.br +int im_header_string( IMAGE *image, const char *field, char **out ) +.br +GType im_header_get_type( IMAGE *im, const char *field ); +.br +int im_header_get( IMAGE *im, const char *field, GValue *value_copy ); +.br +typedef void *(*im_header_map_fn)( IMAGE *, + const char *, GValue *, void *, void * ); +.br +void *im_header_map( IMAGE *im, + im_header_map_fn fn, void *a, void *b ); + +.SH DESCRIPTION +.B im_header_int(3) +reads the value of an integer header field. These are +"Xsize", "Ysize", "Bands", "Bbits", "BandFmt", "Coding", and "Type", or any +integer meta field. + +.B im_header_double(3) +reads the value of the integer header fields. These are +"Xres", and "Yres", or any double meta field. + +.B im_header_string(3) +reads the value of the integer header fields. These are +"Hist", and "filename" or any string meta field. + +.B im_header_get_type(3) +returns the GType (eg. G_TYPE_INT) for a field. It returns zero if the field +does not exist. It does not set +.B im_error(3), +so it's useful for test for a field's existence. + +.B im_header_get(3) +fills the GValue with a copy of the field value, if the field exists. The +value should be zeroed but otherwise uninitialised. The value should be unset +once the user has finished with it. + +.B im_header_map(3) +maps a function over all header fields, presenting the value of each field as +a GValue. Return non-NULL from the map function to stop iteration early. It +maps over the builtin fields first, then any meta fields. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_close(3), im_open(3). diff --git a/libsrc/iofuncs/man3/im_header_double.3 b/libsrc/iofuncs/man3/im_header_double.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_double.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_get.3 b/libsrc/iofuncs/man3/im_header_get.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_get.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_get_type.3 b/libsrc/iofuncs/man3/im_header_get_type.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_get_type.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_int.3 b/libsrc/iofuncs/man3/im_header_int.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_int.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_map.3 b/libsrc/iofuncs/man3/im_header_map.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_map.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_header_string.3 b/libsrc/iofuncs/man3/im_header_string.3 new file mode 100644 index 00000000..eec90686 --- /dev/null +++ b/libsrc/iofuncs/man3/im_header_string.3 @@ -0,0 +1 @@ +.so man3/im_header.3 diff --git a/libsrc/iofuncs/man3/im_histlin.3 b/libsrc/iofuncs/man3/im_histlin.3 new file mode 100644 index 00000000..cd5bc3e0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_histlin.3 @@ -0,0 +1,34 @@ +.TH IM_HISTORY 3 "22 April 1991" +.SH NAME +im_histlin, im_updatehist, im_history_get \- manage image history +.SH SYNOPSIS +.B #include + +int im_histlin( IMAGE *im, const char *fmt, ... ); +.br +int im_updatehist( IMAGE *out, const char *name, int argc, char *argv[] ); +.br +const char *im_history_get( IMAGE *im ); + +.br +.SH DESCRIPTION +.B im_histlin(3) +formats its arguments as printf(3), appends " # time-and-date", and appends +the whole line of text to the image history. The string is typically the +command-line action that would be required to do whatever it is that you've +just done to the image. + +.B im_updatehist(3) +is given a standard argc/argv, formats them appropriately, and calls +im_histlin(3) for you. Note that the program name is passed separately. + +.B im_history_get(3) +returns the entire history of an image as a single C string, one action per +line. No need to free, but you mustn't modify either. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_cp_desc(3). +.SH COPYRIGHT +Imperial College, London, 2007. diff --git a/libsrc/iofuncs/man3/im_history_get.3 b/libsrc/iofuncs/man3/im_history_get.3 new file mode 100644 index 00000000..d421d4b0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_history_get.3 @@ -0,0 +1 @@ +.so man3/im_histlin.3 diff --git a/libsrc/iofuncs/man3/im_image.3 b/libsrc/iofuncs/man3/im_image.3 new file mode 100644 index 00000000..597a5913 --- /dev/null +++ b/libsrc/iofuncs/man3/im_image.3 @@ -0,0 +1 @@ +.so man3/im_binfile.3 diff --git a/libsrc/iofuncs/man3/im_image_sanity.3 b/libsrc/iofuncs/man3/im_image_sanity.3 new file mode 100644 index 00000000..f29d2047 --- /dev/null +++ b/libsrc/iofuncs/man3/im_image_sanity.3 @@ -0,0 +1,19 @@ +.TH IM_IMAGE_SANITY 3 "Feb 2001" +.SH NAME +im_image_sanity \- check image descriptors for sanity +.SH SYNOPSIS +.B #include + +int im_image_sanity( IMAGE *im ) + +.SH DESCRIPTION +.B im_image_sanity(3) +performs a few simple checks on the IMAGE for bad fields. If it finds a +problem, it returns -1, serts im_errorstring, and prints a warning message. + +It is called from various places within VIPS to try to catch errors early. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_incheck(3). diff --git a/libsrc/iofuncs/man3/im_incheck.3 b/libsrc/iofuncs/man3/im_incheck.3 new file mode 100644 index 00000000..e0092c33 --- /dev/null +++ b/libsrc/iofuncs/man3/im_incheck.3 @@ -0,0 +1 @@ +.so man3/im_iocheck.3 diff --git a/libsrc/iofuncs/man3/im_init.3 b/libsrc/iofuncs/man3/im_init.3 new file mode 100644 index 00000000..df1539fe --- /dev/null +++ b/libsrc/iofuncs/man3/im_init.3 @@ -0,0 +1,22 @@ +.TH IM_INIT 3 "11 April 1990" +.SH NAME +im_init \- make an IMAGE descriptor +.SH SYNOPSIS +#include + +IMAGE *im_init( char *filename ) +.SH DESCRIPTION +im_init(3) allocates space for an IMAGE descriptor, sets all fields to +initial values, and returns the descriptor. A copy is made of the filename +argument. The IMAGE returned by im_init(3) should be passed to im_close(3) to +free it. + +This function is used internally by VIPS and should not be called by users. +.SH SEE ALSO +im_mmapin(3), im_openin(3), im_setbuf(3), im_close(3). +.SH COPYRIGHT +Birkbeck College and the National Gallery (c) 1994 +.SH AUTHOR +N. Dessipris \- 11/04/1990 +.br +J.Cupitt \- 23/2/94 diff --git a/libsrc/iofuncs/man3/im_init_world.3 b/libsrc/iofuncs/man3/im_init_world.3 new file mode 100644 index 00000000..4374f029 --- /dev/null +++ b/libsrc/iofuncs/man3/im_init_world.3 @@ -0,0 +1,47 @@ +.TH IM_INIT_WORLD 3 "11 April 1990" +.SH NAME +im_init_world, im_get_option_group \- start up VIPS +.SH SYNOPSIS +#include + +int im_init_world( const char *argv0 ) +.br +GOptionGroup *im_get_option_group( void ); +.SH DESCRIPTION +.B im_init_world(3) +starts up the VIPS library. It: + + - initialises any libraries that VIPS is using, including GObject + - starts up the threading system + - guesses where the VIPS data files are and sets up i18n + - loads any plugins + +The +.B argv0 +argument is the value of +.B argv[0] +your program was passed by the host operating system. VIPS uses this with +.B im_guess_prefix(3) +to try to find the various VIPS data files. + +.SH EXAMPLE + + int + main( int argc, char **argv ) + { + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + return( 0 ); + } + +.B im_get_option_group(3) +returns a +.B GOptionGroup +containing various VIPS command-line options. It can be used with GOption to +help parse argc/argv. + +.SH SEE ALSO +im_guess_prefix(3), GOption(3) +.SH COPYRIGHT +Birkbeck College and the National Gallery (c) 1994 diff --git a/libsrc/iofuncs/man3/im_initdesc.3 b/libsrc/iofuncs/man3/im_initdesc.3 new file mode 100644 index 00000000..a531cf32 --- /dev/null +++ b/libsrc/iofuncs/man3/im_initdesc.3 @@ -0,0 +1,45 @@ +.TH IM_INITDESC 3 "22 April 1991" +.SH NAME +im_initdesc \- initialises an image descriptor to specific values +.SH SYNOPSIS +#include + +void im_initdesc( IMAGE *image, + int xsize, int ysize, + int bands, int bandbits, int bandfmt, + int coding, int type, + float xres, float yres, + float xo, float yo ) +.SH DESCRIPTION +.B im_initdesc(3) +initialises the image descriptor according to the entered values. +More specifically the Xsize, Ysize, Bands, BandFmt, Coding, Type, +Xres, Yres, Xoffset and Yoffset members of the descriptor are initialised by the +correspondingly entered arguments. The members fd, baseaddr, data and +filename are not handled by this function. The order of the args is +the same as in vips/vips.h. + +The +.B bandbits +parameter is deprecated and ignored by this function: you can always pass zero. + +Example: make a 512 by 512 one-band memory buffer. + + if( !(im = im_open( "temp", "t" )) ) + /* Error ... + im_initdesc( im, + 512, 512, + 1, 0, FMTUCHAR, + NOCODING, B_W, + 1.0, 1.0, + 0, 0 ); + if( im_setupout( im ) ) + /* Error ... + +.SH ALSO +im_open(3), im_setupout(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_invalidate.3 b/libsrc/iofuncs/man3/im_invalidate.3 new file mode 100644 index 00000000..487e9587 --- /dev/null +++ b/libsrc/iofuncs/man3/im_invalidate.3 @@ -0,0 +1,16 @@ +.TH IM_INVALIDATE 3 "5 December 2006" +.SH NAME +im_invalidate \- flush caches related to an image +.SH SYNOPSIS +.B #include + +.B void im_invalidate( IMAGE *im ) + +.SH DESCRIPTION +.B im_invalidate(3) +marks all caches related to the image as invalid and requring recalculation. +It needs to be called if an image changes after being made: for example, after +a paint action, or perhaps after a new frame has arrived from a video source. + +.SH SEE\ ALSO +im_prepare(3), im_region_buffer(3). diff --git a/libsrc/iofuncs/man3/im_iocheck.3 b/libsrc/iofuncs/man3/im_iocheck.3 new file mode 100644 index 00000000..921fa570 --- /dev/null +++ b/libsrc/iofuncs/man3/im_iocheck.3 @@ -0,0 +1,50 @@ +.TH IM_IOCHECK 3 "11 April 1990" +.SH NAME +im_incheck, im_outcheck, im_iocheck \- checks image descriptors +for WIO +.SH SYNOPSIS +.B #include + +int im_incheck( in ) +.br +IMAGE *in; + +int im_outcheck( out ) +.br +IMAGE *out; + +.B int im_iocheck( in, out) +.br +.B IMAGE *in, *out; + +.SH DESCRIPTION +im_incheck(3) +checks that an image descriptor is suitable for WIO input (ie. all of +its pels can be found from im\-\>data). If possible, it transforms the +descriptor to make WIO input ok using the following rules: + +IM_PARTIAL: the descriptor is magically turned into an IM_SETBUF descriptor. +Memory is allocated and the image generated into that. The old partial +callbacks are closed down, and the descriptor reformed as a IM_SETBUF. + +IM_OPENOUT: if the descriptor has been written to, it is automatically +`rewound,` that is, it is closed and reopened as an IM_MMAPIN descriptor. + +IM_SETBUF: just checks that the descriptor has been written to. + +See the manual page for im_setupout(3) for a skeleton program. + +im_outcheck(3) checks that a descriptor is suitable for WIO output with +im_writeline(3). If it sees an IM_PARTIAL image, it turns it magically into an +IM_SETBUF image. + +im_iocheck(3) simply calls in_incheck(3) for image in and im_outcheck(3) for +image out. +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_open(3), im_cp_desc(3), im_setupout(3), im_makerw(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_isMSBfirst.3 b/libsrc/iofuncs/man3/im_isMSBfirst.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isMSBfirst.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_iscomplex.3 b/libsrc/iofuncs/man3/im_iscomplex.3 new file mode 100644 index 00000000..5d330032 --- /dev/null +++ b/libsrc/iofuncs/man3/im_iscomplex.3 @@ -0,0 +1,121 @@ +.TH IM_ISINT 3 "11 April 1990" +.SH NAME +im_isint, im_ispng, im_isuint, im_isfloat, im_isscalar, im_iscomplex, im_istiff, +istifftiled, im_isjpeg, im_isfile, im_ispartial, im_isvips, im_isMSBfirst, +im_fexists, im_amiMSBfirst \- classify image types +.SH SYNOPSIS +.B #include + +int im_isMSBfirst( im ) +.br +IMAGE *im; + +int im_amiMSBfirst( void ) + +int im_fexists( const char *filename, ... ) + +int im_istiff( filename ) +.br +char *filename; + +int im_istifftiled( filename ) +.br +char *filename; + +int im_isjpeg( filename ) +.br +char *filename; + +int im_ispng( filename ) +.br +char *filename; + +int im_isppm( filename ) +.br +char *filename; + +int im_isvips( filename ) +.br +char *filename; + +int im_isfile( im ) +.br +IMAGE *im; + +int im_ispartial( im ) +.br +IMAGE *im; + +int im_isint( im ) +.br +IMAGE *im; + +int im_isuint( im ) +.br +IMAGE *im; + +int im_isfloat( im ) +.br +IMAGE *im; + +int im_isscalar( im ) +.br +IMAGE *im; + +int im_iscomplex( im ) +.br +IMAGE *im; + +.SH DESCRIPTION +im_fexists(3) returns non-zero if the named file exists and is readable. Args +as printf(3). + +im_istiff(3) tests a file for tiff-ness. + +im_istifftiled(3) tests a file for tiff-tiled-ness. + +im_isjpeg(3) tests a file for jpeg-ness. + +im_ispng(3) tests a file for png-ness. + +im_isppm(3) tests a file for PPM/PGM/PBM-ness. + +im_isvips(3) tests a file for vips-ness. + +im_isMSBfirst(3) returns true (1) if the file is in most-significant-byte first +form. This is the byte order used on the SPARC architecture, and others. It +returns false (0)for intel-order images. + +im_amiMSBfirst(3) returns true (1) if this processor is MSB first. + +im_isfile(3) returns true if the descriptor corresponds to some disc object, +that is, was opened with modes "r", "rw" or "w". + +im_ispartial(3) returns true if the descriptor is partial, +that is, was opened with mode "p". + +The rest of these functions test im\-\>BandFmt, returning logical truth +(non-zero) if BandFmt is one of a number of possibles, and returning zero if +it is not. + +im_isint(3) (is integer type) returns true if im\-\>BandFmt is one of FMTUCHAR, +FMTCHAR, FMTUSHORT, FMTSHORT, FMTUINT or FMTINT. + +im_isuint(3) (is unsigned integer type) returns true if im\-\>BandFmt is one of +FMTUCHAR, FMTUSHORT or FMTUINT. + +im_isfloat(3) (is floating point type) returns true if im\-\>BandFmt is one of +FMTFLOAT or FMTDOUBLE. + +im_isscalar(3) (is scalar type) returns true if im\-\>BandFmt is one of FMTUCHAR, +FMTCHAR, FMTUSHORT, FMTSHORT, FMTUINT, FMTINT, FMTFLOAT or FMTDOUBLE. + +im_iscomplex(3) (is complex type) returns true if im\-\>BandFmt is one of +FMTCOMPLEX or FMTDPCOMPLEX. + +.SH COPYRIGHT +National Gallery, 1993 +.SH SEE ALSO +im_tiff2vips(3), im_jpeg2vips(3) +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_isfile.3 b/libsrc/iofuncs/man3/im_isfile.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isfile.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isfloat.3 b/libsrc/iofuncs/man3/im_isfloat.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isfloat.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isint.3 b/libsrc/iofuncs/man3/im_isint.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isint.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isjpeg.3 b/libsrc/iofuncs/man3/im_isjpeg.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isjpeg.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_ispartial.3 b/libsrc/iofuncs/man3/im_ispartial.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_ispartial.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_ispng.3 b/libsrc/iofuncs/man3/im_ispng.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_ispng.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isppm.3 b/libsrc/iofuncs/man3/im_isppm.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isppm.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isscalar.3 b/libsrc/iofuncs/man3/im_isscalar.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isscalar.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_istifftiled.3 b/libsrc/iofuncs/man3/im_istifftiled.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_istifftiled.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isuint.3 b/libsrc/iofuncs/man3/im_isuint.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isuint.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_isvips.3 b/libsrc/iofuncs/man3/im_isvips.3 new file mode 100644 index 00000000..31d449ad --- /dev/null +++ b/libsrc/iofuncs/man3/im_isvips.3 @@ -0,0 +1 @@ +.so man3/im_iscomplex.3 diff --git a/libsrc/iofuncs/man3/im_iterate.3 b/libsrc/iofuncs/man3/im_iterate.3 new file mode 100644 index 00000000..1735b64e --- /dev/null +++ b/libsrc/iofuncs/man3/im_iterate.3 @@ -0,0 +1,72 @@ +.TH IM_ITERATE 3 "30 October 1992" +.SH NAME +im_iterate \- PIO input from image +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_iterate( im, start_fn, scan_fn, stop_fn, a, b ) +.br +IMAGE *im; +.br +void *(*start_fn)(); +.br +int (*scan_fn)(); +.br +int (*stop_fn)(); +.br +void *a, *b; + +where, typically, + +void *start_fn( im, a, b ) +.br +IMAGE *im; +.br +void *a, *b; + +int scan_fn( or, seq, a, b ) +.br +REGION *or; +.br +void *seq; +.br +void *a, *b; + +int stop_fn( seq, a, b ) +.br +void *seq; +.br +void *a, *b; +.SH DESCRIPTION +im_iterate(3) is used for PIO image input. See `VIPS Library Programmers' +guide,' in the accompanying documentation, for an introduction to this +function. + +im_iterate(3) makes one or more regions on im, and starts one or more sequences +running over the image. im_iterate(3) guarantees that + + - scan_fn() will see each of the pels in im exactly once + - start_fn() and stop_fn() are both exclusive + +See the guide, the man page for im_generate(3), and the source to im_deviate(3) +for examples. + +On machines with SVR4 threads and several CPUs, im_generate(3) and +im_iterate(3) automatically parallelise programs. You can set the desired +concurrency level with the environment variable IM_CONCURRENCY, for example + + example% setenv IM_CONCURRENCY 2 + example% stats fred.v + +will run stats with enough concurrency to keep 2 CPUs fully occupied. +If IM_CONCURRENCY is not set, then it defaults to 1. +.SH RETURN VALUE +All functions return 0 on success and non-zero on error. +.SH SEE ALSO +im_generate(3). +.SH COPYRIGHT +National Gallery, 1993 +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_list_add.3 b/libsrc/iofuncs/man3/im_list_add.3 new file mode 100644 index 00000000..1f901b08 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_add.3 @@ -0,0 +1,229 @@ +.TH IM_LIST_ADD 3 "2 May 1991" +.SH NAME +im_list_add, im_list_len, im_list_pos, im_list_member, im_list_append, +im_list_remove, +im_list_eq, im_list_map, im_list_map_rev, im_list_fold, im_list_fix, +im_list_free, im_list_insert \- linked list functions +.SH SYNOPSIS +#include +.br +#include + +typedef struct list_type { +.br + struct list_type *next; +.br + void *this; +.br +} List; + +#define hd(L) ((L)->this) +.br +#define tl(L) ((L)->next) + +typedef void *(*im_list_map_fn)( void *, void *, void * ); +.br +typedef void (*im_list_free_fn)( void *, void *, void * ); +.br +typedef void *(*im_list_fold_fn)( void *, void *, +.br + void *, void * ); + +int im_list_len( List *l ); +.br +int im_list_pos( List *l, void *t ); +.br +int im_list_member( List *l, void *t ); +.br +void *im_list_index( List *l, int n ); +.br +int im_list_add( List **base, void *new ); +.br +int im_list_insert( List **base, void *new, void *old ); +.br +int im_list_append( List **base, void *new ); +.br +int im_list_remove( List **base, void *t ); + +void *im_list_eq( void *a, void *b ); +.br +void *im_list_map( List *l, +.br + im_list_map_fn fn, void *a, void *b ); +.br +void *im_list_map_rev( List *l, +.br + im_list_map_fn fn, void *a, void *b ); +.br +void *im_list_fold( List *l, +.br + void *start, im_list_fold_fn fn, void *a, void *b ); +.br +void im_list_fix( List **base, +.br + im_list_map_fn fn, void *a, void *b ); +.br +void im_list_free( List **base, +.br + im_list_free_fn fn, void *a, void *b ); + +.SH DESCRIPTION +Manipulate linked lists in various ways. These functions are heavily used by +the VIPS IO system; use them yourself if you like. VIPS lists store lists of +void * pointers - use casts if you want to store some other type. Note that +if sizeof( your object ) != sizeof( void * ), you will be in trouble! + +All are based on the List type (see above). An empty list is a NULL pointer, a +one element list is a pointer to a List struct, whose this field contains a +pointer to the object in the list and whose next field is NULL. Macros hd(3) +and tl(3) (head and tail) return this and next respectively. + +im_list_len(3) returns the number of elements in list l. im_list_pos(3) searches +list l for stored object t, returning an index. The first list element has +index zero. im_list_pos(3) returns -1 for not present. im_list_index(3) returns +the item at position n in the list, or NULL for index out of range. +im_list_member(3) returns non-zero if the list contains the element. + +im_list_map(3) applies a void * valued function to every element in a list, +running from beginning to end. If the function returns NULL, im_list_map +continues with the next element. If the function returns non-NULL, +im_list_map(3) abandons the map and returns immediately, returning the value +the user function returned. If the list is empty, im_list_map(3) returns NULL. + +The two extra arguments a and b are carried around for you by VIPS and fed +into each call of the function. They are useful for communicating context +information. + +You can use im_list_map to implement many kinds of list search/apply operation. +VIPS supplies the function im_list_eq(3) which tests two void * pointers for +equality, returning the pointer if they match, and returning NULL otherwise. + +Example: search a list for an object + + im_list_map( list, +.br + (im_list_map_fn) im_list_eq, object, NULL ); + +This could also be written as + + List *p; + + for( p = list; p; p = tl( p ) ) +.br + if( object == hd( p ) ) +.br + break; + +I prefer the first. + +im_list_map_rev(3) behaves exactly as im_list_map(3), but applies the function +running from te end to the beginning of the list. It is much slower than +im_list_map(3) and should be used only in emergencies. + +im_list_fold(3) folds up a list with a dyadic function. If a list contains +[1,2], return fn( 2, fn( 1, start, a, b ), a, b ). If the list is empty, +return start. + +The two extra arguments a and b are carried around for you by VIPS and fed +into each call of the function. They are useful for communicating context +information. + +Example: find a pointer to the largest element in a list of ints (assume +sizeof(int) <= sizeof(void *)) + + max_pair( int *new, int *old ) +.br + { +.br + if( !old || *new > *old ) +.br + return( new ); +.br + else +.br + return( old ); +.br + } + + largest = im_list_fold( list, +.br + NULL, (im_list_map_fn) max_pair, NULL, NULL ); + +im_list_add(3) adds a new element to the head of a list. Since the head of the +list will move, you must pass in a *pointer* to your pointer to your old head. + +Example: make a list of the numbers 9-0 (assume sizeof(int) <= sizeof(void *)) + + int i; +.br + List *nlist = NULL; + + for( i = 0; i < 10; i++ ) +.br + im_list_add( &nlist, (void *) i ); + +im_list_insert(3) adds a new element to a list, placing it just before the +indicated old element. If the old element is not found, im_list_insert(3) +returns an error. + +im_list_append(3) appends a new element to the end of a list. This is much +slower than im_list_add(3), and should be avoided if possible. + +im_list_remove(3) removes the specified element from the list. Since the head +of the list may move, you must pass in a *pointer* to your pointer to your +old head. + +im_list_fix(3) finds the fixed-point of a list-altering function. It repeatedly +maps a function over the list until the function returns NULL. Note that, +since the list may be changing, you must pass in a *pointer* to the pointer +you store the list in. + +The two extra arguments a and b are carried around for you by VIPS and fed +into each call of the function. They are useful for communicating context +information. + +Example: remove all elements less than x from a list of numbers (assume +sizeof(int) <= sizeof(void *)) + + int * +.br + test_ele( int *n, List **base, int x ) +.br + { +.br + if( *n < x ) { +.br + im_list_remove( base, n ); +.br + return( base ); +.br + } +.br + else +.br + return( NULL ); +.br + } + + im_list_fix( &nlist, +.br + (im_list_map_fn) test_ele, &nlist, x ); + +im_list_free(3) frees the list, applying a user free function to every element +as it is freed. You may pass NULL instead of a pointer to a function, in which +case im_list_free(3) will just free the memory used by the list nodes. + +The two extra arguments a and b are carried around for you by VIPS and fed +into each call of the function. They are useful for communicating context +information. + +.SH RETURN VALUE +The functions returns a 0 or a pointer on sucess, and non-zero or NULL on +failure. +.SH SEE\ ALSO +im_rect_intersectrect(3), etc. +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/iofuncs/man3/im_list_append.3 b/libsrc/iofuncs/man3/im_list_append.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_append.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_eq.3 b/libsrc/iofuncs/man3/im_list_eq.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_eq.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_fix.3 b/libsrc/iofuncs/man3/im_list_fix.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_fix.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_fold.3 b/libsrc/iofuncs/man3/im_list_fold.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_fold.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_free.3 b/libsrc/iofuncs/man3/im_list_free.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_free.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_index.3 b/libsrc/iofuncs/man3/im_list_index.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_index.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_insert.3 b/libsrc/iofuncs/man3/im_list_insert.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_insert.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_len.3 b/libsrc/iofuncs/man3/im_list_len.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_len.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_map.3 b/libsrc/iofuncs/man3/im_list_map.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_map.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_map_rev.3 b/libsrc/iofuncs/man3/im_list_map_rev.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_map_rev.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_member.3 b/libsrc/iofuncs/man3/im_list_member.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_member.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_pos.3 b/libsrc/iofuncs/man3/im_list_pos.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_pos.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_list_remove.3 b/libsrc/iofuncs/man3/im_list_remove.3 new file mode 100644 index 00000000..2299eb96 --- /dev/null +++ b/libsrc/iofuncs/man3/im_list_remove.3 @@ -0,0 +1 @@ +.so man3/im_list_add.3 diff --git a/libsrc/iofuncs/man3/im_makerw.3 b/libsrc/iofuncs/man3/im_makerw.3 new file mode 100644 index 00000000..e76d462b --- /dev/null +++ b/libsrc/iofuncs/man3/im_makerw.3 @@ -0,0 +1,26 @@ +.TH IM_MAKERW 3 "22 April 1992" +.SH NAME +im_makerw \- make an IMAGE writeable +.SH SYNOPSIS +#include + +int im_makerw( image ) +.br +IMAGE *image; +.SH DESCRIPTION +im_makerw(3) attempts to make image writeable, ready for an in-place operation. +Its actions are just as those for im_incheck(3), except that descriptors which +after im_incheck(3) end up as IM_MMAPIN descriptors, magically become +IM_MMAPINRW descriptors. + +Extreme caution is urged! You can permanently damage valuable data with this +call. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_mmapin(3), im_mmapinrw(3), im_fastlineuser(3). +.SH COPYRIGHT +.br +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/iofuncs/man3/im_malloc.3 b/libsrc/iofuncs/man3/im_malloc.3 new file mode 100644 index 00000000..ed13b2c4 --- /dev/null +++ b/libsrc/iofuncs/man3/im_malloc.3 @@ -0,0 +1,155 @@ +.TH IM_AND 3 "30 October 1992" +.SH NAME +im_add_close_callback, im_add_eval_callback, im_malloc, im_free, +im_add_evalend_callback \- add image callbacks +.SH SYNOPSIS +.B #include + +int im_add_close_callback( im, fn, a, b ) +.br +IMAGE *im; +.br +int (*fn)(); +.br +void *a, *b; + +int im_add_evalend_callback( im, fn, a, b ) +.br +IMAGE *im; +.br +int (*fn)(); +.br +void *a, *b; + +where + +int fn( a, b ) +.br +void *a, *b; + +char *im_malloc( IMAGE *im, int size ); + +int im_free( void *s ); + +.B #include +.br +.B #include + +int im_add_eval_callback( im, fn, a, b ) +.br +IMAGE *im; +.br +int (*fn)(); +.br +void *a, *b; + +where + +int fn( a, b ) +.br +void *a, *b; + +.SH DESCRIPTION +These functions attach callbacks to images. Callbacks are functions, with +optional extra arguments a and b, which are remembered by the IMAGE they are +attached to. These functions are triggered at some later stage in reponse to +some event. You can attach as many callbacks as you like; all will be +remembered, and will be called when the event occurs. The most recently added +callback is called first --- this can be important for close callbacks. + +.B im_add_close_callback(3) +adds a callback which will be triggered when the image +is closed by +.B im_close(3). +The callback is expected to return 0 for success and +non-zero for failure. If the function fails, then the whole +.B im_close(3) +fails. + +This function is used by VIPS to implement +.B im_malloc(3). +This allocates memory +exactly as the standard +.B malloc(3) +function, but memory allocated is local to a +descriptor. When the descriptor is closed, the memory allocated is +automatically freed for you. If you pass NULL for the descriptor, then +.B im_malloc(3) +acts as +.B malloc(3). +On error, +.B im_malloc(3) +returns NULL, setting an +error message. See the man pages for +.B IM_NEW(3) and +.B im_open_local(3) +for further examples. + +Free memory with +.B im_free(3). + +You may use close callbacks to trigger other +.B im_close(3) +operations, and there +may even be circularity in your +.B im_close(3) +lists. + +.B im_add_evalend_callback(3) +adds a callback which will be triggered when VIPS +has finished writing to the descriptor. If you want to output some diagnostics +from your function (an overflow count, for example), this is the callback to +use. + +.B im_add_eval_callback(3) +adds a callback which will be triggered repeatedly as +the image is written to. This works for both PIO and WIO images, +although it is rather more successful with PIO image output. + +When the callback is triggered, the time field of the descriptor will point to +a time_info structure, as defined in + + #include + + struct time_info { + IMAGE *im; /* Image we are part of */ + time_t start; /* Start time, in seconds */ + int run; /* Time we have been running */ + int eta; /* Seconds of computation left */ + int ttiles; /* Tiles we expect to calculate */ + int ntiles; /* Tiles calculated so far */ + int percent; /* Percent complete */ + }; + +These fields are not exact! They should only be used to give approximate +feedback to the user. It is possible to have + + percent > 100 + ntiles > ttiles + eta == 0 + +so be careful. Again, the eval callback should return 0 for success and +non-zero for failure. If the callback fails, evaluation is abandoned. This may +be used to provide a `cancel' feature in your user-interface. + + int + eval_cb( IMAGE *im ) + { + printf( "%d%% complete ...\\n", im->time->percent ); + return( 0 ); + } + + if( im_add_eval_callback( out, eval_cb, out, NULL ) ) + return( -1 ); + + ... now as we write to out, we will get %complete + ... messages on stdout. + +.SH RETURN VALUE +All functions return 0 on success and non-zero on error. +.SH SEE ALSO +IM_NEW(3), im_open_local(3). +.SH COPYRIGHT +National Gallery, 1993 +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_meta.3 b/libsrc/iofuncs/man3/im_meta.3 new file mode 100644 index 00000000..f251cbe3 --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta.3 @@ -0,0 +1,118 @@ +.TH IM_META 3 "7 June 2005" +.SH NAME +im_meta_set_int, im_meta_get_int, im_meta_set_double, im_meta_get_double, +im_meta_set_area, im_meta_get_area, im_meta_set_string, im_meta_get_string, +im_meta_set_blob, im_meta_get_blob, +im_meta_set, im_meta_get +\- read and write extra header fields +.SH SYNOPSIS +#include + +int im_meta_set_int( IMAGE *im, const char *field, int i ); +.br +int im_meta_get_int( IMAGE *im, const char *field, int *i ); +.br +int im_meta_set_double( IMAGE *im, const char *field, double d ); +.br +int im_meta_get_double( IMAGE *im, const char *field, double *d ); +.br +int im_meta_set_area( IMAGE *im, const char *field, + im_callback_fn free_fn, void *data ); +.br +int im_meta_get_area( IMAGE *im, const char *field, void **data ); +.br +int im_meta_set_string( IMAGE *im, const char *field, + const char *str ); +.br +int im_meta_get_string( IMAGE *im, const char *field, char **str ); +.br +int im_meta_set_blob( IMAGE *im, const char *field, + im_callback_fn free_fn, void *blob, int blob_length ); +.br +int im_meta_get_blob( IMAGE *im, const char *field, + void **blob, int *blob_length ); + +int im_meta_set( IMAGE *im, const char *field, GValue *value ); +.br +int im_meta_get( IMAGE *im, const char *field, GValue *value_copy ); +.br +GType im_meta_get_type( IMAGE *im, const char *field ); + +#define IM_TYPE_SAVE_STRING (im_save_string_get_type()) +.br +GType im_save_string_get_type( void ); +.br +const char *im_save_string_get( const GValue *value ); +.br +void im_save_string_set( GValue *value, const char *str ); +.br +void im_save_string_setf( GValue *value, const char *fmt, ... ); + +.SH DESCRIPTION +These functions read and write extra image header fields. Writing to a field +destroys any old value. You must read a field with the correct type of reader: +you can't read an int field as a string. Fields are copied when images are +processed, so you can use them to pass information to subsequent operations. +Unless otherwise noted, image header fields created with these functions are +also saved to disc when an image is saved in VIPS format, and automatically +restored when the image is loaded again. + +.B im_meta_set_int(3) +sets an integer field. Any existing field of any type with this name is +removed. It returns 0 for success, or -1 on error setting +.B im_error(3). + +.B im_meta_get_int(3) +reads an integer field. It returns 0 for success, or -1 on error, setting +.B im_error(3). +It can fail if the field does not exist, or if the field is not an int. + +.B im_meta_set_double(3) +and +.B im_meta_get_double(3) +work exactly as +.B im_meta_set_int(3) +and +.B im_meta_get_int(3). + +.B im_meta_set_area(3) +sets a field which is an area of memory. When the field is copied to a +subsequent IMAGE, VIPS just copies the pointer. VIPS keeps a reference count +and when the last IMAGE using this field is closed, VIPS will call +.B free_fn +to release the memory. You can therefore use this function to attach very +large areas of memory to images efficiently. Areas cannot be saved to VIPS +files on disc, since there is no known length. + +.B im_meta_set_string(3) +is a convenience function over +.B im_meta_set_area(3). +It copies the string, and +then shares that copy between all images derived from this IMAGE. + +.B im_meta_set_blob(3) +sets a field which is a blob (binary object). A blob is just like string, +except that rather than being NULL-terminated, you must pass an explicit +length. + +.B im_meta_set(3), +.B im_meta_get_type(3) +and +.B im_meta_get(3) +operate at a lower level: they let you set image header fields as GValue. Use +one of the convenience functions above if possible. +.B im_meta_get_type(3) +returns 0 if the field is not defined. + +In order for the autoload/save to VIPS files to work, you need to use a GType +with a transform defined to and from +.B IM_TYPE_SAVE_STRING. + +.SH RETURN VALUE +The functions return 0 success and -1 on error. +.SH SEE ALSO +im_header_int(3), im_header_map(3). +.SH COPYRIGHT +The National Gallery, 2005. +.SH AUTHOR +Markus Wollgarten and John Cupitt diff --git a/libsrc/iofuncs/man3/im_meta_get.3 b/libsrc/iofuncs/man3/im_meta_get.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_area.3 b/libsrc/iofuncs/man3/im_meta_get_area.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_area.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_blob.3 b/libsrc/iofuncs/man3/im_meta_get_blob.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_blob.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_double.3 b/libsrc/iofuncs/man3/im_meta_get_double.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_double.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_int.3 b/libsrc/iofuncs/man3/im_meta_get_int.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_int.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_string.3 b/libsrc/iofuncs/man3/im_meta_get_string.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_string.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_get_type.3 b/libsrc/iofuncs/man3/im_meta_get_type.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_get_type.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set.3 b/libsrc/iofuncs/man3/im_meta_set.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_area.3 b/libsrc/iofuncs/man3/im_meta_set_area.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_area.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_blob.3 b/libsrc/iofuncs/man3/im_meta_set_blob.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_blob.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_double.3 b/libsrc/iofuncs/man3/im_meta_set_double.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_double.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_int.3 b/libsrc/iofuncs/man3/im_meta_set_int.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_int.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_meta_set_string.3 b/libsrc/iofuncs/man3/im_meta_set_string.3 new file mode 100644 index 00000000..d90069be --- /dev/null +++ b/libsrc/iofuncs/man3/im_meta_set_string.3 @@ -0,0 +1 @@ +.so man3/im_meta.3 diff --git a/libsrc/iofuncs/man3/im_mmapin.3 b/libsrc/iofuncs/man3/im_mmapin.3 new file mode 100644 index 00000000..271d8603 --- /dev/null +++ b/libsrc/iofuncs/man3/im_mmapin.3 @@ -0,0 +1,22 @@ +.TH IM_MMAPIN 3 "11 April 1990" +.SH NAME +im_mmapin \- memory maps a VASARI image file (read only) and returns +an image descriptor. +.SH SYNOPSIS +.B #include + +.B IMAGE *im_mmapin(file_name) +.br +.B char *file_name; +.SH DESCRIPTION +.B im_mmapin(3) +opens a file for input. It has been replaced by im_open( file_name, "r" ). +.SH RETURN VALUE +The function returns the image descriptor on success and NULL on error. +.SH SEE ALSO +im_open(3), im_close(3), im_mmapinrw(3), im_makerw(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_mmapinrw.3 b/libsrc/iofuncs/man3/im_mmapinrw.3 new file mode 100644 index 00000000..cbcdedbf --- /dev/null +++ b/libsrc/iofuncs/man3/im_mmapinrw.3 @@ -0,0 +1,27 @@ +.TH IM_MMAPINRW 3 "22 April 1991" +.SH NAME +im_mmapinrw \- memory maps a VASARI image file with read and write access +.SH SYNOPSIS +.B #include + +.B IMAGE *im_mmapinrw(file_name) +.br +.B char *file_name; +.SH DESCRIPTION +.B im_mmapinrw(3) +works exactly as im_mmapin but the entered file_name is memory mapped +with read and write permissions. This is useful for some applications, +like writing lines onto an existing file. Since +the user might change accidentally the context of the original file, +great care should taken when handling a file with this routine. + +im_open(3) is generally more convenient. +.SH RETURN VALUE +The function returns the image descriptor on success and NULL on error. +.SH SEE\ ALSO +im_close(3), im_open(3), im_makerw(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_open.3 b/libsrc/iofuncs/man3/im_open.3 new file mode 100644 index 00000000..ddb9379e --- /dev/null +++ b/libsrc/iofuncs/man3/im_open.3 @@ -0,0 +1,135 @@ +.TH IM_OPEN 3 "30 October 1992" +.SH NAME +im_open, im_open_local, im_open_local_array \- open VIPS +image descriptor(s) +.SH SYNOPSIS +#include + +IMAGE *im_open( const char *filename, const char *mode ) + +IMAGE *im_open_local( IMAGE *im, const char *filename, const char *mode ) + +int im_open_local_array( IMAGE *im, + IMAGE **out, int n, const char *filename, const char *mode ) + +.SH DESCRIPTION +.B im_open(3) +examines the mode string, and creates an appropriate VIPS IMAGE descriptor. + +.B "r" +opens the named file for reading. If the file is not in the native VIPS format +for your machine, +.B im_open(3) +automatically converts the file for you in memory. For some large files (eg. +TIFF) this may not be what you want: you should call the appropriate converter +yourself, and arrange for the conversion to take place on disc. See +.B im_tiff2vips(3), +.B im_jpeg2vips(3), +.B im_png2vips(3), +.B im_magick2vips(3), +and +.B im_ppm2vips(3). + +.B im_open(3) +can read files in most formats. + +.B "w" +opens the named file for writing. It looks at the file name suffix to +determine the type to write -- for example: + + im_open( "fred.tif", "w" ) + +will write in TIFF format. + +You can pass parameters to the conversion functions encoded in the filename +string. For example: + + im_open( "fred.tif:deflate", "w" ) + +will write a deflate (ZIP) compressed TIFF file. See the man pages for +.B im_vips2tiff(3), +.B im_vips2jpeg(3), +.B im_vips2png(3) +and +.B im_vips2ppm(3) +for details on all of the options available. + +.B "t" +creates a temporary memory buffer image. + +.B "p" +creates a "glue" descriptor you can use to join two image processing +operations together. + +.B "rw" +opens the named file for reading and writing. This will only work for VIPS +files in a format native to your machine. It is only for paintbox-type +applications. + +.B im_open_local(3) +is a convenience function which opens an image descriptor as +im_open(3), but makes it local to im, that is, when im is closed, the +descriptor created by im_open_local(3) will be closed too. + +.B im_open_local(3) +is handy for saving you from adding many +.B im_close(3) +calls to +escape points. Example: find the total of an array of images. + + #include + #include + + int + total( IMAGE **in, int nin, IMAGE *out ) + { + int i; + IMAGE *t1, *t2; + + if( nin <= 0 ) { + im_errormsg( "total: nin should be > 0" ); + return( -1 ); + } + else if( nin == 1 ) + return( im_copy( *in, out ) ); + else + for( t1 = *in, i = 1; i < nin; i++ ) { + if( i + 1 == nin ) + t2 = out; + else if( !(t2 = im_open_local( out, "t2", "p" )) ) + return( -1 ); + + if( im_add( t1, in[i], t2 ) ) + return( -1 ); + t1 = t2; + } + + return( 0 ); + } + +This function will create many intermediate images, but does not need to close +them. Any which are created will be closed automatically when out is closed by +our caller. + +im_open_local(3) returns NULL on error, or if its first parameter is NULL. + +.B im_open_local_array(3) +will open an array of images, failing if any of the opens fail. It's handy if +you need a number of images for intermediates. Example: + + IMAGE *t[6]; + + if( im_open_local_array( out, t, 6, "mytemps", "p" ) ) + return( -1 ); + +opens 6 temp images (t[0] to t[5]). + +.SH RETURN VALUE +The function returns the image descriptor on success and NULL on error. +.SH SEE ALSO +im_close(3), im_vips2tiff(3), im_vips2jpeg(3), im_vips2ppm(3), +im_tiff2vips(3), im_jpeg2vips(3), im_ppm2vips(3). +.SH COPYRIGHT +K. Martinez, 1992. +.SH AUTHOR +K. Martinez. diff --git a/libsrc/iofuncs/man3/im_open_local.3 b/libsrc/iofuncs/man3/im_open_local.3 new file mode 100644 index 00000000..227e5534 --- /dev/null +++ b/libsrc/iofuncs/man3/im_open_local.3 @@ -0,0 +1 @@ +.so man3/im_open.3 diff --git a/libsrc/iofuncs/man3/im_open_local_array.3 b/libsrc/iofuncs/man3/im_open_local_array.3 new file mode 100644 index 00000000..227e5534 --- /dev/null +++ b/libsrc/iofuncs/man3/im_open_local_array.3 @@ -0,0 +1 @@ +.so man3/im_open.3 diff --git a/libsrc/iofuncs/man3/im_openout.3 b/libsrc/iofuncs/man3/im_openout.3 new file mode 100644 index 00000000..ddbc4bb9 --- /dev/null +++ b/libsrc/iofuncs/man3/im_openout.3 @@ -0,0 +1,22 @@ +.TH IM_OPENOUT 3 "22 April 1991" +.SH NAME +im_openout \- sets an image descriptor corresponding to an output file +.SH SYNOPSIS +.B #include + +.B IMAGE *im_openout(file_name) +.br +.B char *file_name; +.SH DESCRIPTION +.B im_openout(3) +returns a descriptor which when written to will write pels to file_name. This +function has been replaced by im_open(3). +.SH RETURN VALUE +The function returns a valid image descriptor on success and NULL on error. +.SH SEE\ ALSO +im_open(3), im_close(3), im_mmapin(3), im_intro(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_outcheck.3 b/libsrc/iofuncs/man3/im_outcheck.3 new file mode 100644 index 00000000..e0092c33 --- /dev/null +++ b/libsrc/iofuncs/man3/im_outcheck.3 @@ -0,0 +1 @@ +.so man3/im_iocheck.3 diff --git a/libsrc/iofuncs/man3/im_partial.3 b/libsrc/iofuncs/man3/im_partial.3 new file mode 100644 index 00000000..82396807 --- /dev/null +++ b/libsrc/iofuncs/man3/im_partial.3 @@ -0,0 +1,24 @@ +.TH IM_OPENOUT 3 "22 April 1991" +.SH NAME +im_partial \- makes a PIO intermediate image +.SH SYNOPSIS +.B #include + +.B IMAGE *im_partial(file_name) +.br +.B char *file_name; +.SH DESCRIPTION +.B im_partial(3) +returns a descriptor which can be used to join PIO image processing +operations together. + +This function is obsolete --- you should call im_open(3) instead. +.SH RETURN VALUE +The function returns a valid image descriptor on success and NULL on error. +.SH SEE ALSO +im_open(3), im_close(3), im_mmapin(3), im_intro(3). +.SH COPYRIGHT +.br +National Gallery, 1993 +.SH AUTHOR +J.Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_pincheck.3 b/libsrc/iofuncs/man3/im_pincheck.3 new file mode 100644 index 00000000..1dc8c392 --- /dev/null +++ b/libsrc/iofuncs/man3/im_pincheck.3 @@ -0,0 +1 @@ +.so man3/im_piocheck.3 diff --git a/libsrc/iofuncs/man3/im_piocheck.3 b/libsrc/iofuncs/man3/im_piocheck.3 new file mode 100644 index 00000000..98280199 --- /dev/null +++ b/libsrc/iofuncs/man3/im_piocheck.3 @@ -0,0 +1,36 @@ +.TH IM_IOCHECK 3 "11 April 1990" +.SH NAME +im_pincheck, im_poutcheck, im_piocheck \- checks image descriptors for +PIO +.SH SYNOPSIS +.B #include + +int im_pincheck( in ) +.br +IMAGE *in; + +int im_poutcheck( out ) +.br +IMAGE *out; + +.B int im_piocheck( in, out) +.br +.B IMAGE *in, *out; +.SH DESCRIPTION +.B im_pincheck(3) +checks that an image descriptor is suitable for PIO input. If the descriptor is +IM_OPENOUT and the descriptor has been written to, it is automatically +`rewound,' that is, it is closed and reopened as an IM_MMAPIN descriptor. + +im_poutcheck(3) checks that a descriptor is suitable for PIO output. + +im_piocheck(3) simply calls in_pincheck(3) for image in and im_poutcheck(3) for +image out. +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_generate(3), im_open(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_poutcheck.3 b/libsrc/iofuncs/man3/im_poutcheck.3 new file mode 100644 index 00000000..1dc8c392 --- /dev/null +++ b/libsrc/iofuncs/man3/im_poutcheck.3 @@ -0,0 +1 @@ +.so man3/im_piocheck.3 diff --git a/libsrc/iofuncs/man3/im_prepare.3 b/libsrc/iofuncs/man3/im_prepare.3 new file mode 100644 index 00000000..49297a1c --- /dev/null +++ b/libsrc/iofuncs/man3/im_prepare.3 @@ -0,0 +1,91 @@ +.TH IM_PREPARE 3 "11 April 1990" +.SH NAME +im_prepare, im_prepare_to \- fill region with data +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_prepare( reg, r ) +.br +REGION *reg; +.br +Rect *r; + +int im_prepare_to( reg, dest, r, x, y ) +.br +REGION *reg, *dest; +.br +Rect *r; +.br +int x, y; + +int im_prepare_many( reg, r ) +.br +REGION **reg; +.br +Rect *r; + +.SH DESCRIPTION +.B im_prepare(3) +fills region reg with pels covering the area inside r. + +r is expected to lie within the image on which reg was defined; if it does not +it will be clipped against the size of the image. Consequence: if + + im_prepare( reg, r ) + +succeeds, VIPS guarantees that pels within reg->valid may be read from reg +with +.B IM_REGION_ADDR(3). +It does not guarantee that you may read all of r! + +The action taken by im_prepare(3) depends upon the image descriptor on which +reg was defined: + +PARTIAL: The area requested is clipped against the edges of the image, local +memory is attached to reg with +.B im_region_buffer(3) +and that area requested +from the image's generate function, see +.B im_generate(3). +If necessary, a new +sequence is started. + +SETBUF: MMAPIN: MMAPINRW: The area defined by r is clipped against the edges +of the image, and that area attached to reg. + +.B im_prepare_to(3) +is very like +.B im_prepare(3), +but rather than writing pixels to local memory on reg (or attaching reg to +some other piece of memory), it instead writes pixels into the region dest at +position x, y. The parameters dest, r, x, y behave in the same way as the +paramaters to +.B im_region_region(3). + +Effectively, it's just like +.B im_prepare(3) +followed by a copy operation. Except that the copy will be skipped when +possible. + +This call is used by (among others) +.B im_generate(3) +to make operations output to +disc buffers, and by +.B im_embed(3) +to get images written inside larger images. + +.B im_prepare_many(3) +prepares the same Rect on a NULL terminated array of REGION pointers, as +returned by +.BR im_start_many(3) . + +.SH RETURN VALUE +The function returns 0 on success and non-zero on error. +.SH SEE ALSO +im_generate(3), im_open(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_prepare_many.3 b/libsrc/iofuncs/man3/im_prepare_many.3 new file mode 100644 index 00000000..381ca378 --- /dev/null +++ b/libsrc/iofuncs/man3/im_prepare_many.3 @@ -0,0 +1 @@ +.so man3/im_prepare.3 diff --git a/libsrc/iofuncs/man3/im_prepare_to.3 b/libsrc/iofuncs/man3/im_prepare_to.3 new file mode 100644 index 00000000..381ca378 --- /dev/null +++ b/libsrc/iofuncs/man3/im_prepare_to.3 @@ -0,0 +1 @@ +.so man3/im_prepare.3 diff --git a/libsrc/iofuncs/man3/im_printdesc.3 b/libsrc/iofuncs/man3/im_printdesc.3 new file mode 100644 index 00000000..9a94b602 --- /dev/null +++ b/libsrc/iofuncs/man3/im_printdesc.3 @@ -0,0 +1,66 @@ +.TH IM_PRINTDESC 3 "11 April 1990" +.SH NAME +im_printdesc, im_Type2char, im_char2Type, im_BandFmt2char, im_char2BandFmt, +im_Coding2char, im_char2Coding, im_Compression2char, im_char2Compression +\- decode image descriptors + +.SH SYNOPSIS +.B #include +.br +.B #include + +.B void im_printdesc(image) +.br +.B IMAGE *image; + +char *im_Type2char( ty ); +.br +int ty; + +char *im_BandFmt2char( bf ); +.br +int bf; + +char *im_Coding2char( cod ); +.br +int cod; + +char *im_Compression2char( comp ); +.br +int comp; + +int im_char2Type( str ); +.br +char *str; + +int im_char2BandFmt( str ); +.br +char *str; + +int im_char2Coding( str ); +.br +char *str; + +int im_char2Compression( str ); +.br +char *str; + +.SH DESCRIPTION +.B im_printdesc(3) +prints the image descriptor pointed by image. + +.B im_printdesc(3) +exports the functions it uses to code and uncode the type fields +in image headers. The functions which return char * return a static string of +the form "" on error, + +The functions which return int return -1 on error, +setting +.B im_error(3). + +.SH SEE ALSO +header(1). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_printlines.3 b/libsrc/iofuncs/man3/im_printlines.3 new file mode 100644 index 00000000..4db97dcb --- /dev/null +++ b/libsrc/iofuncs/man3/im_printlines.3 @@ -0,0 +1 @@ +.so man3/im_debugim.3 diff --git a/libsrc/iofuncs/man3/im_rect_dup.3 b/libsrc/iofuncs/man3/im_rect_dup.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_dup.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_equalsrect.3 b/libsrc/iofuncs/man3/im_rect_equalsrect.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_equalsrect.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_includespoint.3 b/libsrc/iofuncs/man3/im_rect_includespoint.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_includespoint.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_includesrect.3 b/libsrc/iofuncs/man3/im_rect_includesrect.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_includesrect.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_intersectrect.3 b/libsrc/iofuncs/man3/im_rect_intersectrect.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_intersectrect.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_isempty.3 b/libsrc/iofuncs/man3/im_rect_isempty.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_isempty.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_marginadjust.3 b/libsrc/iofuncs/man3/im_rect_marginadjust.3 new file mode 100644 index 00000000..32ea226e --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_marginadjust.3 @@ -0,0 +1,81 @@ +.TH Rect 3 "2 May 1991" +.SH NAME +im_rect_marginadjust, im_rect_includespoint, im_rect_includesrect, +im_rect_intersectrect, im_rect_isempty, im_rect_unionrect, im_rect_normalise, +im_rect_equalsrect, im_rect_dup, +IM_RECT_RIGHT, IM_RECT_BOTTOM, IM_RECT_HCENTRE, IM_RECT_VCENTRE +\- rectangle algebra functions +.SH SYNOPSIS +#include +.br +#include + +typedef struct { +.br + int left, top, width, height; +.br +} Rect; + +#define IM_RECT_RIGHT(R) ((R)->left + (R)->width) +.br +#define IM_RECT_BOTTOM(R) ((R)->top + (R)->height) +.br +#define IM_RECT_HCENTRE(R) ((R)->left + (R)->width / 2) +.br +#define IM_RECT_VCENTRE(R) ((R)->top + (R)->height / 2) + +void im_rect_marginadjust( Rect *r, int n ); +.br +int im_rect_includespoint( Rect *r, int x, int y ); +.br +int im_rect_includesrect( Rect *r1, Rect *r2 ); +.br +void im_rect_intersectrect( Rect *r1, Rect *r2, Rect *r3 ); +.br +int im_rect_isempty( Rect *r ); +.br +void im_rect_unionrect( Rect *r1, Rect *r2, Rect *r3 ); +.br +int im_rect_equalsrect( Rect *r1, Rect *r2 ); +.br +Rect *im_rect_dup( Rect *r ); +.br +void im_rect_normalise( Rect *r ); +.SH DESCRIPTION +These functions perform simple algebra on Rect structs. There should also be a +set of functions to do rectlist algebra. + +im_rect_marginadjust(3) expands a Rect by n units up, down, left and right. +Negative expansions shrink the Rect. + +im_rect_includespoint(3) returns non-zero +if point (x,y) lies within Rect r. + +im_rect_includesrect(3) returns non-zero if +Rect r2 lies completely within Rect r1. + +im_rect_intersectrect(3) fills Rect r3 +with the intersection of Rects r1 and r2. + +im_rect_isempty(3) returns non-zero +if Rect r has either width less than or equal to 0 or height less than or +equal to 0. + +im_rect_unionrect(3) fills Rect r3 with the bounding box of Rect r1 and Rect +r2. A proper union operation requires lists of rectangles, sadly. + +im_rect_equalsrect(3) returns non-zero if r1 and r2 are identical. + +im_rect_dup(3) allocates memory for a new Rect structure and copies the +elements of r into it. It returns a pointer to the new struct, or NULL on +error. + +im_rect_normalise(3) flips r so that the same pixels are enclosed, +but width and height are guaranteed >=0. + +.SH SEE ALSO +im_prepare(3), im_region_create(3) +.SH COPYRIGHT +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/iofuncs/man3/im_rect_normalise.3 b/libsrc/iofuncs/man3/im_rect_normalise.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_normalise.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_rect_unionrect.3 b/libsrc/iofuncs/man3/im_rect_unionrect.3 new file mode 100644 index 00000000..0ad304e6 --- /dev/null +++ b/libsrc/iofuncs/man3/im_rect_unionrect.3 @@ -0,0 +1 @@ +.so man3/im_rect_marginadjust.3 diff --git a/libsrc/iofuncs/man3/im_region_buffer.3 b/libsrc/iofuncs/man3/im_region_buffer.3 new file mode 100644 index 00000000..97d5afb9 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_buffer.3 @@ -0,0 +1,119 @@ +.TH REGION-OPERATIONS 3 "11 April 1990" +.SH NAME +im_region_buffer, im_region_image, im_region_region, im_region_position, +im_region_equalsregion \- +attach data to region +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_region_buffer( reg, r ) +.br +REGION *reg; +.br +Rect *r; + +int im_region_image( reg, r ) +.br +REGION *reg; +.br +Rect *r; + +int im_region_region( reg, treg, r, x, y ) +.br +REGION *reg, treg; +.br +Rect *r; +.br +int x, y; + +int im_region_position( reg, x, y ) +.br +REGION *reg; +.br +int x, y; + +int im_region_equalsregion( REGION *reg1, REGION *reg2 ) + +.SH DESCRIPTION +These functions are used to say where exactly input from or output to a +region goes. Briefly, +.B im_region_buffer(3) +gives a region its own piece of local storage, +.B im_region_image(3) +links the region to an image, and +.B im_region_region(3) +links the region to another region. + +.B im_region_region(3) +is the most powerful of the three --- you can use it to +avoid copy operations. See the source for +.B im_extract(3), +.B im_insert(3), +.B im_copy(3), +.B im_embed(3), +and +.B im_lrmerge(3) +for examples of its use, and see also +.B im_prepare_copy(3). + +.B im_region_buffer(3) +clips Rect r against the size of the image upon which reg is +defined, then searches for any calculated pixels which enclose that area. If +there are any suitable pixels already, the region is set to point to them. +Otherwise, a fresh buffer is allocated. + +If region->buffer->done is set, then the region is already calculated. +Otherwise, the pixels need to be calculated. + +.B im_region_buffer(3) +is called by +.B im_prepare(3) +just before it calls the user generate function. + +.B im_region_image(3) +attaches reg to the image on which it was defined. The image +has to be SETBUF, MMAPIN, MMAPINRW, or OPENIN --- ie., the types passed by +.B im_incheck(3). + +.B im_region_region(3) +redirects reg to treg. treg can be defined on another +image. r is clipped against the size of reg's image, and against treg->valid. +treg has to have pel data associated with it (ie. memory local to treg, memory +that is part of the image on which treg is defined, or even memory from a +third region), and the pel data has to be in a form which is compatible with +reg, ie. BandFmt and Bands must be the same. + +r becomes the valid field in reg, (x,y) is the top left-hand corner of the +area in treg to which valid is mapped. + + +.B im_region_position(3) +changes reg->valid, so that reg->valid.left == x and +reg->valid.top == y. width and height are clipped against the size of the +image. If x < 0 or y < 0 or the clipped rect has zero width or height, the +call fails. + +This function affects the way that pels are addressed by the +.B IM_REGION_ADDR(3) +macro. It does not affect the pels themselves! It has the effect of moving +the area of pels a region represents. This call is used by +.B im_extract(3). + +.B im_region_equalsregion(3) +tests for reg1 and reg2 are identical, ie.: + + IM_REGION_ADDR( reg1, x, y ) == + IM_REGION_ADDR( reg2, x, y ) && + *IM_REGION_ADDR( reg1, x, y ) == + *IM_REGION_ADDR( reg2, x, y ) + +It is used internally by VIPS, but may be useful to applications. + +.SH RETURN VALUE +All int-valued functions return zero on success and non-zero on error. +.SH COPYRIGHT +National Gallery, 1993 +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_region_create.3 b/libsrc/iofuncs/man3/im_region_create.3 new file mode 100644 index 00000000..25335c80 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_create.3 @@ -0,0 +1,40 @@ +.TH PREDICATES 3 "11 April 1990" +.SH NAME +im_region_create, im_region_free \- region creation and destruction +.SH SYNOPSIS +.B #include +.br +.B #include + +REGION *im_region_create( im ) +.br +IMAGE *im; + +int im_region_free( reg ) +.br +REGION *reg; +.SH DESCRIPTION +These functions create and destroy regions on images. Regions are used for +PIO, see accompanying documentation. Regions have type + + typedef struct { + Rect valid /* Area of im represented */ + IMAGE *im; /* im we are defined on */ + + ... more fields, all private and used for + ... housekeeping + } REGION; + +im_region_create(3) returns a pointer to a new region, or NULL on error. +Regions are made blank, with no input or output possible. See im_prepare(3), +im_generate(3), im_start_one(3) and IM_REGION_ADDR(3). + +im_region_free(3) frees a region and any resources associated with that +region. When an image is closed, all regions which have been created on that +image are automatically freed. +.SH RETURN VALUE +All int-valued functions return zero on success and non-zero on error. +.SH COPYRIGHT +National Gallery, 1993 +.SH AUTHOR +J. Cupitt \- 23/7/93 diff --git a/libsrc/iofuncs/man3/im_region_free.3 b/libsrc/iofuncs/man3/im_region_free.3 new file mode 100644 index 00000000..d21d2aa2 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_free.3 @@ -0,0 +1 @@ +.so man3/im_region_create.3 diff --git a/libsrc/iofuncs/man3/im_region_image.3 b/libsrc/iofuncs/man3/im_region_image.3 new file mode 100644 index 00000000..886fa201 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_image.3 @@ -0,0 +1 @@ +.so man3/im_region_buffer.3 diff --git a/libsrc/iofuncs/man3/im_region_position.3 b/libsrc/iofuncs/man3/im_region_position.3 new file mode 100644 index 00000000..886fa201 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_position.3 @@ -0,0 +1 @@ +.so man3/im_region_buffer.3 diff --git a/libsrc/iofuncs/man3/im_region_region.3 b/libsrc/iofuncs/man3/im_region_region.3 new file mode 100644 index 00000000..886fa201 --- /dev/null +++ b/libsrc/iofuncs/man3/im_region_region.3 @@ -0,0 +1 @@ +.so man3/im_region_buffer.3 diff --git a/libsrc/iofuncs/man3/im_render.3 b/libsrc/iofuncs/man3/im_render.3 new file mode 100644 index 00000000..bfa7bbb1 --- /dev/null +++ b/libsrc/iofuncs/man3/im_render.3 @@ -0,0 +1,101 @@ +.TH IM_RENDER 3 "5 October 2003" +.SH NAME +im_render, im_render_fade, im_cache \- make image in the background +.SH SYNOPSIS +#include + +int im_render_fade( IMAGE *in, IMAGE *out, IMAGE *mask, +.br + int width, int height, int max, +.br + int fps, int steps, +.br + int priority, +.br + void (*notify)( IMAGE *, Rect *, void * ), void *client ); + +int im_render( IMAGE *in, IMAGE *out, IMAGE *mask, +.br + int width, int height, int max, +.br + void (*notify)( IMAGE *, Rect *, void * ), void *client ); + +int im_cache( IMAGE *in, IMAGE *out, +.br + int width, int height, int max ); + +.SH DESCRIPTION +.B im_render_fade(3) +behaves rather like +.B im_copy(3) +between images +.B in +and +.B out, +except that it keeps a cache of computed pixels. This cache is made of up to +.B max +tiles (a value of -1 for +.B max +means any number of tiles), and each tile is of size +.B width +by +.B height +pixels. Each cache tile is made with a call to +.B im_prepare_thread(3), +so cache tile calculation will be accelerated on multi-CPU machines. If image +.B in +represents a large computation, +.B im_render_fade(3) +can save a lot of time. + +If the +.B notify +parameter points to a function, then tiles are not calculated immediately, but +are added to a job list and calculated as CPU becomes available. When a tile has +been calculated, the notify function is passed the image which was being +cached, the area of the image which is now available, and a client pointer. + +The +.B mask +image is a one band uchar image the same size as out, which has 255 for every +pixels which is in the cache and 0 everywhere else. You should not read +pixels from +.B mask +after +.B out +has been closed. If mask is NULL, then no mask image is written. + +If +.B steps +is greater than zero, then pixels in mask are moved between 0 and 255 as tiles +age in that many steps. The +.B fps +parameter sets how many times per second the notify function is called to +indicate that an updated tile is ready. + +If +.B priority +is zero, then the render will only run when the system is idle, and only a few +renders will run at any time. Higher values will cause the render to happen +more quickly. + +.B im_cache(3) +is a convenience function for +.B im_render_fade(3) +that caches image pixels synchronously. If you ask for an area not in the cache, +execution blocks until the area has been calculated. + +.B im_render(3) +is deprecated: it is the old interface to the render function, before the +.B fps +and +.B steps +parameters were added. + +.SH RETURN VALUE +The function returns 0 on success, and non-zero on error, setting +im_error(). +.SH SEE ALSO +im_prepare(3) +.SH AUTHOR +J Cupitt, 2003 diff --git a/libsrc/iofuncs/man3/im_render_fade.3 b/libsrc/iofuncs/man3/im_render_fade.3 new file mode 100644 index 00000000..b7647daa --- /dev/null +++ b/libsrc/iofuncs/man3/im_render_fade.3 @@ -0,0 +1 @@ +.so man3/im_render.3 diff --git a/libsrc/iofuncs/man3/im_setbuf.3 b/libsrc/iofuncs/man3/im_setbuf.3 new file mode 100644 index 00000000..eb13b64f --- /dev/null +++ b/libsrc/iofuncs/man3/im_setbuf.3 @@ -0,0 +1,21 @@ +.TH IM_SETBUF 3 "11 April 1990" +.SH NAME +im_setbuf \- sets an image descriptor to keep an image in ram +.SH SYNOPSIS +.B #include + +.B IMAGE *im_setbuf(buffer_name) +.br +.B char *buffer_image; +.SH DESCRIPTION +.B im_setbuf(3) +returns an image descriptor which, when written to, will save pels in a memory +buffer. It is generally more convenient to call im_open(3). +.SH RETURN VALUE +The function returns a valid image descriptor on success or NULL on error. +.SH SEE\ ALSO +im_close(3), im_open(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/libsrc/iofuncs/man3/im_setupout.3 b/libsrc/iofuncs/man3/im_setupout.3 new file mode 100644 index 00000000..845bd7bf --- /dev/null +++ b/libsrc/iofuncs/man3/im_setupout.3 @@ -0,0 +1,51 @@ +.TH IM_SETUPOUT 3 "11 April 1990" +.SH NAME +im_setupout \- set up an image descriptor for WIO output +.SH SYNOPSIS +.B #include + +.B int im_setupout(image) +.br +.B IMAGE *image; +.SH DESCRIPTION +.B im_setupout(3) +makes a descriptor ready for WIO writing. If the descriptor is a memory +buffer, enough memory is allocated to be able to hold all of the pels of the +image. If the descriptor is an output file, then the header is written to +disc. + +Typically, for WIO, you should have + + int + im_wombat( in, out ) + IMAGE *in, *out; + { + if( im_iocheck( in, out ) ) + return( -1 ); + + ... check parameters, check image descriptors + ... for type-compatibility, etc. etc. + + if( im_cp_desc( out, in ) ) + return( -1 ); + + ... set fields in out for the type of image you + ... wish to write + + if( im_setupout( out ) ) + return( -1 ); + + ... process from input to output, reading from in->data + ... and writing to out with im_writeline(3) + + return( 0 ); + } +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_cp_desc(3), im_iocheck(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990, updated on 22/04/1991 diff --git a/libsrc/iofuncs/man3/im_start_many.3 b/libsrc/iofuncs/man3/im_start_many.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_start_many.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_start_one.3 b/libsrc/iofuncs/man3/im_start_one.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_start_one.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_stop_many.3 b/libsrc/iofuncs/man3/im_stop_many.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_stop_many.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_stop_one.3 b/libsrc/iofuncs/man3/im_stop_one.3 new file mode 100644 index 00000000..c124af1e --- /dev/null +++ b/libsrc/iofuncs/man3/im_stop_one.3 @@ -0,0 +1 @@ +.so man3/im_generate.3 diff --git a/libsrc/iofuncs/man3/im_updatehist.3 b/libsrc/iofuncs/man3/im_updatehist.3 new file mode 100644 index 00000000..d421d4b0 --- /dev/null +++ b/libsrc/iofuncs/man3/im_updatehist.3 @@ -0,0 +1 @@ +.so man3/im_histlin.3 diff --git a/libsrc/iofuncs/man3/im_verror.3 b/libsrc/iofuncs/man3/im_verror.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_verror.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_version.3 b/libsrc/iofuncs/man3/im_version.3 new file mode 100644 index 00000000..ae5522de --- /dev/null +++ b/libsrc/iofuncs/man3/im_version.3 @@ -0,0 +1,46 @@ +.TH IM_VERSION 3 "15 December 1999" +.SH NAME +im_version, im_version_string \- return VIPS version info +.SH SYNOPSIS +.B #include + +.B int im_version( int flag ) + +.B const char *im_version_string( void ) + +.SH DESCRIPTION + +.B im_version(3) +returns the major version number if +.B flag +== 0, the minor version number if +.B flag +== 1, and the micro version number if +.B flag +== 2. + +Major versions are supposed to represent very large changes to the +library, minor versions indicate steady improvements (with odd minor +versions indicating unstable and undocumented development releases), +and micro versions indicating bug-fixes. + +.B im_version_string(3) +returns a static string representing the vips version number, and the date +when the library was built. + +The version number always has three parts, separated by '.' characters. The +first part is the major release, the second the minor release (odd minor +values denote development releases), and the third the micro version number. + +The version number and the date are separated by a '-' character. + +The value of this string is constructed in 'configure.in'. + +A typical value for tthe string might be "7.7.0-Thu Dec 16 18:10:01 GMT 1999". + +.SH RETURN VALUE +The function returns 0 on success and non-zero on error. +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt \- 15/12/99 diff --git a/libsrc/iofuncs/man3/im_version_string.3 b/libsrc/iofuncs/man3/im_version_string.3 new file mode 100644 index 00000000..f4a36ee1 --- /dev/null +++ b/libsrc/iofuncs/man3/im_version_string.3 @@ -0,0 +1 @@ +.so man3/im_version.3 diff --git a/libsrc/iofuncs/man3/im_warn.3 b/libsrc/iofuncs/man3/im_warn.3 new file mode 100644 index 00000000..7db4b63b --- /dev/null +++ b/libsrc/iofuncs/man3/im_warn.3 @@ -0,0 +1 @@ +.so man3/im_error.3 diff --git a/libsrc/iofuncs/man3/im_wrapmany.3 b/libsrc/iofuncs/man3/im_wrapmany.3 new file mode 100644 index 00000000..e2d3a418 --- /dev/null +++ b/libsrc/iofuncs/man3/im_wrapmany.3 @@ -0,0 +1 @@ +.so man3/im_wrapone.3 diff --git a/libsrc/iofuncs/man3/im_wrapone.3 b/libsrc/iofuncs/man3/im_wrapone.3 new file mode 100644 index 00000000..9235bb45 --- /dev/null +++ b/libsrc/iofuncs/man3/im_wrapone.3 @@ -0,0 +1,69 @@ +.TH WRAPPERS 3 "11 April 1990" +.SH NAME +im_wrapone, im_wrapmany \- easy interface to partial image IO system +.SH SYNOPSIS +.B #include +.br +.B #include + +int im_wrapone( IMAGE *in, IMAGE *out, + im_wrapone_fn fn, void *a, void *b ) +.br +int im_wrapmany( IMAGE **in, IMAGE *out, + im_wrapmany_fn fn, void *a, void *b ) + +where + +typedef void (*im_wrapone_fn)( void *in, void *out, int n, + void *a, void *b ) +.br +typedef void (*im_wrapmany_fn)( void **in, void *out, int n, + void *a, void *b ) + +.SH DESCRIPTION +These functions provide a simple way to use the VIPS partial image IO system, +provided that your image processing function involves no coordinate +transformations, that is, that each output PEL depends only upon the +corresponding PEL(s) in the input image(s). + +You should write your operation as a buffer processing function --- for +example: + + static void + invert_buffer( unsigned char *in, unsigned char *out, + int width ) + { + int x; + + for( x = 0; x < width; x++ ) + *out++ = 255 - *in++; + } + +This can now be turned into a full PIO image processing function by: + + int + invert( IMAGE *in, IMAGE *out ) + { + if( in->BandFmt != FMTUCHAR || + in->Coding != NOCODING || + in->Bands != 1 ) { + im_errormsg( "invert: bad input" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + if( im_wrapone( in, out, + (im_wrapone_fn) invert_buffer, NULL, NULL ) ) + return( 0 ); + } + +im_wrapmany(3) works as im_wrapone(3), but allows many input images. All the +inputs should be same Xsize and Ysize, but may vary in type. The array of +input images should be NULL-terminated, as for im_start_many(3). + +.SH RETURN VALUE +All int-valued functions return zero on success and non-zero on error. +.SH COPYRIGHT +National Gallery, 1995 +.SH AUTHOR +J. Cupitt \- 9/2/95 diff --git a/libsrc/iofuncs/man3/im_writeline.3 b/libsrc/iofuncs/man3/im_writeline.3 new file mode 100644 index 00000000..96029903 --- /dev/null +++ b/libsrc/iofuncs/man3/im_writeline.3 @@ -0,0 +1,37 @@ +.TH IM_WRITELINE 3 "22 April 1991" +.SH NAME +im_writeline \- writes a line of data in the image descriptor +.SH SYNOPSIS +.B #include + +.B int im_writeline(ypos, image, buffer) +.br +.B int ypos; +.br +.B IMAGE *image; +.br +.B char *buffer; +.SH DESCRIPTION +.B im_writeline(3) +writes the ypos line of an image held in buffer to either the output +buffer image (previously set by im_setbuf(3)) or to an output file +(previously set by im_openput(3)). The function provides uniform treatment of +output irrespectively whether it is a file or a buffer image. + +You should be careful that the buffer does indeed contain enough data for a +complete line of pels --- see lsize(3). + +Any evaluation callbacks which have been added to image are triggered --- see +im_generate(3) and im_add_eval_callback(3). +.SH BUGS +This function changed in VIPS6: +programs written previously must be modified by removing the last argument +of the old im_writeline(3). +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_setbuf(3), im_openout(3), im_add_eval_callback(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris 23/04/1991 diff --git a/libsrc/iofuncs/memory.c b/libsrc/iofuncs/memory.c new file mode 100644 index 00000000..2b9bd068 --- /dev/null +++ b/libsrc/iofuncs/memory.c @@ -0,0 +1,209 @@ +/* @(#) memory.c: mem handling stuff + * + * 2/11/99 JC + * - from im_open.c and callback.c + * - malloc tracking stuff added + * 11/3/01 JC + * - im_strncpy() added + * 20/4/01 JC + * - im_(v)snprintf() added + * 6/7/05 + * - more tracking for DEBUGM + * 20/10/06 + * - return NULL for size <= 0 + * 11/5/06 + * - abort() on malloc() failure with DEBUG + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Define for simple malloc tracking ... better to use dmalloc if you can. +#define DEBUGM + */ + +/* abort() on memory errors. +#define DEBUG + */ + +#ifdef DEBUG +# warning DEBUG on in libsrc/iofuncs/memory.c +#endif /*DEBUG*/ + +/* Track total alloc/total free here for debugging. + */ +#ifdef DEBUGM +static size_t int total_mem_alloc = 0; +static unsigned int total_allocs = 0; +static size_t int high_water_mark = 0; +static GMutex *malloc_mutex = NULL; +static GSList *malloc_list = NULL; +static const int trace_freq = 100; /* Msg every this many malloc/free */ +static int next_trace = 0; +#endif /*DEBUGM*/ + +/* VIPS free function. Try to put all vips free() through this. + */ +int +im_free( void *s ) +{ +#ifdef DEBUGM +{ + size_t size; + + s = (void *) ((char*)s - 16); + size = *((size_t*)s); + g_mutex_lock( malloc_mutex ); + + assert( g_slist_find( malloc_list, s ) ); + malloc_list = g_slist_remove( malloc_list, s ); + assert( !g_slist_find( malloc_list, s ) ); + malloc_list = g_slist_remove( malloc_list, s ); + assert( total_allocs > 0 ); + + total_mem_alloc -= size; + total_allocs -= 1; + + next_trace += 1; + if( next_trace > trace_freq ) { + printf( "im_free: %d, %d allocs, total %.3gM, " + "high water %.3gM\n", + size, + total_allocs, + total_mem_alloc / (1024.0 * 1024.0), + high_water_mark / (1024.0 * 1024.0) ); + next_trace = 0; + } + + g_mutex_unlock( malloc_mutex ); +} +#endif /*DEBUGM*/ + +#ifdef DEBUG + if( !s ) + abort(); +#endif /*DEBUG*/ + + free( s ); + + return( 0 ); +} + +/* Malloc local to a descriptor. Try to put all vips malloc through this. Not + * thread-safe if im != NULL. + */ +void * +im_malloc( IMAGE *im, size_t size ) +{ + void *buf; + +#ifdef DEBUGM + /* Assume the first im_malloc() is single-threaded. + */ + if( !malloc_mutex ) + malloc_mutex = g_mutex_new(); +#endif /*DEBUGM*/ + +#ifdef DEBUGM + /* If debugging mallocs, need an extra sizeof(uint) bytes to track + * size of this block. Ask for an extra 16 to make sure we don't break + * alignment rules. + */ + size += 16; +#endif /*DEBUGM*/ + + if( !(buf = malloc( size )) ) { +#ifdef DEBUG + abort(); +#endif /*DEBUG*/ + + im_error( "im_malloc", + _( "out of memory --- size == %dMB" ), + (int) (size / (1024.0*1024.0)) ); + im_warn( "im_malloc", + _( "out of memory --- size == %dMB" ), + (int) (size / (1024.0*1024.0)) ); + return( NULL ); + } + +#ifdef DEBUGM + /* Record number alloced. + */ + g_mutex_lock( malloc_mutex ); + assert( !g_slist_find( malloc_list, buf ) ); + malloc_list = g_slist_prepend( malloc_list, buf ); + *((size_t*)buf) = size; + buf = (void *) ((char*)buf + 16); + total_mem_alloc += size; + if( total_mem_alloc > high_water_mark ) + high_water_mark = total_mem_alloc; + total_allocs += 1; + + next_trace += 1; + if( next_trace > trace_freq ) { + printf( "im_malloc: %d, %d allocs, total %.3gM, " + "high water %.3gM\n", + size, + total_allocs, + total_mem_alloc / (1024.0 * 1024.0), + high_water_mark / (1024.0 * 1024.0) ); + next_trace = 0; + } + + g_mutex_unlock( malloc_mutex ); + + /* Handy to breakpoint on this printf() for catching large mallocs(). + */ + if( size > 1000000 ) + printf( "woah! big!\n" ); +#endif /*DEBUGM*/ + + if( im && im_add_close_callback( im, + (im_callback_fn) im_free, buf, NULL ) ) { + im_free( buf ); + return( NULL ); + } + + return( buf ); +} diff --git a/libsrc/iofuncs/meta.c b/libsrc/iofuncs/meta.c new file mode 100644 index 00000000..819b10fb --- /dev/null +++ b/libsrc/iofuncs/meta.c @@ -0,0 +1,926 @@ +/* Meta information on an IMAGE. Extra fields added by the user/client/etc. + * + * 6/6/05 + * - hacked from code from Markus Wollgarten + * 30/6/05 JC + * - take a copy of field names so we work for sprintf()'d fields + * - separate GType for refstring so we can spot it from im_header_map() + * 13/7/05 + * - added BLOB too (ie. can be saved to xml) + * 26/8/05 + * - get_ funcs set im_error() + * 29/8/05 + * - added im__meta_destroy() + * 1/9/05 + * - oop, hash table insert/replace confusion fixed + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +#include + +#include + +#include "base64.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* + + The GValue we store can be a number, mutable string, ref-counted + immutable string (eg. large chunk of XML), etc. Convenience + functions make it less painful. See im_meta_get_int() etc. + + */ + +#ifdef DEBUG +/* Check that this meta is on the hash table. + */ +static void * +meta_sanity_on_hash( Meta *meta, IMAGE *im ) +{ + Meta *found; + + if( meta->im != im ) + printf( "*** field \"%s\" has incorrect im\n", + meta->field ); + + if( !(found = g_hash_table_lookup( im->Meta, meta->field )) ) + printf( "*** field \"%s\" is on traverse but not in hash\n", + meta->field ); + + if( found != meta ) + printf( "*** meta \"%s\" on traverse and hash do not match\n", + meta->field ); + + return( NULL ); +} + +static void +meta_sanity_on_traverse( const char *field, Meta *meta, IMAGE *im ) +{ + if( meta->field != field ) + printf( "*** field \"%s\" has incorrect field\n", + meta->field ); + + if( meta->im != im ) + printf( "*** field \"%s\" has incorrect im\n", + meta->field ); + + if( !g_slist_find( im->Meta_traverse, meta ) ) + printf( "*** field \"%s\" is in hash but not on traverse\n", + meta->field ); +} + +static void +meta_sanity( const IMAGE *im ) +{ + if( im->Meta ) + g_hash_table_foreach( im->Meta, + (GHFunc) meta_sanity_on_traverse, (void *) im ); + im_slist_map2( im->Meta_traverse, + (VSListMap2Fn) meta_sanity_on_hash, (void *) im, NULL ); +} +#endif /*DEBUG*/ + +static void +meta_free( Meta *meta ) +{ +#ifdef DEBUG +{ + char *str_value; + + str_value = g_strdup_value_contents( &meta->value ); + printf( "meta_free: field %s, value = %s\n", + meta->field, str_value ); + g_free( str_value ); +} +#endif /*DEBUG*/ + + if( meta->im ) + meta->im->Meta_traverse = + g_slist_remove( meta->im->Meta_traverse, meta ); + + g_value_unset( &meta->value ); + IM_FREE( meta->field ); + im_free( meta ); +} + +static Meta * +meta_new( IMAGE *im, const char *field, GValue *value ) +{ + Meta *meta; + + if( !(meta = IM_NEW( NULL, Meta )) ) + return( NULL ); + meta->im = im; + meta->field = NULL; + memset( &meta->value, 0, sizeof( GValue ) ); + + if( !(meta->field = im_strdup( NULL, field )) ) { + meta_free( meta ); + return( NULL ); + } + + g_value_init( &meta->value, G_VALUE_TYPE( value ) ); + g_value_copy( value, &meta->value ); + + im->Meta_traverse = g_slist_append( im->Meta_traverse, meta ); + g_hash_table_replace( im->Meta, meta->field, meta ); + +#ifdef DEBUG +{ + char *str_value; + + str_value = g_strdup_value_contents( value ); + printf( "meta_new: field %s, value = %s\n", + field, str_value ); + g_free( str_value ); +} +#endif /*DEBUG*/ + + return( meta ); +} + +/* Destroy all the meta on an image. + */ +void +im__meta_destroy( IMAGE *im ) +{ + IM_FREEF( g_hash_table_destroy, im->Meta ); + assert( !im->Meta_traverse ); +} + +static void +meta_init( IMAGE *im ) +{ + if( !im->Meta ) { + assert( !im->Meta_traverse ); + im->Meta = g_hash_table_new_full( g_str_hash, g_str_equal, + NULL, (GDestroyNotify) meta_free ); + } +} + +static void * +meta_cp_field( Meta *meta, IMAGE *dst ) +{ + Meta *meta_copy; + +#ifdef DEBUG +{ + char *str_value; + + str_value = g_strdup_value_contents( &meta->value ); + printf( "im__meta_cp: copying field %s, value = %s\n", + meta->field, str_value ); + g_free( str_value ); +} +#endif /*DEBUG*/ + + /* No way to return error here, sadly. + */ + meta_copy = meta_new( dst, meta->field, &meta->value ); + +#ifdef DEBUG + meta_sanity( dst ); +#endif /*DEBUG*/ + + return( NULL ); +} + +/* Copy meta on to dst. Called from im_cp_desc(). + */ +int +im__meta_cp( IMAGE *dst, const IMAGE *src ) +{ + if( src->Meta ) { + /* Loop, copying fields. + */ + meta_init( dst ); + im_slist_map2( src->Meta_traverse, + (VSListMap2Fn) meta_cp_field, dst, NULL ); + } + + return( 0 ); +} + +/* Set a meta, overwriting any old meta. + */ +int +im_meta_set( IMAGE *im, const char *field, GValue *value ) +{ + Meta *meta; + + assert( field ); + assert( value ); + + meta_init( im ); + if( !(meta = meta_new( im, field, value )) ) + return( -1 ); + +#ifdef DEBUG + meta_sanity( im ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Fill value with a copy of the meta, -1 on error. value_copy must be zeroed + * but uninitialised. + */ +int +im_meta_get( IMAGE *im, const char *field, GValue *value_copy ) +{ + Meta *meta; + + assert( field ); + assert( value_copy ); + + /* Defined? + */ + if( !im->Meta || !(meta = g_hash_table_lookup( im->Meta, field )) ) { + im_error( "im_meta_get", _( "field \"%s\" not found" ), field ); + return( -1 ); + } + + g_value_init( value_copy, G_VALUE_TYPE( &meta->value ) ); + g_value_copy( &meta->value, value_copy ); + + return( 0 ); +} + +GType +im_meta_get_type( IMAGE *im, const char *field ) +{ + Meta *meta; + + assert( field ); + + /* Defined? + */ + if( !im->Meta || !(meta = g_hash_table_lookup( im->Meta, field )) ) + return( 0 ); + + return( G_VALUE_TYPE( &meta->value ) ); +} + +/* Helpers for set/get. Write a value and destroy it. + */ +static int +meta_set_value( IMAGE *im, const char *field, GValue *value ) +{ + if( im_meta_set( im, field, value ) ) { + g_value_unset( value ); + return( -1 ); + } + g_value_unset( value ); + + return( 0 ); +} + +static int +meta_get_value( IMAGE *im, const char *field, GType type, GValue *value_copy ) +{ + if( im_meta_get( im, field, value_copy ) ) + return( -1 ); + if( G_VALUE_TYPE( value_copy ) != type ) { + im_error( "im_meta_get", _( "field \"%s\" " + "is of type %s, not %s" ), + field, + g_type_name( G_VALUE_TYPE( value_copy ) ), + g_type_name( type ) ); + g_value_unset( value_copy ); + return( -1 ); + } + + return( 0 ); +} + +/* Save meta fields to the header. We have a new string type for header fields + * to save to XML and define transform functions to go from our meta types to + * this string type. + */ +GType +im_save_string_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + type = g_boxed_type_register_static( "im_save_string", + (GBoxedCopyFunc) g_strdup, + (GBoxedFreeFunc) g_free ); + } + + return( type ); +} + +const char * +im_save_string_get( const GValue *value ) +{ + return( (char *) g_value_get_boxed( value ) ); +} + +void +im_save_string_set( GValue *value, const char *str ) +{ + assert( G_VALUE_TYPE( value ) == IM_TYPE_SAVE_STRING ); + + g_value_set_boxed( value, str ); +} + +void +im_save_string_setf( GValue *value, const char *fmt, ... ) +{ + va_list ap; + char *str; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_SAVE_STRING ); + + va_start( ap, fmt ); + str = g_strdup_vprintf( fmt, ap ); + va_end( ap ); + + im_save_string_set( value, str ); +} + +/* Read/write int, double metadata. + */ +int +im_meta_set_int( IMAGE *im, const char *field, int i ) +{ + GValue value = { 0 }; + + g_value_init( &value, G_TYPE_INT ); + g_value_set_int( &value, i ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_int( IMAGE *im, const char *field, int *i ) +{ + GValue value_copy = { 0 }; + + if( meta_get_value( im, field, G_TYPE_INT, &value_copy ) ) + return( -1 ); + *i = g_value_get_int( &value_copy ); + g_value_unset( &value_copy ); + + return( 0 ); +} + +int +im_meta_set_double( IMAGE *im, const char *field, double d ) +{ + GValue value = { 0 }; + + g_value_init( &value, G_TYPE_DOUBLE ); + g_value_set_double( &value, d ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_double( IMAGE *im, const char *field, double *d ) +{ + GValue value_copy = { 0 }; + + if( meta_get_value( im, field, G_TYPE_DOUBLE, &value_copy ) ) + return( -1 ); + *d = g_value_get_double( &value_copy ); + g_value_unset( &value_copy ); + + return( 0 ); +} + +/* Transform funcs for builtin types to SAVE_STRING. + */ +static void +transform_int_save_string( const GValue *src_value, GValue *dest_value ) +{ + im_save_string_setf( dest_value, "%d", g_value_get_int( src_value ) ); +} + +static void +transform_save_string_int( const GValue *src_value, GValue *dest_value ) +{ + g_value_set_int( dest_value, atoi( im_save_string_get( src_value ) ) ); +} + +static void +transform_double_save_string( const GValue *src_value, GValue *dest_value ) +{ + char buf[G_ASCII_DTOSTR_BUF_SIZE]; + + /* Need to be locale independent. + */ + g_ascii_dtostr( buf, G_ASCII_DTOSTR_BUF_SIZE, + g_value_get_double( src_value ) ); + im_save_string_set( dest_value, buf ); +} + +static void +transform_save_string_double( const GValue *src_value, GValue *dest_value ) +{ + g_value_set_double( dest_value, + g_ascii_strtod( im_save_string_get( src_value ), NULL ) ); +} + +/* A GType for a ref-counted area of memory. + */ +typedef struct _Area { + int count; + size_t length; /* 0 if not known */ + void *data; + im_callback_fn free_fn; +} Area; + +#ifdef DEBUG +static int area_number = 0; +#endif /*DEBUG*/ + +/* An area of mem with a free func. (eg. \0-terminated string, or a struct). + * Inital count == 1, so _unref() after attaching somewhere. + */ +static Area * +area_new( im_callback_fn free_fn, void *data ) +{ + Area *area; + + if( !(area = IM_NEW( NULL, Area )) ) + return( NULL ); + area->count = 1; + area->length = 0; + area->data = data; + area->free_fn = free_fn; + +#ifdef DEBUG + area_number += 1; + printf( "area_new: %p count = %d (%d in total)\n", + area, area->count, area_number ); +#endif /*DEBUG*/ + + return( area ); +} + +/* An area of mem with a free func and a length (some sort of binary object, + * like an ICC profile). + */ +static Area * +area_new_blob( im_callback_fn free_fn, void *blob, size_t blob_length ) +{ + Area *area; + + if( !(area = area_new( free_fn, blob )) ) + return( NULL ); + area->length = blob_length; + + return( area ); +} + +static Area * +area_copy( Area *area ) +{ + assert( area->count >= 0 ); + + area->count += 1; + +#ifdef DEBUG + printf( "area_copy: %p count = %d\n", area, area->count ); +#endif /*DEBUG*/ + + return( area ); +} + +static void +area_unref( Area *area ) +{ + assert( area->count > 0 ); + + area->count -= 1; + +#ifdef DEBUG + printf( "area_unref: %p count = %d\n", area, area->count ); +#endif /*DEBUG*/ + + if( area->count == 0 && area->free_fn ) { + (void) area->free_fn( area->data, NULL ); + area->free_fn = NULL; + im_free( area ); + +#ifdef DEBUG + area_number -= 1; + printf( "area_unref: free .. total = %d\n", area_number ); +#endif /*DEBUG*/ + } +} + +/* Transform an area to a G_TYPE_STRING. + */ +static void +transform_area_g_string( const GValue *src_value, GValue *dest_value ) +{ + Area *area; + char buf[256]; + + area = g_value_get_boxed( src_value ); + im_snprintf( buf, 256, "IM_TYPE_AREA, count = %d, data = %p", + area->count, area->data ); + g_value_set_string( dest_value, buf ); +} + +GType +im_area_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + type = g_boxed_type_register_static( "im_area", + (GBoxedCopyFunc) area_copy, + (GBoxedFreeFunc) area_unref ); + g_value_register_transform_func( + type, + G_TYPE_STRING, + transform_area_g_string ); + } + + return( type ); +} + +/* Set value to be a ref-counted area of memory with a free function. + */ +static int +value_set_area( im_callback_fn free_fn, void *data, GValue *value ) +{ + Area *area; + + if( !(area = area_new( free_fn, data )) ) + return( -1 ); + + g_value_init( value, IM_TYPE_AREA ); + g_value_set_boxed( value, area ); + area_unref( area ); + + return( 0 ); +} + +/* Don't touch count (area is static). + */ +static void * +value_get_area_data( const GValue *value ) +{ + Area *area; + + area = g_value_get_boxed( value ); + + return( area->data ); +} + +static size_t +value_get_area_length( const GValue *value ) +{ + Area *area; + + area = g_value_get_boxed( value ); + + return( area->length ); +} + +/* Read/write ref-counted mem area. + */ +int +im_meta_set_area( IMAGE *im, const char *field, + im_callback_fn free_fn, void *data ) +{ + GValue value = { 0 }; + + value_set_area( free_fn, data, &value ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_area( IMAGE *im, const char *field, void **data ) +{ + GValue value_copy = { 0 }; + + if( meta_get_value( im, field, IM_TYPE_AREA, &value_copy ) ) + return( -1 ); + *data = value_get_area_data( &value_copy ); + g_value_unset( &value_copy ); + + return( 0 ); +} + +/* Get a char* from a refstring. + */ +const char * +im_ref_string_get( const GValue *value ) +{ + return( value_get_area_data( value ) ); +} + +/* Get cached strlen() from a refstring. + */ +size_t +im_ref_string_get_length( const GValue *value ) +{ + return( value_get_area_length( value ) ); +} + +/* Set value to be a ref-counted string. + */ +int +im_ref_string_set( GValue *value, const char *str ) +{ + Area *area; + char *str_copy; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_REF_STRING ); + + if( !(str_copy = im_strdup( NULL, str )) ) + return( -1 ); + if( !(area = area_new( (im_callback_fn) im_free, str_copy )) ) { + im_free( str_copy ); + return( -1 ); + } + + /* Handy place to cache this. + */ + area->length = strlen( str ); + + g_value_set_boxed( value, area ); + area_unref( area ); + + return( 0 ); +} + +/* Transform a refstring to a G_TYPE_STRING. + */ +static void +transform_ref_string_g_string( const GValue *src_value, GValue *dest_value ) +{ + g_value_set_string( dest_value, im_ref_string_get( src_value ) ); +} + +/* To a save string. + */ +static void +transform_ref_string_save_string( const GValue *src_value, GValue *dest_value ) +{ + im_save_string_setf( dest_value, "%s", im_ref_string_get( src_value ) ); +} + +static void +transform_save_string_ref_string( const GValue *src_value, GValue *dest_value ) +{ + im_ref_string_set( dest_value, im_save_string_get( src_value ) ); +} + +GType +im_ref_string_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + type = g_boxed_type_register_static( "im_ref_string", + (GBoxedCopyFunc) area_copy, + (GBoxedFreeFunc) area_unref ); + g_value_register_transform_func( type, G_TYPE_STRING, + transform_ref_string_g_string ); + g_value_register_transform_func( type, IM_TYPE_SAVE_STRING, + transform_ref_string_save_string ); + g_value_register_transform_func( IM_TYPE_SAVE_STRING, type, + transform_save_string_ref_string ); + } + + return( type ); +} + +/* Read/write C string. + */ +int +im_meta_set_string( IMAGE *im, const char *field, const char *str ) +{ + GValue value = { 0 }; + + g_value_init( &value, IM_TYPE_REF_STRING ); + im_ref_string_set( &value, str ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_string( IMAGE *im, const char *field, char **str ) +{ + GValue value_copy = { 0 }; + Area *area; + + if( meta_get_value( im, field, IM_TYPE_REF_STRING, &value_copy ) ) + return( -1 ); + area = g_value_get_boxed( &value_copy ); + *str = area->data; + g_value_unset( &value_copy ); + + return( 0 ); +} + +/* Get a void * from a BLOB. + */ +void * +im_blob_get( const GValue *value, size_t *blob_length ) +{ + Area *area; + + /* Can't check value type, because we may get called from + * im_blob_get_type(). + */ + + area = g_value_get_boxed( value ); + if( blob_length ) + *blob_length = area->length; + + return( area->data ); +} + +/* Transform a blob to a G_TYPE_STRING. + */ +static void +transform_blob_g_string( const GValue *src_value, GValue *dest_value ) +{ + void *blob; + size_t blob_length; + char buf[256]; + + blob = im_blob_get( src_value, &blob_length ); + im_snprintf( buf, 256, "IM_TYPE_BLOB, data = %p, length = %zd", + blob, blob_length ); + g_value_set_string( dest_value, buf ); +} + +/* Transform a blob to a save string and back. + */ +static void +transform_blob_save_string( const GValue *src_value, GValue *dest_value ) +{ + void *blob; + size_t blob_length; + char *b64; + + blob = im_blob_get( src_value, &blob_length ); + if( (b64 = im__b64_encode( blob, blob_length )) ) { + im_save_string_set( dest_value, b64 ); + im_free( b64 ); + } +} + +static void +transform_save_string_blob( const GValue *src_value, GValue *dest_value ) +{ + const char *b64; + void *blob; + size_t blob_length; + + b64 = im_save_string_get( src_value ); + if( (blob = im__b64_decode( b64, &blob_length )) ) + im_blob_set( dest_value, + (im_callback_fn) im_free, blob, blob_length ); +} + +GType +im_blob_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + type = g_boxed_type_register_static( "im_blob", + (GBoxedCopyFunc) area_copy, + (GBoxedFreeFunc) area_unref ); + g_value_register_transform_func( type, G_TYPE_STRING, + transform_blob_g_string ); + g_value_register_transform_func( type, IM_TYPE_SAVE_STRING, + transform_blob_save_string ); + g_value_register_transform_func( IM_TYPE_SAVE_STRING, type, + transform_save_string_blob ); + } + + return( type ); +} + +/* Set value to be a blob. + */ +int +im_blob_set( GValue *value, + im_callback_fn free_fn, void *blob, size_t blob_length ) +{ + Area *area; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_BLOB ); + + if( !(area = area_new_blob( free_fn, blob, blob_length )) ) + return( -1 ); + + g_value_set_boxed( value, area ); + area_unref( area ); + + return( 0 ); +} + +/* Read/write blob. + */ +int +im_meta_set_blob( IMAGE *im, const char *field, + im_callback_fn free_fn, void *blob, size_t blob_length ) +{ + GValue value = { 0 }; + + g_value_init( &value, IM_TYPE_BLOB ); + im_blob_set( &value, free_fn, blob, blob_length ); + + return( meta_set_value( im, field, &value ) ); +} + +int +im_meta_get_blob( IMAGE *im, const char *field, + void **blob, size_t *blob_length ) +{ + GValue value_copy = { 0 }; + + if( meta_get_value( im, field, IM_TYPE_BLOB, &value_copy ) ) + return( -1 ); + *blob = im_blob_get( &value_copy, blob_length ); + g_value_unset( &value_copy ); + + return( 0 ); +} + +/* Make the types we need for basic functioning. Called from init_world(). + */ +void +im__meta_init_types( void ) +{ + (void) im_save_string_get_type(); + (void) im_area_get_type(); + (void) im_ref_string_get_type(); + (void) im_blob_get_type(); + + /* Register transform functions to go from built-in saveable types to + * a save string. Transform functions for our own types are set + * during type creation. + */ + g_value_register_transform_func( G_TYPE_INT, IM_TYPE_SAVE_STRING, + transform_int_save_string ); + g_value_register_transform_func( IM_TYPE_SAVE_STRING, G_TYPE_INT, + transform_save_string_int ); + g_value_register_transform_func( G_TYPE_DOUBLE, IM_TYPE_SAVE_STRING, + transform_double_save_string ); + g_value_register_transform_func( IM_TYPE_SAVE_STRING, G_TYPE_DOUBLE, + transform_save_string_double ); +} diff --git a/libsrc/iofuncs/package.c b/libsrc/iofuncs/package.c new file mode 100644 index 00000000..7513979b --- /dev/null +++ b/libsrc/iofuncs/package.c @@ -0,0 +1,1029 @@ +/* VIPS package handling. + * + * J. Cupitt, 8/4/93. + * + * 18/2/04 JC + * - now uses g_module_*() instead of dlopen() + * 9/8/04 + * - uses glib dir scanning stuff instead of dirent.h + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Standard VIPS packages. + */ +extern im_package im__arithmetic; +extern im_package im__boolean; +extern im_package im__colour; +extern im_package im__conversion; +extern im_package im__convolution; +extern im_package im__freq_filt; +extern im_package im__histograms_lut; +extern im_package im__inplace; +extern im_package im__matrix; +extern im_package im__morphology; +extern im_package im__mosaicing; +extern im_package im__other; +extern im_package im__relational; +extern im_package im__video; + +/* im_guess_prefix() args. + */ +static im_arg_desc guess_prefix_args[] = { + IM_INPUT_STRING( "argv0" ), + IM_INPUT_STRING( "env_name" ), + IM_OUTPUT_STRING( "PREFIX" ) +}; + +/* Call im_guess_prefix() via arg vector. + */ +static int +guess_prefix_vec( im_object *argv ) +{ + const char *prefix = im_guess_prefix( argv[0], argv[1] ); + + if( !prefix ) { + argv[2] = NULL; + return( -1 ); + } + + argv[2] = im_strdup( NULL, prefix ); + + return( 0 ); +} + +/* Description of im_guess_prefix. + */ +static im_function guess_prefix_desc = { + "im_guess_prefix", /* Name */ + "guess install area", /* Description */ + 0, /* Flags */ + guess_prefix_vec, /* Dispatch function */ + IM_NUMBER( guess_prefix_args ), /* Size of arg list */ + guess_prefix_args /* Arg list */ +}; + +/* im_header_int() args. + */ +static im_arg_desc header_int_args[] = { + IM_INPUT_STRING( "field" ), + IM_INPUT_IMAGE( "image" ), + IM_OUTPUT_INT( "value" ) +}; + +/* Call im_header_int() via arg vector. + */ +static int +header_int_vec( im_object *argv ) +{ + return( im_header_int( (IMAGE *) argv[1], (const char *) argv[0], + (int *) argv[2] ) ); +} + +/* Description of im_header_int(). + */ +static im_function header_int_desc = { + "im_header_int", /* Name */ + "extract int fields from header", /* Description */ + 0, /* Flags */ + header_int_vec, /* Dispatch function */ + IM_NUMBER( header_int_args ), /* Size of arg list */ + header_int_args /* Arg list */ +}; + +/* im_header_get_type() args. + */ +static im_arg_desc header_get_type_args[] = { + IM_INPUT_STRING( "field" ), + IM_INPUT_IMAGE( "image" ), + IM_OUTPUT_INT( "gtype" ) +}; + +/* Call im_header_get_type() via arg vector. + */ +static int +header_get_type_vec( im_object *argv ) +{ + int *out = ((int *) argv[2]); + + *out = im_header_get_type( (IMAGE *) argv[1], (const char *) argv[0] ); + + return( 0 ); +} + +/* Description of im_header_get_type(). + */ +static im_function header_get_type_desc = { + "im_header_get_type", /* Name */ + "return field type", /* Description */ + 0, /* Flags */ + header_get_type_vec, /* Dispatch function */ + IM_NUMBER( header_get_type_args ),/* Size of arg list */ + header_get_type_args /* Arg list */ +}; + +/* im_header_double() args. + */ +static im_arg_desc header_double_args[] = { + IM_INPUT_STRING( "field" ), + IM_INPUT_IMAGE( "image" ), + IM_OUTPUT_DOUBLE( "value" ) +}; + +/* Call im_header_double() via arg vector. + */ +static int +header_double_vec( im_object *argv ) +{ + return( im_header_double( (IMAGE *) argv[1], (const char *) argv[0], + (double *) argv[2] ) ); +} + +/* Description of im_header_double(). + */ +static im_function header_double_desc = { + "im_header_double", /* Name */ + "extract double fields from header", /* Description */ + 0, /* Flags */ + header_double_vec, /* Dispatch function */ + IM_NUMBER( header_double_args ), /* Size of arg list */ + header_double_args /* Arg list */ +}; + +/* im_header_string() args. + */ +static im_arg_desc header_string_args[] = { + IM_INPUT_STRING( "field" ), + IM_INPUT_IMAGE( "image" ), + IM_OUTPUT_STRING( "value" ) +}; + +/* Call im_header_string() via arg vector. + */ +static int +header_string_vec( im_object *argv ) +{ + char *out; + + if( im_header_string( (IMAGE *) argv[1], + (const char *) argv[0], &out ) || + !(argv[2] = im_strdup( NULL, out )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_header_string(). + */ +static im_function header_string_desc = { + "im_header_string", /* Name */ + "extract string fields from header", /* Description */ + 0, /* Flags */ + header_string_vec, /* Dispatch function */ + IM_NUMBER( header_string_args ), /* Size of arg list */ + header_string_args /* Arg list */ +}; + +/* im_version_string() args. + */ +static im_arg_desc version_string_args[] = { + IM_OUTPUT_STRING( "version" ) +}; + +/* Call im_version_string() via arg vector. + */ +static int +version_string_vec( im_object *argv ) +{ + if( !(argv[0] = im_strdup( NULL, im_version_string() )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_version_string. + */ +static im_function version_string_desc = { + "im_version_string", /* Name */ + "VIPS version string", /* Description */ + 0, /* Flags */ + version_string_vec, /* Dispatch function */ + IM_NUMBER( version_string_args ), /* Size of arg list */ + version_string_args /* Arg list */ +}; + +/* im_version() args. + */ +static im_arg_desc version_args[] = { + IM_INPUT_INT( "flag" ), + IM_OUTPUT_INT( "version" ) +}; + +/* Call im_version() via arg vector. + */ +static int +version_vec( im_object *argv ) +{ + int flag = *((int *) argv[0]); + int *out = ((int *) argv[1]); + + int version = im_version( flag ); + + if( version < 0 ) + return( -1 ); + + *out = version; + + return( 0 ); +} + +/* Description of im_version. + */ +static im_function version_desc = { + "im_version", /* Name */ + "VIPS version number", /* Description */ + 0, /* Flags */ + version_vec, /* Dispatch function */ + IM_NUMBER( version_args ), /* Size of arg list */ + version_args /* Arg list */ +}; + +/* im_cache() args. + */ +static im_arg_desc cache_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "tile_width" ), + IM_INPUT_INT( "tile_height" ), + IM_INPUT_INT( "max_tiles" ) +}; + +/* Call im_cache() via arg vector. + */ +static int +cache_vec( im_object *argv ) +{ + int tile_width = *((int *) argv[2]); + int tile_height = *((int *) argv[3]); + int max_tiles = *((int *) argv[4]); + + return( im_cache( argv[0], argv[1], + tile_width, tile_height, max_tiles ) ); +} + +/* Description of im_cache. + */ +static im_function cache_desc = { + "im_cache", /* Name */ + "cache results of an operation",/* Description */ + 0, /* Flags */ + cache_vec, /* Dispatch function */ + IM_NUMBER( cache_args ), /* Size of arg list */ + cache_args /* Arg list */ +}; + +/* im_binfile() args. + */ +static im_arg_desc binfile_args[] = { + IM_INPUT_STRING( "filename" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "width" ), + IM_INPUT_INT( "height" ), + IM_INPUT_INT( "bands" ), + IM_INPUT_INT( "offset" ) +}; + +/* Call im_binfile() via arg vector. + */ +static int +binfile_vec( im_object *argv ) +{ + int width = *((int *) argv[2]); + int height = *((int *) argv[3]); + int bands = *((int *) argv[4]); + int offset = *((int *) argv[5]); + IMAGE *im; + + if( !(im = im_binfile( argv[0], width, height, bands, offset )) ) + return( -1 ); + + if( im_copy( im, argv[1] ) || + im_add_close_callback( argv[1], + (im_callback_fn) im_close, im, NULL ) ) { + im_close( im ); + return( -1 ); + } + + return( 0 ); +} + +/* Description of im_binfile. + */ +static im_function binfile_desc = { + "im_binfile", /* Name */ + "open a headerless binary file",/* Description */ + 0, /* Flags */ + binfile_vec, /* Dispatch function */ + IM_NUMBER( binfile_args ), /* Size of arg list */ + binfile_args /* Arg list */ +}; + +/* Package up iofuncs functions. + */ +static im_function *iofuncs_list[] = { + &binfile_desc, + &cache_desc, + &guess_prefix_desc, + &header_get_type_desc, + &header_int_desc, + &header_double_desc, + &header_string_desc, + &version_desc, + &version_string_desc +}; + +/* Package of io functions. + */ +static im_package im__iofuncs = { + "iofuncs", + IM_NUMBER( iofuncs_list ), + iofuncs_list +}; + +/* List of built-in VIPS packages. + */ +static im_package *built_in[] = { + &im__arithmetic, + &im__boolean, + &im__colour, + &im__conversion, + &im__convolution, + &im__freq_filt, + &im__histograms_lut, + &im__inplace, + &im__iofuncs, + &im__matrix, + &im__morphology, + &im__mosaicing, + &im__other, + &im__relational, + &im__video +}; + +/* How we represent a loaded plugin. + */ +typedef struct _Plugin { + GModule *module; /* As loaded by g_module_open() */ + char *name; /* Name we loaded */ + im_package *pack; /* Package table */ +} Plugin; + +/* List of loaded plugins. + */ +static GSList *plugin_list = NULL; + +/* Free a plugin. + */ +static int +plugin_free( Plugin *plug ) +{ + char *name = plug->name ? plug->name : ""; + + if( plug->module ) { + if( !g_module_close( plug->module ) ) { + im_error( "plugin", + _( "unable to close plugin \"%s\"" ), name ); + im_error( "plugin", "%s", g_module_error() ); + return( -1 ); + } + + plug->module = NULL; + } + IM_FREE( plug->name ); + plug->pack = NULL; + im_free( plug ); + + plugin_list = g_slist_remove( plugin_list, plug ); + + return( 0 ); +} + +/* Load a plugin. + */ +im_package * +im_load_plugin( const char *name ) +{ + Plugin *plug; + + if( !g_module_supported() ) { + im_error( "plugin", + _( "plugins not supported on this platform" ) ); + return( NULL ); + } + + /* Build a new plugin. + */ + if( !(plug = IM_NEW( NULL, Plugin )) ) + return( NULL ); + plug->module = NULL; + plug->name = NULL; + plug->pack = NULL; + plugin_list = g_slist_prepend( plugin_list, plug ); + + /* Attach name. + */ + if( !(plug->name = im_strdup( NULL, name )) ) { + plugin_free( plug ); + return( NULL ); + } + + /* Open library. + */ + if( !(plug->module = g_module_open( name, 0 )) ) { + im_error( "plugin", _( "unable to open plugin \"%s\"" ), name ); + im_error( "plugin", "%s", g_module_error() ); + plugin_free( plug ); + + return( NULL ); + } + + /* Find package. + */ + /* Bizarre double-cast stops a bogus gcc 4.1 compiler warning. + */ + if( !g_module_symbol( plug->module, + "package_table", (gpointer *) ((void *) &plug->pack) ) ) { + im_error( "plugin", + _( "unable to find symbol \"package_table\" " + "in plugin \"%s\"" ), name ); + im_error( "plugin", "%s", g_module_error() ); + plugin_free( plug ); + + return( NULL ); + } + + /* Sanity check. + */ + if( !plug->pack->name || plug->pack->nfuncs < 0 || + plug->pack->nfuncs > 10000 ) { + im_error( "plugin", + _( "corrupted package table in plugin \"%s\"" ), + name ); + plugin_free( plug ); + + return( NULL ); + } + +#ifdef DEBUG + printf( "added package \"%s\" ...\n", plug->pack->name ); +#endif /*DEBUG*/ + + return( plug->pack ); +} + +/* Load all plugins in a directory ... look for '.plg' suffix. Error if we had + * any probs. + */ +int +im_load_plugins( const char *fmt, ... ) +{ + va_list ap; + char dir_name[PATH_MAX]; + GDir *dir; + const char *name; + int result; + + /* Silently succeed if we can't do modules. + */ + if( !g_module_supported() ) + return( 0 ); + + va_start( ap, fmt ); + (void) im_vsnprintf( dir_name, PATH_MAX - 1, fmt, ap ); + va_end( ap ); + + if( !(dir = g_dir_open( dir_name, 0, NULL )) ) { + im_error( "plugin", + "unable to open directory \"%s\"", dir_name ); + return( -1 ); + } + + result = 0; + while( (name = g_dir_read_name( dir )) ) + if( im_ispostfix( name, ".plg" ) ) { + char path[PATH_MAX]; + + im_snprintf( path, PATH_MAX - 1, + "%s" G_DIR_SEPARATOR_S "%s", dir_name, name ); + if( !im_load_plugin( path ) ) + result = -1; + } + g_dir_close( dir ); + + return( result ); +} + +/* Close all loaded plugins. + */ +int +im_close_plugins( void ) +{ + while( plugin_list ) + if( plugin_free( (Plugin *) plugin_list->data ) ) + return( -1 ); + + return( 0 ); +} + +/* Apply a user-function to a plugin package. + */ +static void * +apply_plugin( Plugin *plug, VSListMap2Fn fn, void *a ) +{ + if( !plug->pack ) + return( NULL ); + else + return( fn( plug->pack, a, NULL ) ); +} + +/* Map a function over all packages. Map over plugins first, to allow + * overriding of VIPS functions. + */ +void * +im_map_packages( VSListMap2Fn fn, void *a ) +{ + void *r = im_slist_map2( plugin_list, + (VSListMap2Fn) apply_plugin, (void *) fn, a ); + int i; + + /* If not there, try main VIPS package list. + */ + if( !r ) + for( i = 0; i < IM_NUMBER( built_in ); i++ ) + if( (r = fn( built_in[i], a, NULL )) ) + return( r ); + + return( r ); +} + +/* Search a package for a function. + */ +static im_function * +search_package( im_package *pack, const char *name ) +{ + int i; + + for( i = 0; i < pack->nfuncs; i++ ) + if( strcmp( pack->table[i]->name, name ) == 0 ) + return( pack->table[i] ); + + return( NULL ); +} + +/* Search all packages for a function. + */ +im_function * +im_find_function( const char *name ) +{ + im_function *fn = im_map_packages( + (VSListMap2Fn) search_package, (void *) name ); + + if( !fn ) { + im_error( "im_find_function", _( "\"%s\" not found" ), name ); + return( NULL ); + } + + return( fn ); +} + +/* Test for package is of name. + */ +static im_package * +package_name( im_package *pack, const char *name ) +{ + if( strcmp( pack->name, name ) == 0 ) + return( pack ); + + return( NULL ); +} + +/* Find a package. + */ +im_package * +im_find_package( const char *name ) +{ + im_package *pack = im_map_packages( + (VSListMap2Fn) package_name, (void *) name ); + + if( !pack ) { + im_error( "im_find_package", _( "\"%s\" not found" ), name ); + return( NULL ); + } + + return( pack ); +} + +/* Test for package contains a function. + */ +static im_package * +package_function( im_package *pack, const char *name ) +{ + if( search_package( pack, name ) ) + return( pack ); + else + return( NULL ); +} + +/* Find a function's package by name. + */ +im_package * +im_package_of_function( const char *name ) +{ + im_package *pack = im_map_packages( + (VSListMap2Fn) package_function, (void *) name ); + + if( !pack ) { + im_error( "im_package_of_function", + _( "\"%s\" not found" ), name ); + return( NULL ); + } + + return( pack ); +} + +/* Free any store we allocated for the argument list. + */ +int +im_free_vargv( im_function *fn, im_object *vargv ) +{ + int i; + int vargc = fn->argc; + + /* Free all elements. + */ + for( i = 0; i < vargc; i++ ) + if( vargv[i] ) { + /* If there is local storage, free it. + */ + if( fn->argv[i].desc->size != 0 ) + im_free( vargv[i] ); + + /* NULL out pointer. + */ + vargv[i] = NULL; + } + + return( 0 ); +} + +/* Allocate any local store the args will need; NULL out all others. + */ +int +im_allocate_vargv( im_function *fn, im_object *vargv ) +{ + int i; + int vargc = fn->argc; + + /* NULL out all pointers. + */ + for( i = 0; i < vargc; i++ ) + vargv[i] = NULL; + + /* Allocate any space we will need. + */ + for( i = 0; i < vargc; i++ ) { + int sz = fn->argv[i].desc->size; + + if( sz != 0 ) + if( !(vargv[i] = im_malloc( NULL, sz )) ) { + /* Free anything we did allocate. + */ + (void) im_free_vargv( fn, vargv ); + return( -1 ); + } + + /* Zero memory. + */ + memset( vargv[i], 0, sz ); + } + + return( 0 ); +} + +/* Destroy the objects in the arg list. + */ +static int +destroy_args( im_function *fn, im_object *vargv ) +{ + int i; + int vargc = fn->argc; + + /* Destoy all elements with destroy functions. + */ + for( i = 0; i < vargc; i++ ) + if( vargv[i] ) + /* If there's a destroy function for this type, + * trigger it. + */ + if( fn->argv[i].desc->dest && + fn->argv[i].desc->dest( vargv[i] ) ) + return( -1 ); + + return( 0 ); +} + +/* Init an im_object array from a set of command-line arguments. + */ +static int +build_args( im_function *fn, im_object *vargv, int argc, char **argv ) +{ + im_arg_desc *arg = fn->argv; + int vargc = fn->argc; + char *str; + int i, j; + + /* Loop, constructing each im_arg. + */ + for( i = 0, j = 0; i < vargc; i++ ) { + /* Find type for this arg. + */ + im_type_desc *type = arg[i].desc; + + /* Do we need to use up a command line argument? + */ + if( type->flags & IM_TYPE_ARG ) { + if( !argv[j] ) { + im_error( "im_run_command", + _( "too few arguments" ) ); + return( -1 ); + } + str = argv[j++]; + + /* Init object. + */ + if( type->init && type->init( &vargv[i], str ) ) + return( -1 ); + } + else { + /* Init object with no arg. + */ + if( type->init && type->init( &vargv[i], "no arg" ) ) + return( -1 ); + } + } + + /* Have we used up all the command-line args? + */ + if( argv[j] ) { + im_error( "im_run_command", _( "too many arguments" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* Free a region, but return 0 so we can be used as a close callback. + */ +static int +region_free( REGION *reg ) +{ + im_region_free( reg ); + + return( 0 ); +} + +/* Make a region on sub, closed by callback on main. + */ +static int +region_local_image( IMAGE *main, IMAGE *sub ) +{ + REGION *reg; + + if( !(reg = im_region_create( sub )) ) + return( -1 ); + if( im_add_close_callback( main, + (im_callback_fn) region_free, reg, NULL ) ) { + im_region_free( reg ); + + return( -1 ); + } + + return( 0 ); +} + +/* i is an output image on a PIO function ... make all input images depend + * on it. + */ +static int +note_dependencies( im_function *fn, im_object *vargv, int i ) +{ + int j; + + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *type = fn->argv[j].desc; + + if( !(type->flags & IM_TYPE_OUTPUT) && + strcmp( type->type, IM_TYPE_IMAGE ) == 0 ) { + if( region_local_image( vargv[i], vargv[j] ) ) + return( -1 ); + } + } + + return( 0 ); +} + +/* Call all defined print functions. + */ +static int +print_args( im_function *fn, im_object *vargv ) +{ + int i; + int vargc = fn->argc; + + /* Print all elements. + */ + for( i = 0; i < vargc; i++ ) + if( fn->argv[i].print && vargv[i] ) + if( fn->argv[i].print( vargv[i] ) ) + return( -1 ); + + return( 0 ); +} + +/* Add to the hist of all output images. + */ +static int +add_hist( im_function *fn, im_object *vargv, int argc, char **argv ) +{ + int i; + int vargc = fn->argc; + + /* Search for output images. + */ + for( i = 0; i < vargc; i++ ) + if( strcmp( fn->argv[i].desc->type, IM_TYPE_IMAGE ) == 0 && + (fn->argv[i].desc->flags & IM_TYPE_OUTPUT) ) + if( im_updatehist( vargv[i], fn->name, argc, argv ) ) + return( -1 ); + + return( 0 ); +} + +/* Call a VIPS function. + */ +static int +dispatch_function( im_function *fn, im_object *vargv, int argc, char **argv ) +{ + int i; + + /* Init memory from command line arguments. + */ + if( build_args( fn, vargv, argc, argv ) ) + return( -1 ); + + /* If this is a PIO function, we need to make sure that we close + * the input images after the output images, since the output image + * may include delayed image conversion filters which will not run + * until the output is closed. + * + * Do this by: + * - for each output image + * - for each input image + * - create a region on the input, closed by a + * close callback on the output image + */ + if( fn->flags & IM_FN_PIO ) + for( i = 0; i < fn->argc; i++ ) { + im_type_desc *type = fn->argv[i].desc; + + if( type->flags & IM_TYPE_OUTPUT && + strcmp( type->type, IM_TYPE_IMAGE ) == 0 ) + if( note_dependencies( fn, vargv, i ) ) + return( -1 ); + } + + /* Call function. + */ + if( fn->disp( vargv ) ) + return( -1 ); + + /* Print output. + */ + if( print_args( fn, vargv ) ) + return( -1 ); + + /* Add to history of all output images. + */ + if( add_hist( fn, vargv, argc, argv ) ) + return( -1 ); + + /* All ok! + */ + return( 0 ); +} + +/* Run a command. + */ +int +im_run_command( char *name, int argc, char **argv ) +{ + static im_object object_array[IM_MAX_ARGS]; + im_object *vargv = object_array; + im_function *fn; + + /* Search packages for a matching function. + */ + if( !(fn = im_find_function( name )) ) + return( -1 ); + + /* Allocate space for arguments. + */ + if( im_allocate_vargv( fn, vargv ) ) + return( -1 ); + + /* Call it. + */ + if( dispatch_function( fn, vargv, argc, argv ) ) { + destroy_args( fn, vargv ); + im_free_vargv( fn, vargv ); + return( -1 ); + } + + /* Clean up and exit. + */ + if( destroy_args( fn, vargv ) ) + return( -1 ); + im_free_vargv( fn, vargv ); + + return( 0 ); +} + +/* Return the version string from configure.in + */ +const char * +im_version_string( void ) +{ + return( IM_VERSION_STRING ); +} + +/* Return major/minor/micro release numbers. + */ +int +im_version( int flag ) +{ + switch( flag ) { + case 0: + return( IM_MAJOR_VERSION ); + + case 1: + return( IM_MINOR_VERSION ); + + case 2: + return( IM_MICRO_VERSION ); + + default: + im_error( "im_version", _( "flag not 0,1,2" ) ); + return( -1 ); + } +} diff --git a/libsrc/iofuncs/predicate.c b/libsrc/iofuncs/predicate.c new file mode 100644 index 00000000..45b273fa --- /dev/null +++ b/libsrc/iofuncs/predicate.c @@ -0,0 +1,528 @@ +/* Test various predicates. + * + * J.Cupitt, 8/4/93. + * 13/10/95 JC + * - ANSIfied + * - im_ispoweroftwo() added + * 14/11/96 Jc + * - im_isjpeg() added + * 25/3/97 JC + * - im_isvips() added + * 14/4/97 JC + * - im_istifftiled() added + * 29/10/98 JC + * - im_isMSBfirst() and im_amiMSBfirst() added + * 16/6/99 JC + * - added im_existsf() + * 22/11/00 JC + * - added im_isppm() + * 23/4/01 JC + * - HAVE_TIFF turns on TIFFness + * 19/10/02 HB + * - PNG added + * 1/5/06 + * - added exr + * 3/8/07 + * - cleanups + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#ifdef HAVE_IO_H +#include +#endif /*HAVE_IO_H*/ +#include +#include + +#include + +#ifdef HAVE_TIFF +#include +#endif /*HAVE_TIFF*/ + +#ifdef HAVE_PNG +#include +#endif /*HAVE_PNG*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Test BandFmt. + */ +int +im_isint( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + return( 1 ); + + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 0 ); + + default: + error_exit( "im_isint: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +/* Test endianness of an image. SPARC is MSB first + */ +int +im_isMSBfirst( IMAGE *im ) +{ + if( im->magic == IM_MAGIC_SPARC ) + return( 1 ); + else + return( 0 ); +} + +/* Test this processor for endianness. True for SPARC order. + */ +int +im_amiMSBfirst( void ) +{ + int test; + unsigned char *p = (unsigned char *) &test; + + test = 0; + p[0] = 255; + + if( test == 255 ) + return( 0 ); + else + return( 1 ); +} + +int +im_isuint( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_UINT: + return( 1 ); + + case IM_BANDFMT_INT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 0 ); + + default: + error_exit( "im_isuint: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +int +im_isfloat( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + return( 1 ); + + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 0 ); + + default: + error_exit( "im_isfloat: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +int +im_isscalar( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + return( 1 ); + + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 0 ); + + default: + error_exit( "im_isscalar: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +int +im_iscomplex( IMAGE *im ) +{ + switch( im->BandFmt ) { + case IM_BANDFMT_COMPLEX: + case IM_BANDFMT_DPCOMPLEX: + return( 1 ); + + case IM_BANDFMT_UCHAR: + case IM_BANDFMT_CHAR: + case IM_BANDFMT_USHORT: + case IM_BANDFMT_SHORT: + case IM_BANDFMT_UINT: + case IM_BANDFMT_INT: + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_DOUBLE: + return( 0 ); + + default: + error_exit( "im_iscomplex: unknown image BandFmt" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +/* Read a few bytes from the start of a file. For sniffing file types. + */ +static int +get_bytes( const char *filename, unsigned char buf[], int len ) +{ + int fd; + + /* File may not even exist (for tmp images for example!) + * so no hasty messages. And the file might be truncated, so no error + * on read either. + */ +#ifdef BINARY_OPEN + if( (fd = open( filename, O_RDONLY | O_BINARY )) == -1 ) +#else /*BINARY_OPEN*/ + if( (fd = open( filename, O_RDONLY )) == -1 ) +#endif /*BINARY_OPEN*/ + return( 0 ); + if( read( fd, buf, len ) != len ) { + close( fd ); + return( 0 ); + } + close( fd ); + + return( 1 ); +} + +int +im_istiff( const char *filename ) +{ + unsigned char buf[2]; + + if( get_bytes( filename, buf, 2 ) ) + if( (buf[0] == 'M' && buf[1] == 'M') || + (buf[0] == 'I' && buf[1] == 'I') ) + return( 1 ); + + return( 0 ); +} + +#ifdef HAVE_PNG +int +im_ispng( const char *filename ) +{ + unsigned char buf[8]; + + return( get_bytes( filename, buf, 8 ) && + !png_sig_cmp( buf, 0, 8 ) ); +} +#else /*HAVE_PNG*/ +int +im_ispng( const char *filename ) +{ + return( 0 ); +} +#endif /*HAVE_PNG*/ + +#ifdef HAVE_MAGICK +int +im_ismagick( const char *filename ) +{ + IMAGE *im; + int result; + + if( !(im = im_open( "dummy", "p" )) ) + return( -1 ); + result = im_magick2vips_header( filename, im ); + im_clear_error_string(); + im_close( im ); + + return( result == 0 ); +} +#else /*HAVE_MAGICK*/ +int +im_ismagick( const char *filename ) +{ + return( 0 ); +} +#endif /*HAVE_MAGICK*/ + +int +im_isppm( const char *filename ) +{ + unsigned char buf[2]; + + if( get_bytes( filename, buf, 2 ) ) + if( buf[0] == 'P' && (buf[1] >= '1' || buf[1] <= '6') ) + return( 1 ); + + return( 0 ); +} + +#ifdef HAVE_TIFF + +/* Handle TIFF errors here. + */ +static void +vhandle( char *module, char *fmt, ... ) +{ + va_list ap; + + im_errormsg( "TIFF error in \"%s\": ", module ); + + va_start( ap, fmt ); + im_verrormsg( fmt, ap ); + va_end( ap ); +} + +int +im_istifftiled( const char *filename ) +{ + TIFF *tif; + int tiled; + + /* Override the default TIFF error handler. + */ + TIFFSetErrorHandler( (TIFFErrorHandler) vhandle ); + +#ifdef BINARY_OPEN + if( !(tif = TIFFOpen( filename, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(tif = TIFFOpen( filename, "r" )) ) { +#endif /*BINARY_OPEN*/ + /* Not a TIFF file ... return False. + */ + im_clear_error_string(); + return( 0 ); + } + tiled = TIFFIsTiled( tif ); + TIFFClose( tif ); + + return( tiled ); +} + +#else /*HAVE_TIFF*/ + +int +im_istifftiled( const char *filename ) +{ + return( 0 ); +} + +#endif /*HAVE_TIFF*/ + +int +im_isjpeg( const char *filename ) +{ + unsigned char buf[2]; + + if( get_bytes( filename, buf, 2 ) ) + if( (int) buf[0] == 0xff && (int) buf[1] == 0xd8 ) + return( 1 ); + + return( 0 ); +} + +int +im_isvips( const char *filename ) +{ + unsigned char buf[4]; + + if( get_bytes( filename, buf, 4 ) ) { + if( buf[0] == 0x08 && buf[1] == 0xf2 && + buf[2] == 0xa6 && buf[3] == 0xb6 ) + /* SPARC-order VIPS image. + */ + return( 1 ); + else if( buf[3] == 0x08 && buf[2] == 0xf2 && + buf[1] == 0xa6 && buf[0] == 0xb6 ) + /* INTEL-order VIPS image. + */ + return( 1 ); + } + + return( 0 ); +} + +int +im_isexr( const char *filename ) +{ + unsigned char buf[4]; + + if( get_bytes( filename, buf, 4 ) ) + if( buf[0] == 0x76 && buf[1] == 0x2f && + buf[2] == 0x31 && buf[3] == 0x01 ) + return( 1 ); + + return( 0 ); +} + +/* Test for file exists. + */ +int +im_existsf( const char *name, ... ) +{ + va_list ap; + char buf1[PATH_MAX]; + + va_start( ap, name ); + (void) im_vsnprintf( buf1, PATH_MAX - 1, name, ap ); + va_end( ap ); + + /* Try that. + */ + if( !access( buf1, R_OK ) ) + return( 1 ); + + return( 0 ); +} + +/* True if this IMAGE is a disc file of some sort. + */ +int +im_isfile( IMAGE *im ) +{ + switch( im->dtype ) { + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENOUT: + case IM_OPENIN: + return( 1 ); + + case IM_PARTIAL: + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_NONE: + return( 0 ); + + default: + error_exit( "im_isfile: corrupt IMAGE descriptor" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +/* True if this IMAGE is a partial of some sort. + */ +int +im_ispartial( IMAGE *im ) +{ + switch( im->dtype ) { + case IM_PARTIAL: + return( 1 ); + + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + case IM_MMAPIN: + case IM_MMAPINRW: + case IM_OPENIN: + case IM_OPENOUT: + case IM_NONE: + return( 0 ); + + default: + error_exit( "im_ispartial: corrupt IMAGE descriptor" ); + /*NOTREACHED*/ + return( -1 ); + } +} + +/* True if an int is a power of two ... 1, 2, 4, 8, 16, 32, etc. Do with just + * integer arithmetic for portability. A previous Nicos version using doubles + * and log/log failed on x86 with rounding problems. Return 0 for not + * power of two, otherwise return the position of the set bit (numbering with + * bit 1 as the lsb). + */ +int +im_ispoweroftwo( int p ) +{ + int i, n; + + /* Count set bits. Could use a LUT, I guess. + */ + for( i = 0, n = 0; p; i++, p >>= 1 ) + if( (p & 1) == 1 ) + n++; + + /* Should be just one set bit. + */ + if( n == 1 ) + /* Return position of bit. + */ + return( i ); + else + return( 0 ); +} diff --git a/libsrc/iofuncs/rect.c b/libsrc/iofuncs/rect.c new file mode 100644 index 00000000..7eb59222 --- /dev/null +++ b/libsrc/iofuncs/rect.c @@ -0,0 +1,167 @@ +/* Simple rectangle algebra. Should build rectangle list algebra on top of + * this. + * + * J. Cupitt, 8/4/93. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Move the margins of a rect. +1 means out one pixel. + */ +void +im_rect_marginadjust( Rect *r, int n ) +{ + r->left -= n; + r->top -= n; + r->width += 2 * n; + r->height += 2 * n; +} + +/* Does rect contain a point? + */ +int +im_rect_includespoint( Rect *r, int x, int y ) +{ + return( r->left <= x && + r->top <= y && + r->left + r->width > x && + r->top + r->height > y ); +} + +/* Is r2 a subset of r1? + */ +int +im_rect_includesrect( Rect *r1, Rect *r2 ) +{ + return( r1->left <= r2->left && + r1->top <= r2->top && + r1->left + r1->width >= r2->left + r2->width && + r1->top + r1->height >= r2->top + r2->height ); +} + +/* Fill r3 with the intersection of r1 and r2. r3 can equal r1 or r2. + */ +void +im_rect_intersectrect( Rect *r1, Rect *r2, Rect *r3 ) +{ + int left = IM_MAX( r1->left, r2->left ); + int top = IM_MAX( r1->top, r2->top ); + int right = IM_MIN( IM_RECT_RIGHT( r1 ), IM_RECT_RIGHT( r2 ) ); + int bottom = IM_MIN( IM_RECT_BOTTOM( r1 ), IM_RECT_BOTTOM( r2 ) ); + int width = IM_MAX( 0, right - left ); + int height = IM_MAX( 0, bottom - top ); + + r3->left = left; + r3->top = top; + r3->width = width; + r3->height = height; +} + +/* Is a rect empty? ie. zero width or height. + */ +int +im_rect_isempty( Rect *r ) +{ + return( r->width <= 0 || r->height <= 0 ); +} + +/* Fill r3 with the set union of r1 and r2. Can't do this very well, as can + * only have rectangular areas. Just set to smallest box that encloses both r1 + * and r2. If either is empty, can just return the other. + */ +void +im_rect_unionrect( Rect *r1, Rect *r2, Rect *r3 ) +{ + if( im_rect_isempty( r1 ) ) + *r3 = *r2; + else if( im_rect_isempty( r2 ) ) + *r3 = *r1; + else { + int left = IM_MIN( r1->left, r2->left ); + int top = IM_MIN( r1->top, r2->top ); + int width = IM_MAX( IM_RECT_RIGHT( r1 ), + IM_RECT_RIGHT( r2 ) ) - left; + int height = IM_MAX( IM_RECT_BOTTOM( r1 ), + IM_RECT_BOTTOM( r2 ) )- top; + + r3->left = left; + r3->top = top; + r3->width = width; + r3->height = height; + } +} + +/* Test for equality. + */ +int +im_rect_equalsrect( Rect *r1, Rect *r2 ) +{ + return( r1->left == r2->left && r1->top == r2->top && + r1->width == r2->width && r1->height == r2->height ); +} + +/* DUP a rect. + */ +Rect * +im_rect_dup( Rect *r ) +{ + Rect *out = IM_NEW( NULL, Rect ); + + if( !out ) + return( NULL ); + + *out = *r; + return( out ); +} + +/* Make sure width and height are >0. + */ +void +im_rect_normalise( Rect *r ) +{ + if( r->width < 0 ) { + r->left += r->width; + r->width *= -1; + } + if( r->height < 0 ) { + r->top += r->height; + r->height *= -1; + } +} diff --git a/libsrc/iofuncs/region.c b/libsrc/iofuncs/region.c new file mode 100644 index 00000000..0b523590 --- /dev/null +++ b/libsrc/iofuncs/region.c @@ -0,0 +1,603 @@ +/* Make and destroy partial image regions. + * + * J.Cupitt, 8/4/93. + * 1/7/93 JC + * - adapted for partial v2 + * - ANSIfied + * 15/8/94 JC + * - start & stop can now be NULL for no-op + * 12/5/94 JC + * - threads v2.0 added + * 22/2/95 JC + * - im_region_region() args changed + * 22/6/95 JC + * - im_region_local() did not always reset the data pointer + * 18/11/98 JC + * - init a, b, c also now, to help rtc avoid spurious checks + * 29/6/01 JC + * - im_region_free() now frees immediately + * 6/8/02 JC + * - new mmap() window regions + * 5/11/02 JC + * - fix for mmap a local region + * 28/2/05 + * - shrink local region memory if required much-greater-than allocated + * 3/6/05 + * - im_region_region() allows Bands and BandFmt to differ, provided + * sizeof( pel ) is the same ... makes im_copy_morph() work + * 30/10/06 + * - switch to im_window_t for mmap window stuff + * 29/11/06 + * - switch to im_buffer_t for local mem buffer stuff + * 19/1/07 + * - im_region_image() only sets r, not whole image + * 1'2'07 + * - gah, im_region_image() could still break (thanks Mikkel) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_MOVE +#define DEBUG_ENVIRONMENT 1 +#define DEBUG_CREATE +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include + +#include +#include +#include + +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef DEBUG +/* Track all regions here for debugging. + */ +static GSList *im__regions_all = NULL; +#endif /*DEBUG*/ + +/* Call a start function if no sequence is running on this REGION. + */ +int +im__call_start( REGION *reg ) +{ + IMAGE *im = reg->im; + + /* Have we a sequence running on this region? Start one if not. + */ + if( !reg->seq && im->start ) { + g_mutex_lock( im->sslock ); + reg->seq = im->start( im, im->client1, im->client2 ); + g_mutex_unlock( im->sslock ); + + if( !reg->seq ) { + im_error( "im__call_start", + _( "start function failed for image %s" ), + im->filename ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Call a stop function if a sequence is running in this REGION. No error + * return, really. + */ +void +im__call_stop( REGION *reg ) +{ + IMAGE *im = reg->im; + int res; + + /* Stop any running sequence. + */ + if( reg->seq && im->stop ) { + g_mutex_lock( im->sslock ); + res = im->stop( reg->seq, im->client1, im->client2 ); + g_mutex_unlock( im->sslock ); + + if( res ) + error_exit( "panic: user stop callback failed " + "for image %s", im->filename ); + + reg->seq = NULL; + } +} + +/* If a region is being created in one thread (eg. the main thread) and then + * used in another (eg. a worker thread), the new thread needs to tell VIPS + * to stop sanity assert() fails. The previous owner needs to + * im__region_no_ownership() before we can call this. + */ +void +im__region_take_ownership( REGION *reg ) +{ + /* Lock so that there's a memory barrier with the thread doing the + * im__region_no_ownership() before us. + */ + g_mutex_lock( reg->im->sslock ); + + assert( reg->thread == NULL ); + + /* We don't want to move shared buffers: the other region using this + * buffer will still be on the other thread. Not sure if this will + * ever happen: if it does, we'll need to dup the buffer. + */ + assert( !reg->buffer || reg->buffer->ref_count == 1 ); + + reg->thread = g_thread_self(); + + g_mutex_unlock( reg->im->sslock ); +} + +void +im__region_check_ownership( REGION *reg ) +{ + if( reg->thread ) { + assert( reg->thread == g_thread_self() ); + if( reg->buffer && reg->buffer->cache ) + assert( reg->thread == reg->buffer->cache->thread ); + } +} + +/* Call this from the relinquishing thread. Removes the buffer (if any) from + * this thread's buffer cache. + */ +void +im__region_no_ownership( REGION *reg ) +{ + g_mutex_lock( reg->im->sslock ); + + im__region_check_ownership( reg ); + + reg->thread = NULL; + if( reg->buffer ) + im_buffer_undone( reg->buffer ); + + g_mutex_unlock( reg->im->sslock ); +} + +/* Create a region. Set no attachments. Either im_prepare() or im_generate() + * are responsible for getting regions ready for user functions to read + * from/write to. + */ +REGION * +im_region_create( IMAGE *im ) +{ + REGION *reg; + + im_image_sanity( im ); + + if( !(reg = IM_NEW( NULL, REGION )) ) + return( NULL ); + + reg->im = im; + reg->valid.left = 0; + reg->valid.top = 0; + reg->valid.width = 0; + reg->valid.height = 0; + reg->type = IM_REGION_NONE; + reg->data = NULL; + reg->bpl = 0; + reg->seq = NULL; + reg->thread = NULL; + reg->window = NULL; + reg->buffer = NULL; + + im__region_take_ownership( reg ); + + /* We're usually inside the ss lock anyway. But be safe ... + */ + g_mutex_lock( im->sslock ); + im->regions = g_slist_prepend( im->regions, reg ); + g_mutex_unlock( im->sslock ); + +#ifdef DEBUG + g_mutex_lock( im__global_lock ); + im__regions_all = g_slist_prepend( im__regions_all, reg ); + printf( "%d regions in vips\n", g_slist_length( im__regions_all ) ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG*/ + + return( reg ); +} + +/* Free any resources we have. + */ +static void +im_region_reset( REGION *reg ) +{ + IM_FREEF( im_window_unref, reg->window ); + IM_FREEF( im_buffer_unref, reg->buffer ); +} + +void +im_region_free( REGION *reg ) +{ + IMAGE *im; + + if( !reg ) + return; + im = reg->im; + + /* Stop this sequence. + */ + im__call_stop( reg ); + + /* Free any attached memory. + */ + im_region_reset( reg ); + + /* Detach from image. + */ + g_mutex_lock( im->sslock ); + im->regions = g_slist_remove( im->regions, reg ); + g_mutex_unlock( im->sslock ); + reg->im = NULL; + + /* Was this the last region on an image with close_pending? If yes, + * close the image too. + */ + if( !im->regions && im->close_pending ) { +#ifdef DEBUG_IO + printf( "im_region_free: closing pending image \"%s\"\n", + im->filename ); +#endif /*DEBUG_IO*/ + /* Time to close the image. + */ + im->close_pending = 0; + im_close( im ); + } + + im_free( reg ); + +#ifdef DEBUG + g_mutex_lock( im__global_lock ); + assert( g_slist_find( im__regions_all, reg ) ); + im__regions_all = g_slist_remove( im__regions_all, reg ); + printf( "%d regions in vips\n", g_slist_length( im__regions_all ) ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG*/ +} + +/* Region should be a pixel buffer. On return, check + * reg->buffer->done to see if there are pixels there already. Otherwise, you + * need to calculate. + */ +int +im_region_buffer( REGION *reg, Rect *r ) +{ + IMAGE *im = reg->im; + + Rect image; + Rect clipped; + + im__region_check_ownership( reg ); + + /* Clip against image. + */ + image.top = 0; + image.left = 0; + image.width = im->Xsize; + image.height = im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + /* Test for empty. + */ + if( im_rect_isempty( &clipped ) ) { + im_error( "im_region_buffer", _( "valid clipped to nothing" ) ); + return( -1 ); + } + + /* Already have stuff? + */ + if( reg->type == IM_REGION_BUFFER && + im_rect_includesrect( ®->valid, &clipped ) && + reg->buffer && + !reg->buffer->invalid ) + return( 0 ); + + /* Don't call im_region_reset() ... we combine buffer unref and new + * buffer ref in one call to reduce malloc/free cycling. + */ + IM_FREEF( im_window_unref, reg->window ); + if( !(reg->buffer = im_buffer_unref_ref( reg->buffer, im, &clipped )) ) + return( -1 ); + + /* Init new stuff. + */ + reg->valid = reg->buffer->area; + reg->bpl = IM_IMAGE_SIZEOF_PEL( im ) * reg->buffer->area.width; + reg->type = IM_REGION_BUFFER; + reg->data = reg->buffer->buf; + + return( 0 ); +} + +/* Attach a region to a small section of the image on which it is defined. + * The IMAGE we are attached to should be im_mmapin(), im_mmapinrw() or + * im_setbuf(). The Rect is clipped against the image size. + */ +int +im_region_image( REGION *reg, Rect *r ) +{ + Rect image; + Rect clipped; + + /* Sanity check. + */ + im__region_check_ownership( reg ); + + /* Clip against image. + */ + image.top = 0; + image.left = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + /* Test for empty. + */ + if( im_rect_isempty( &clipped ) ) { + im_error( "im_region_image", _( "valid clipped to nothing" ) ); + return( -1 ); + } + + if( reg->im->data ) { + /* We have the whole image available ... easy! + */ + im_region_reset( reg ); + + /* We can't just set valid = clipped, since this may be an + * incompletely calculated memory buffer. Just set valid to r. + */ + reg->valid = clipped; + reg->bpl = IM_IMAGE_SIZEOF_LINE( reg->im ); + reg->data = reg->im->data + + (gint64) clipped.top * IM_IMAGE_SIZEOF_LINE( reg->im ) + + clipped.left * IM_IMAGE_SIZEOF_PEL( reg->im ); + reg->type = IM_REGION_OTHER_IMAGE; + } + else if( reg->im->dtype == IM_OPENIN ) { + /* No complete image data ... but we can use a rolling window. + */ + if( reg->type != IM_REGION_WINDOW || !reg->window || + reg->window->top > clipped.top || + reg->window->top + reg->window->height < + clipped.top + clipped.height ) { + im_region_reset( reg ); + + if( !(reg->window = im_window_ref( reg->im, + clipped.top, clipped.height )) ) + return( -1 ); + + reg->type = IM_REGION_WINDOW; + } + + /* Note the area the window actually represents. + */ + reg->valid.left = 0; + reg->valid.top = reg->window->top; + reg->valid.width = reg->im->Xsize; + reg->valid.height = reg->window->height; + reg->bpl = IM_IMAGE_SIZEOF_LINE( reg->im ); + reg->data = reg->window->data; + } + else { + im_error( "im_region_image", _( "bad image type" ) ); + return( -1 ); + } + + return( 0 ); +} + +/* Make IM_REGION_ADDR() stuff to reg go to dest instead. + * + * r is the part of the reg image which you want to be able to write to (this + * effectively becomes the valid field), (x,y) is the top LH corner of the + * corresponding area in dest. + * + * Performs all clippings necessary to ensure that ®->valid is indeed + * valid. + * + * If the region we attach to is modified, we are left with dangling pointers! + * If the region we attach to is on another image, the two images must have + * the same sizeof( pel ). + */ +int +im_region_region( REGION *reg, REGION *dest, Rect *r, int x, int y ) +{ + Rect image; + Rect wanted; + Rect clipped; + Rect clipped2; + Rect final; + + /* Sanity check. + */ + if( !dest->data || + IM_IMAGE_SIZEOF_PEL( dest->im ) != + IM_IMAGE_SIZEOF_PEL( reg->im ) ) { + im_error( "im_region_region", + _( "inappropriate region type" ) ); + return( -1 ); + } + im__region_check_ownership( reg ); + + /* We can't test + + assert( dest->thread == g_thread_self() ); + + * since we can have several threads writing to the same region in + * threadgroup. + */ + + /* Clip r against size of the image. + */ + image.top = 0; + image.left = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + im_rect_intersectrect( r, &image, &clipped ); + + /* Translate to dest's coordinate space and clip against the available + * pixels. + */ + wanted.left = x + (clipped.left - r->left); + wanted.top = y + (clipped.top - r->top); + wanted.width = clipped.width; + wanted.height = clipped.height; + + /* Test that dest->valid is large enough. + */ + if( !im_rect_includesrect( &dest->valid, &wanted ) ) { + im_error( "im_region_region", _( "dest too small" ) ); + return( -1 ); + } + + /* Clip against the available pixels. + */ + im_rect_intersectrect( &wanted, &dest->valid, &clipped2 ); + + /* Translate back to reg's coordinate space and set as valid. + */ + final.left = r->left + (clipped2.left - wanted.left); + final.top = r->top + (clipped2.top - wanted.top); + final.width = clipped2.width; + final.height = clipped2.height; + + /* Test for empty. + */ + if( im_rect_isempty( &final ) ) { + im_error( "im_region_region", _( "valid clipped to nothing" ) ); + return( -1 ); + } + + /* Init new stuff. + */ + im_region_reset( reg ); + reg->valid = final; + reg->bpl = dest->bpl; + reg->data = IM_REGION_ADDR( dest, clipped2.left, clipped2.top ); + reg->type = IM_REGION_OTHER_REGION; + + return( 0 ); +} + +/* Do two regions point to the same piece of image? ie. + * IM_REGION_ADDR( reg1, x, y ) == IM_REGION_ADDR( reg2, x, y ) && + * *IM_REGION_ADDR( reg1, x, y ) == + * *IM_REGION_ADDR( reg2, x, y ) for all x, y, reg1, reg2. + */ +int +im_region_equalsregion( REGION *reg1, REGION *reg2 ) +{ + return( reg1->im == reg2->im && + im_rect_equalsrect( ®1->valid, ®2->valid ) && + reg1->data == reg2->data ); +} + +/* Set the position of a region. This only affects reg->valid, ie. the way + * pixels are addressed, not reg->data, the pixels which are addressed. Clip + * against the size of the image. Do not allow negative positions, or + * positions outside the image. + */ +int +im_region_position( REGION *reg, int x, int y ) +{ + Rect req, image, clipped; + + /* Clip! + */ + image.top = 0; + image.left = 0; + image.width = reg->im->Xsize; + image.height = reg->im->Ysize; + req.top = y; + req.left = x; + req.width = reg->valid.width; + req.height = reg->valid.height; + im_rect_intersectrect( &image, &req, &clipped ); + if( x < 0 || y < 0 || im_rect_isempty( &clipped ) ) { + im_error( "im_region_position", _( "bad position" ) ); + return( -1 ); + } + + reg->valid = clipped; + + return( 0 ); +} + +int +im_region_fill( REGION *reg, Rect *r, im_region_fill_fn fn, void *a ) +{ + assert( reg->im->dtype == IM_PARTIAL ); + assert( reg->im->generate ); + + /* Should have local memory. + */ + if( im_region_buffer( reg, r ) ) + return( -1 ); + + /* Evaluate into or, if we've not got calculated pixels. + */ + if( !reg->buffer->done ) { + if( fn( reg, a ) ) + return( -1 ); + + /* Publish our results. + */ + if( reg->buffer ) + im_buffer_done( reg->buffer ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/semaphore.c b/libsrc/iofuncs/semaphore.c new file mode 100644 index 00000000..69219d6e --- /dev/null +++ b/libsrc/iofuncs/semaphore.c @@ -0,0 +1,146 @@ +/* Support for thread stuff. + * + * JC & KM 9/5/94 + * Modified: + * 28/11/94 JC + * - return(0) missing from tidy_thread_info() + * 4/8/99 RP JC + * - reorganised for POSIX + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +void +im_semaphore_init( im_semaphore_t *s, int v, char *name ) +{ + s->v = v; + s->name = name; +#ifdef HAVE_THREADS + s->mutex = g_mutex_new(); + s->cond = g_cond_new(); +#endif /*HAVE_THREADS*/ +} + +void +im_semaphore_destroy( im_semaphore_t *s ) +{ +#ifdef HAVE_THREADS + IM_FREEF( g_mutex_free, s->mutex ); + IM_FREEF( g_cond_free, s->cond ); +#endif /*HAVE_THREADS*/ +} + +/* Add n to the semaphore and signal any threads that are blocked waiting + * a change. + */ +int +im_semaphore_upn( im_semaphore_t *s, int n ) +{ + int value_after_op; + +#ifdef HAVE_THREADS + g_mutex_lock( s->mutex ); +#endif /*HAVE_THREADS*/ + s->v += n; + value_after_op = s->v; +#ifdef HAVE_THREADS + g_mutex_unlock( s->mutex ); + g_cond_signal( s->cond ); +#endif /*HAVE_THREADS*/ + +#ifdef DEBUG_IO + printf( "im_semaphore_upn(\"%s\",%d) = %d\n", + s->name, n, value_after_op ); + if( value_after_op > 1 ) + im_errormsg( "up over 1!" ); +#endif /*DEBUG_IO*/ + + return( value_after_op ); +} + +/* Increment the semaphore. + */ +int +im_semaphore_up( im_semaphore_t *s ) +{ + return( im_semaphore_upn( s, 1 ) ); +} + +/* Wait for sem>n, then subtract n. + */ +int +im_semaphore_downn( im_semaphore_t *s, int n ) +{ + int value_after_op; + +#ifdef HAVE_THREADS + g_mutex_lock( s->mutex ); + while( s->v < n ) + g_cond_wait( s->cond, s->mutex ); +#endif /*HAVE_THREADS*/ + s->v -= n; + value_after_op = s->v; +#ifdef HAVE_THREADS + g_mutex_unlock( s->mutex ); +#endif /*HAVE_THREADS*/ + +#ifdef DEBUG_IO + printf( "im_semaphore_downn(\"%s\",%d): %d\n", + s->name, n, value_after_op ); +#endif /*DEBUG_IO*/ + + return( value_after_op ); +} + +/* Wait for sem>0, then decrement. + */ +int +im_semaphore_down( im_semaphore_t *s ) +{ + return( im_semaphore_downn( s, 1 ) ); +} diff --git a/libsrc/iofuncs/threadgroup.c b/libsrc/iofuncs/threadgroup.c new file mode 100644 index 00000000..ca103b00 --- /dev/null +++ b/libsrc/iofuncs/threadgroup.c @@ -0,0 +1,678 @@ +/* Support for thread groups. + * + * 29/9/99 JC + * - from thread.c + * 23/10/03 JC + * - threadgroup now has a kill flag as well + * 9/6/06 + * - use an idle queue instead of flags + * 24/11/06 + * - double-buffer file writes + * 27/11/06 + * - break stuff out to generate / iterate + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define TIME_THREAD +#define DEBUG_CREATE +#define DEBUG_HIGHWATER +#define DEBUG_IO + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef TIME_THREAD +/* Size of time buffers. + */ +#define IM_TBUF_SIZE (20000) +#endif /*TIME_THREAD*/ + +/* Maximum number of concurrent threads we allow. No reason for the limit, + * it's just there to stop mad values for IM_CONCURRENCY killing the system. + */ +#define IM_MAX_THREADS (1024) + +/* Name of environment variable we get concurrency level from. + */ +#define IM_CONCURRENCY "IM_CONCURRENCY" + +/* Default tile geometry ... can be set by init_world. + */ +int im__tile_width = IM__TILE_WIDTH; +int im__tile_height = IM__TILE_HEIGHT; +int im__fatstrip_height = IM__FATSTRIP_HEIGHT; +int im__thinstrip_height = IM__THINSTRIP_HEIGHT; + +/* Default n threads ... 0 means get from environment. + */ +int im__concurrency = 0; + +#ifndef HAVE_THREADS +/* If we're building without gthread, we need stubs for the g_thread_*() and + * g_mutex_*() functions. has #defines which point the g_ + * names here. + */ + +void im__g_thread_init( GThreadFunctions *vtable ) {} +gpointer im__g_thread_join( GThread *dummy ) { return( NULL ); } +gpointer im__g_thread_self( void ) { return( NULL ); } +GThread *im__g_thread_create_full( GThreadFunc d1, + gpointer d2, gulong d3, gboolean d4, gboolean d5, GThreadPriority d6, + GError **d7 ) + { return( NULL ); } + +GMutex *im__g_mutex_new( void ) { return( NULL ); } +void im__g_mutex_free( GMutex *d ) {} +void im__g_mutex_lock( GMutex *d ) {} +void im__g_mutex_unlock( GMutex *d ) {} +#endif /*!HAVE_THREADS*/ + +void +im_concurrency_set( int concurrency ) +{ + im__concurrency = concurrency; +} + +/* Set (p)thr_concurrency() from IM_CONCURRENCY environment variable. Return + * the number of regions we should pass over the image. + */ +int +im_concurrency_get( void ) +{ + const char *str; + int nthr; + + /* Tell the threads system how much concurrency we expect. + */ + if( im__concurrency > 0 ) + nthr = im__concurrency; + else if( (str = g_getenv( IM_CONCURRENCY )) ) + nthr = atoi( str ); + else + /* Stick to minimum. + */ + nthr = 1; + + if( nthr < 1 || nthr > IM_MAX_THREADS ) { + nthr = IM_CLIP( 1, nthr, IM_MAX_THREADS ); + + im_warn( "im_concurrency_get", + _( "threads clipped to %d" ), nthr ); + } + + /* + + FIXME .. hmm + +#ifdef SOLARIS_THREADS + if( thr_setconcurrency( nthr + 1 ) ) { + im_error( "im_concurrency_get", _( "unable to set " + "concurrency level to %d" ), nthr + 1 ); + return( -1 ); + } +#ifdef DEBUG_IO + printf( "im_generate: using thr_setconcurrency(%d)\n", nthr+1 ); +#endif +#endif + +#ifdef HAVE_PTHREAD +#ifdef HAVE_PTHREAD_SETCONCURRENCY + if( pthread_setconcurrency( nthr + 1 ) ) { + im_error( "im_concurrency_get", _( "unable to set " + "concurrency level to %d" ), nthr + 1 ); + return( -1 ); + } +#ifdef DEBUG_IO + printf( "im_generate: using pthread_setconcurrency(%d)\n", nthr+1 ); +#endif +#endif +#endif + */ + + return( nthr ); +} + +#ifdef TIME_THREAD +/* Save time buffers. + */ +static int +save_time_buffers( REGION *reg ) +{ + int i; + static int rn = 1; + FILE *fp; + char name[ 256 ]; + + im_snprintf( name, 256, "time%d", rn++ ); + if( !(fp = fopen( name, "w" )) ) + error_exit( "unable to write to \"%s\"", name ); + for( i = 0; i < reg->tpos; i++ ) + fprintf( fp, "%lld\n%lld\n", reg->btime[i], reg->etime[i] ); + fclose( fp ); + + return( 0 ); +} +#endif /*TIME_THREAD*/ + +/* Handle the idle list. + */ + +/* Make sure thr is not on the idle list ... eg. on thread_free(). + */ +static void +threadgroup_idle_remove( im_thread_t *thr ) +{ + im_threadgroup_t *tg = thr->tg; + + g_mutex_lock( tg->idle_lock ); + + tg->idle = g_slist_remove( tg->idle, thr ); + +#ifdef DEBUG_HIGHWATER + tg->nidle -= 1; + assert( tg->nidle >= 0 && tg->nidle <= tg->nthr ); +#endif /*DEBUG_HIGHWATER*/ + + g_mutex_unlock( tg->idle_lock ); +} + +/* Add to idle. + */ +static void +threadgroup_idle_add( im_thread_t *thr ) +{ + im_threadgroup_t *tg = thr->tg; + + g_mutex_lock( tg->idle_lock ); + + assert( !g_slist_find( tg->idle, thr ) ); + tg->idle = g_slist_prepend( tg->idle, thr ); + +#ifdef DEBUG_HIGHWATER + tg->nidle += 1; + assert( tg->nidle >= 0 && tg->nidle <= tg->nthr ); +#endif /*DEBUG_HIGHWATER*/ + + im_semaphore_up( &tg->idle_sem ); + + g_mutex_unlock( tg->idle_lock ); +} + +/* Take the first thread off idle. + */ +im_thread_t * +im_threadgroup_get( im_threadgroup_t *tg ) +{ + im_thread_t *thr; + + /* Wait for a thread to be added to idle. + */ + im_semaphore_down( &tg->idle_sem ); + + g_mutex_lock( tg->idle_lock ); + + assert( tg->idle ); + assert( tg->idle->data ); + thr = (im_thread_t *) tg->idle->data; + tg->idle = g_slist_remove( tg->idle, thr ); + +#ifdef DEBUG_HIGHWATER + tg->nidle -= 1; + assert( tg->nidle >= 0 && tg->nidle <= tg->nthr ); + if( tg->nidle < tg->min_idle ) + tg->min_idle = tg->nidle; +#endif /*DEBUG_HIGHWATER*/ + + g_mutex_unlock( tg->idle_lock ); + + return( thr ); +} + +/* Wait for all threads to be idle. + */ +void +im_threadgroup_wait( im_threadgroup_t *tg ) +{ + /* Wait for all threads to signal idle. + */ + im_semaphore_downn( &tg->idle_sem, tg->nthr ); + + g_mutex_lock( tg->idle_lock ); + + assert( tg->idle ); + assert( g_slist_length( tg->idle ) == tg->nthr ); + + g_mutex_unlock( tg->idle_lock ); + + /* All threads are now blocked on their 'go' semaphores, and idle is + * zero. Up idle by the number of threads, ready for the next loop. + */ + im_semaphore_upn( &tg->idle_sem, tg->nthr ); +} + +/* Junk a thread. + */ +static void +thread_free( im_thread_t *thr ) +{ + /* Is there a thread running this region? Kill it! + */ + if( thr->thread ) { + thr->kill = -1; + im_semaphore_up( &thr->go ); + + /* Return value is always NULL (see thread_main_loop). + */ + (void) g_thread_join( thr->thread ); +#ifdef DEBUG_CREATE + printf( "thread_free: g_thread_join()\n" ); +#endif /*DEBUG_CREATE*/ + + thr->thread = NULL; + } + im_semaphore_destroy( &thr->go ); + + threadgroup_idle_remove( thr ); + IM_FREEF( im_region_free, thr->reg ); + thr->oreg = NULL; + thr->tg = NULL; + +#ifdef TIME_THREAD + if( thr->btime ) + (void) save_time_buffers( thr ); +#endif /*TIME_THREAD*/ +} + +/* The work we do in one loop ... fill a region, and call a function. Either + * called from the main thread (if no threading), or from worker. + */ +static void +work_fn( im_thread_t *thr ) +{ + /* Doublecheck only one thread per region. + */ + assert( thr->thread == g_thread_self() ); + + /* Prepare this area. + */ + if( thr->tg->inplace ) { + if( im_prepare_to( thr->reg, thr->oreg, + &thr->pos, thr->x, thr->y ) ) + thr->error = -1; + } + else { + /* Attach to this position. + */ + if( im_prepare( thr->reg, &thr->pos ) ) + thr->error = -1; + } + + /* Call our work function. + */ + if( !thr->error && thr->tg->work && + thr->tg->work( thr->reg, thr->a, thr->b, thr->c ) ) + thr->error = -1; + + /* Back on the idle queue again. + */ + threadgroup_idle_add( thr ); +} + +#ifdef HAVE_THREADS +/* What runs as a thread ... loop, waiting to be told to fill our region. + */ +static void * +thread_main_loop( void *a ) +{ + im_thread_t *thr = (im_thread_t *) a; + im_threadgroup_t *tg = thr->tg; + + /* We now control the region (it was created by tg when we were + * built). + */ + im__region_take_ownership( thr->reg ); + + for(;;) { + assert( tg == thr->tg ); + + /* Signal the main thread that we are idle, and block. + */ + im_semaphore_down( &thr->go ); + + /* Asked to exit? + */ + if( thr->kill ) + break; + +#ifdef TIME_THREAD + /* Note start time. + */ + if( thr->btime ) + thr->btime[thr->tpos] = gethrtime(); +#endif /*TIME_THREAD*/ + + /* Loop once. + */ + work_fn( thr ); + +#ifdef TIME_THREAD + /* Note stop time. + */ + if( thr->etime ) { + thr->etime[thr->tpos] = gethrtime(); + thr->tpos++; + } +#endif /*TIME_THREAD*/ + } + + return( NULL ); +} +#endif /*HAVE_THREADS*/ + +/* Attach another thread to a threadgroup. + */ +static im_thread_t * +threadgroup_thread_new( im_threadgroup_t *tg ) +{ + im_thread_t *thr; + + if( !(thr = IM_NEW( tg->im, im_thread_t )) ) + return( NULL ); + thr->tg = tg; + thr->reg = NULL; + thr->thread = NULL; + im_semaphore_init( &thr->go, 0, "go" ); + thr->kill = 0; + thr->error = 0; + thr->oreg = NULL; + thr->a = thr->b = thr->c = NULL; +#ifdef TIME_THREAD + thr->btime = NULL; + thr->etime = NULL; + thr->tpos = 0; +#endif /*TIME_THREAD*/ + + /* Attach stuff. + */ + if( !(thr->reg = im_region_create( tg->im )) ) { + thread_free( thr ); + return( NULL ); + } + + /* Get ready to hand the region over to the thread. + */ + im__region_no_ownership( thr->reg ); + +#ifdef TIME_THREAD + thr->btime = IM_ARRAY( tg->im, IM_TBUF_SIZE, hrtime_t ); + thr->etime = IM_ARRAY( tg->im, IM_TBUF_SIZE, hrtime_t ); + if( !thr->btime || !thr->etime ) { + thread_free( thr ); + return( NULL ); + } +#endif /*TIME_THREAD*/ + +#ifdef HAVE_THREADS + /* Make a worker thread. We have to use g_thread_create_full() because + * we need to insist on a non-tiny stack. Some platforms default to + * very small values (eg. various BSDs). + */ + if( !(thr->thread = g_thread_create_full( thread_main_loop, thr, + IM__DEFAULT_STACK_SIZE, TRUE, FALSE, + G_THREAD_PRIORITY_NORMAL, NULL )) ) { + im_error( "threadgroup_thread_new", + _( "unable to create thread" ) ); + thread_free( thr ); + return( NULL ); + } + +#ifdef DEBUG_CREATE + printf( "threadgroup_thread_new: g_thread_create_full()\n" ); +#endif /*DEBUG_CREATE*/ +#endif /*HAVE_THREADS*/ + + /* It's idle. + */ + threadgroup_idle_add( thr ); + + return( thr ); +} + +/* Kill all threads in a threadgroup, if there are any. + */ +static void +threadgroup_kill_threads( im_threadgroup_t *tg ) +{ + if( tg->thr ) { + int i; + + for( i = 0; i < tg->nthr; i++ ) + thread_free( tg->thr[i] ); + tg->thr = NULL; + + assert( !tg->idle ); + + /* Reset the idle semaphore. + */ + im_semaphore_destroy( &tg->idle_sem ); + im_semaphore_init( &tg->idle_sem, 0, "idle" ); + +#ifdef DEBUG_IO + printf( "threadgroup_kill_threads: killed %d threads\n", + tg->nthr ); +#endif /*DEBUG_IO*/ + } +} + +/* Free a threadgroup. Can be called multiple times. + */ +int +im_threadgroup_free( im_threadgroup_t *tg ) +{ +#ifdef DEBUG_IO + printf( "im_threadgroup_free: \"%s\" (%p)\n", tg->im->filename, tg ); +#endif /*DEBUG_IO*/ + + if( !tg || tg->zombie ) + return( 0 ); + + threadgroup_kill_threads( tg ); + + im_semaphore_destroy( &tg->idle_sem ); + IM_FREEF( g_mutex_free, tg->idle_lock ); + tg->zombie = -1; + +#ifdef DEBUG_HIGHWATER + printf( "im_threadgroup_free %p: max busy workers = %d\n", + tg, tg->nthr - tg->min_idle ); +#endif /*DEBUG_HIGHWATER*/ + + return( 0 ); +} + +/* Attach a threadgroup to an image. + */ +im_threadgroup_t * +im_threadgroup_create( IMAGE *im ) +{ + im_threadgroup_t *tg; + int i; + + /* Allocate and init new thread block. + */ + if( !(tg = IM_NEW( im, im_threadgroup_t )) ) + return( NULL ); + tg->zombie = 0; + tg->im = im; + tg->work = NULL; + tg->inplace = 0; + if( (tg->nthr = im_concurrency_get()) < 0 ) + return( NULL ); + tg->thr = NULL; + tg->kill = 0; + + /* Pick a render geometry. + */ + switch( tg->im->dhint ) { + case IM_SMALLTILE: + tg->pw = im__tile_width; + tg->ph = im__tile_height; + tg->nlines = tg->ph; + break; + + case IM_FATSTRIP: + tg->pw = tg->im->Xsize; + tg->ph = im__fatstrip_height; + tg->nlines = tg->ph * tg->nthr * 2; + break; + + case IM_ANY: + case IM_THINSTRIP: + tg->pw = tg->im->Xsize; + tg->ph = im__thinstrip_height; + tg->nlines = tg->ph * tg->nthr * 2; + break; + + default: + error_exit( "panic: internal error #98i245983425" ); + } + + /* Attach tidy-up callback. + */ + if( im_add_close_callback( im, + (im_callback_fn) im_threadgroup_free, tg, NULL ) ) { + (void) im_threadgroup_free( tg ); + return( NULL ); + } + +#ifdef DEBUG_IO + printf( "im_threadgroup_create: %d by %d patches, " + "groups of %d scanlines\n", tg->pw, tg->ph, tg->nlines ); +#endif /*DEBUG_IO*/ + + /* Init locks. + */ + im_semaphore_init( &tg->idle_sem, 0, "idle" ); + tg->idle = NULL; + tg->idle_lock = g_mutex_new(); + +#ifdef DEBUG_HIGHWATER + tg->nidle = 0; + tg->min_idle = tg->nthr; +#endif /*DEBUG_HIGHWATER*/ + + /* Make thread array. + */ + if( !(tg->thr = IM_ARRAY( im, tg->nthr + 1, im_thread_t * )) ) + return( NULL ); + for( i = 0; i < tg->nthr + 1; i++ ) + tg->thr[i] = NULL; + + /* Attach threads. + */ + for( i = 0; i < tg->nthr; i++ ) + if( !(tg->thr[i] = threadgroup_thread_new( tg )) ) + return( NULL ); + +#ifdef DEBUG_IO + printf( "im_threadgroup_create: \"%s\" (%p), with %d threads\n", + im->filename, tg, tg->nthr ); +#endif /*DEBUG_IO*/ + + return( tg ); +} + +/* Trigger work. If not threading, just call fn directly. + */ +void +im_threadgroup_trigger( im_thread_t *thr ) +{ + im_threadgroup_t *tg = thr->tg; + + /* thr pos needs to be set before coming here ... check. + */ +{ + Rect image; + + image.left = 0; + image.top = 0; + image.width = tg->im->Xsize; + image.height = tg->im->Ysize; + + assert( im_rect_includesrect( &image, &thr->pos ) ); +} + + /* Start worker going. + */ + im_semaphore_up( &thr->go ); + +#ifndef HAVE_THREADS + /* No threading ... just eval directly. + */ + work_fn( thr ); +#endif /*HAVE_THREADS*/ +} + +/* Test all threads for error. + */ +int +im_threadgroup_iserror( im_threadgroup_t *tg ) +{ + int i; + + if( tg->kill ) + return( -1 ); + if( tg->im->kill ) + return( -1 ); + + for( i = 0; i < tg->nthr; i++ ) + if( tg->thr[i]->error ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/iofuncs/time.c b/libsrc/iofuncs/time.c new file mode 100644 index 00000000..3fc88c7b --- /dev/null +++ b/libsrc/iofuncs/time.c @@ -0,0 +1,112 @@ +/* Time execution of pipelines. + * + * 20/7/93 JC + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Allocate a new time struct and fill in start values. + */ +static int +new_time( IMAGE *im ) +{ + struct time_info *tim = IM_NEW( im, struct time_info ); + + if( !tim ) + return( -1 ); + + if( im->time ) { + im_errormsg( "new_time: sanity failure" ); + return( -1 ); + } + + tim->im = im; + tim->start = time( NULL ); + tim->run = 0; + tim->eta = 0; + tim->tpels = (gint64) im->Xsize * im->Ysize; + tim->npels = 0; + tim->percent = 0; + im->time = tim; + + return( 0 ); +} + +/* A new tile has been computed. Update time_info. + */ +static int +update_time( struct time_info *tim, int w, int h ) +{ + float prop; + + tim->run = time( NULL ) - tim->start; + tim->npels += w * h; + prop = (float) tim->npels / (float) tim->tpels; + tim->percent = 100 * prop; + if( prop > 0 ) + tim->eta = (1.0 / prop) * tim->run - tim->run; + + return( 0 ); +} + +/* Handle eval callbacks. w and h are the size of the tile we made this time. + */ +int +im__handle_eval( IMAGE *im, int w, int h ) +{ + if( im->evalfns ) { + if( !im->time ) + if( new_time( im ) ) + return( -1 ); + if( update_time( im->time, w, h ) ) + return( -1 ); + + if( im__trigger_callbacks( im->evalfns ) ) + return( -1 ); + } + + return( 0 ); +} diff --git a/libsrc/iofuncs/util.c b/libsrc/iofuncs/util.c new file mode 100644 index 00000000..eb0872eb --- /dev/null +++ b/libsrc/iofuncs/util.c @@ -0,0 +1,915 @@ +/* Some basic util functions. + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Temp buffer for snprintf() layer on old systems. + */ +#define MAX_BUF (32768) + +/* Test two lists for eqality. + */ +gboolean +im_slist_equal( GSList *l1, GSList *l2 ) +{ + while( l1 && l2 ) { + if( l1->data != l2->data ) + return( FALSE ); + + l1 = l1->next; + l2 = l2->next; + } + + if( l1 || l2 ) + return( FALSE ); + + return( TRUE ); +} + +/* Map over an slist. _copy() the list in case the callback changes it. + */ +void * +im_slist_map2( GSList *list, VSListMap2Fn fn, void *a, void *b ) +{ + GSList *copy; + GSList *i; + void *result; + + copy = g_slist_copy( list ); + result = NULL; + for( i = copy; i && !(result = fn( i->data, a, b )); i = i->next ) + ; + g_slist_free( copy ); + + return( result ); +} + +void * +im_slist_map4( GSList *list, + VSListMap4Fn fn, void *a, void *b, void *c, void *d ) +{ + GSList *copy; + GSList *i; + void *result; + + copy = g_slist_copy( list ); + result = NULL; + for( i = copy; + i && !(result = fn( i->data, a, b, c, d )); i = i->next ) + ; + g_slist_free( copy ); + + return( result ); +} + +/* Map backwards. We _reverse() rather than recurse and unwind to save stack. + */ +void * +im_slist_map2_rev( GSList *list, VSListMap2Fn fn, void *a, void *b ) +{ + GSList *copy; + GSList *i; + void *result; + + copy = g_slist_copy( list ); + copy = g_slist_reverse( copy ); + result = NULL; + for( i = copy; i && !(result = fn( i->data, a, b )); i = i->next ) + ; + g_slist_free( copy ); + + return( result ); +} + +void * +im_map_equal( void *a, void *b ) +{ + if( a == b ) + return( a ); + + return( NULL ); +} + +void * +im_slist_fold2( GSList *list, void *start, VSListFold2Fn fn, void *a, void *b ) +{ + void *c; + GSList *this, *next; + + for( c = start, this = list; this; this = next ) { + next = this->next; + + if( !(c = fn( this->data, c, a, b )) ) + return( NULL ); + } + + return( c ); +} + +static void +im_slist_free_all_cb( void * thing, void * dummy ) +{ + im_free( thing ); +} + +/* Free a g_slist of things which need im_free()ing. + */ +void +im_slist_free_all( GSList *list ) +{ + g_slist_foreach( list, im_slist_free_all_cb, NULL ); + g_slist_free( list ); +} + +/* Remove all occurences of an item from a list. + */ +GSList * +im_slist_filter( GSList *list, VSListMap2Fn fn, void *a, void *b ) +{ + GSList *tmp; + GSList *prev; + + prev = NULL; + tmp = list; + + while( tmp ) { + if( fn( tmp->data, a, b ) ) { + GSList *next = tmp->next; + + if( prev ) + prev->next = next; + if( list == tmp ) + list = next; + + tmp->next = NULL; + g_slist_free( tmp ); + tmp = next; + } + else { + prev = tmp; + tmp = tmp->next; + } + } + + return( list ); +} + +/* Like strncpy(), but always NULL-terminate, and don't pad with NULLs. + */ +char * +im_strncpy( char *dest, const char *src, int n ) +{ + int i; + + assert( n > 0 ); + + for( i = 0; i < n - 1; i++ ) + if( !(dest[i] = src[i]) ) + break; + dest[i] = '\0'; + + return( dest ); +} + +/* Find the rightmost occurrence of needle in haystack. + */ +char * +im_strrstr( const char *haystack, const char *needle ) +{ + int haystack_len = strlen( haystack ); + int needle_len = strlen( needle ); + int i; + + for( i = haystack_len - needle_len; i >= 0; i-- ) + if( strncmp( needle, haystack + i, needle_len ) == 0 ) + return( (char *) haystack + i ); + + return( NULL ); +} + +/* strdup local to a descriptor. + */ +char * +im_strdup( IMAGE *im, const char *str ) +{ + int l = strlen( str ); + char *buf; + + if( !(buf = (char *) im_malloc( im, l + 1 )) ) + return( NULL ); + strcpy( buf, str ); + + return( buf ); +} + +/* Test for string b ends string a. + */ +gboolean +im_ispostfix( const char *a, const char *b ) +{ + int m = strlen( a ); + int n = strlen( b ); + + if( n > m ) + return( FALSE ); + + return( strcmp( a + m - n, b ) == 0 ); +} + +/* Test for string a starts string b. + */ +gboolean +im_isprefix( const char *a, const char *b ) +{ + int n = strlen( a ); + int m = strlen( b ); + int i; + + if( m < n ) + return( FALSE ); + for( i = 0; i < n; i++ ) + if( a[i] != b[i] ) + return( FALSE ); + + return( TRUE ); +} + +/* Like strtok(). Give a string and a list of break characters. Then: + * - skip initial break characters + * - EOS? return NULL + * - skip a series of non-break characters + * - write a '\0' over the next break character and return a pointer to the + * char after that + * + * The idea is that this can be used in loops as the iterator. Example: + * + * char *p = " 1 2 3 "; // mutable + * char *q; + * int i; + * int v[...]; + * + * for( i = 0; (q = im_break_token( p, " " )); i++, p = q ) + * v[i] = atoi( p ); + * + * will set + * v[0] = 1; + * v[1] = 2; + * v[2] = 3; + * + * or with just one pointer, provided your atoi() is OK with trailing chars + * and you know there is at least one item there + * + * char *p = " 1 2 3 "; // mutable + * int i; + * int v[...]; + * + * for( i = 0; p; p = im_break_token( p, " " ) ) + * v[i] = atoi( p ); + */ +char * +im_break_token( char *str, const char *brk ) +{ + char *p; + + /* Is the string empty? If yes, return NULL immediately. + */ + if( !str || !*str ) + return( NULL ); + + /* Skip initial break characters. + */ + p = str + strspn( str, brk ); + + /* No item? + */ + if( !*p ) + return( NULL ); + + /* We have a token ... search for the first break character after the + * token. + */ + p += strcspn( p, brk ); + + /* Is there string left? + */ + if( *p ) { + /* Write in an end-of-string mark and return the start of the + * next token. + */ + *p++ = '\0'; + p += strspn( p, brk ); + } + + return( p ); +} + +/* Wrapper over (v)snprintf() ... missing on old systems. + */ +int +im_vsnprintf( char *str, size_t size, const char *format, va_list ap ) +{ +#ifdef HAVE_VSNPRINTF + return( vsnprintf( str, size, format, ap ) ); +#else /*HAVE_VSNPRINTF*/ + /* Bleurg! + */ + int n; + static char buf[MAX_BUF]; + + if( size > MAX_BUF ) + error_exit( "panic: buffer overflow " + "(request to write %d bytes to buffer of %d bytes)", + size, MAX_BUF ); + n = vsprintf( buf, format, ap ); + if( n > MAX_BUF ) + error_exit( "panic: buffer overflow " + "(%d bytes written to buffer of %d bytes)", + n, MAX_BUF ); + + im_strncpy( str, buf, size ); + + return( n ); +#endif /*HAVE_VSNPRINTF*/ +} + +int +im_snprintf( char *str, size_t size, const char *format, ... ) +{ + va_list ap; + int n; + + va_start( ap, format ); + n = im_vsnprintf( str, size, format, ap ); + va_end( ap ); + + return( n ); +} + +/* Split filename into name / mode components. name and mode should both be + * FILENAME_MAX chars. + * + * We look for the ':' splitting the name and mode by searching for the + * rightmost occurence of the regexp ".[A-Za-z0-9]+:". Example: consider the + * horror that is + * + * c:\silly:dir:name\fr:ed.tif:jpeg:95,,,,c:\icc\srgb.icc + * + */ +void +im_filename_split( const char *path, char *name, char *mode ) +{ + char *p; + + im_strncpy( name, path, FILENAME_MAX ); + + /* Search back towards start stopping at each ':' char. + */ + for( p = name + strlen( name ) - 1; p > name; p -= 1 ) + if( *p == ':' ) { + char *q; + + for( q = p - 1; isalnum( *q ) && q > name; q -= 1 ) + ; + + if( *q == '.' ) + break; + } + + if( *p == ':' ) { + im_strncpy( mode, p + 1, FILENAME_MAX ); + *p = '\0'; + } + else + strcpy( mode, "" ); +} + +/* Skip any leading path stuff. Horrible: if this is a filename which came + * from win32 and we're a *nix machine, it'll have '\\' not '/' as the + * separator :-( + * + * Try to fudge this ... if the file doesn't contain any of our native + * separators, look for the opposite one as well. If there are none of those + * either, just return the filename. + */ +const char * +im_skip_dir( const char *path ) +{ + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + const char *p; + const char *q; + + const char native_dir_sep = G_DIR_SEPARATOR; + const char non_native_dir_sep = native_dir_sep == '/' ? '\\' : '/'; + + /* Remove any trailing save modifiers: we don't want '/' or '\' in the + * modifier confusing us. + */ + im_filename_split( path, name, mode ); + + /* The '\0' char at the end of the string. + */ + p = name + strlen( name ); + + /* Search back for the first native dir sep, or failing that, the first + * non-native dir sep. + */ + for( q = p; q > name && q[-1] != native_dir_sep; q-- ) + ; + if( q == name ) + for( q = p; q > name && q[-1] != non_native_dir_sep; q-- ) + ; + + return( path + (q - name) ); +} + +/* Extract suffix from filename, ignoring any mode string. Suffix should be + * FILENAME_MAX chars. + */ +void +im_filename_suffix( const char *path, char *suffix ) +{ + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char *p; + + im_filename_split( path, name, mode ); + if( (p = strrchr( name, '.' )) ) + strcpy( suffix, p + 1 ); + else + strcpy( suffix, "" ); +} + +/* Does a filename have one of a set of suffixes. Ignore case. + */ +int +im_filename_suffix_match( const char *path, const char *suffixes[] ) +{ + char suffix[FILENAME_MAX]; + const char **p; + + im_filename_suffix( path, suffix ); + for( p = suffixes; *p; p++ ) + if( g_ascii_strcasecmp( suffix, *p ) == 0 ) + return( 1 ); + + return( 0 ); +} + +/* p points to the start of a buffer ... move it on through the buffer (ready + * for the next call), and return the current option (or NULL for option + * missing). ',' characters inside options can be escaped with a '\'. + */ +char * +im_getnextoption( char **in ) +{ + char *p = *in; + char *q = p; + + if( !p || !*p ) + return( NULL ); + + /* Find the next ',' not prefixed with a '\'. + */ + while( (p = strchr( p, ',' )) && p[-1] == '\\' ) + p += 1; + + if( p ) { + /* Another option follows this one .. set up to pick that out + * next time. + */ + *p = '\0'; + *in = p + 1; + } + else { + /* This is the last one. + */ + *in = NULL; + } + + if( strlen( q ) > 0 ) + return( q ); + else + return( NULL ); +} + +/* Get a suboption string, or NULL. + */ +char * +im_getsuboption( const char *buf ) +{ + char *p, *q, *r; + + if( !(p = strchr( buf, ':' )) ) + /* No suboption. + */ + return( NULL ); + + /* Step over the ':'. + */ + p += 1; + + /* Need to unescape any \, pairs. Shift stuff down one if we find one. + */ + for( q = p; *q; q++ ) + if( q[0] == '\\' && q[1] == ',' ) + for( r = q; *r; r++ ) + r[0] = r[1]; + + return( p ); +} + +/* Make something local to an image descriptor ... pass in a constructor + * and a destructor, plus three args. + */ +void * +im_local( IMAGE *im, + im_construct_fn cons, im_callback_fn dest, void *a, void *b, void *c ) +{ + void *obj; + + if( !im ) { + im_errormsg( "im_local: NULL image descriptor" ); + return( NULL ); + } + + if( !(obj = cons( a, b, c )) ) + return( NULL ); + if( im_add_close_callback( im, (im_callback_fn) dest, obj, a ) ) { + dest( obj, a ); + return( NULL ); + } + + return( obj ); +} + +/* Make an array of things local to a descriptor ... eg. make 6 local temp + * images. + */ +int +im_local_array( IMAGE *im, void **out, int n, + im_construct_fn cons, im_callback_fn dest, void *a, void *b, void *c ) +{ + int i; + + for( i = 0; i < n; i++ ) + if( !(out[i] = im_local( im, cons, dest, a, b, c )) ) + return( -1 ); + + return( 0 ); +} + +/* Get file length ... 64-bitally. -1 for error. + */ +gint64 +im_file_length( int fd ) +{ +#ifdef OS_WIN32 + struct _stati64 st; + + if( _fstati64( fd, &st ) == -1 ) { +#else /*!OS_WIN32*/ + struct stat st; + + if( fstat( fd, &st ) == -1 ) { +#endif /*OS_WIN32*/ + im_error_system( errno, "im_file_length", + _( "unable to get file stats" ) ); + return( -1 ); + } + + return( st.st_size ); +} + +/* Wrap write() up + */ +int +im__write( int fd, const void *buf, size_t count ) +{ + do { + size_t nwritten = write( fd, buf, count ); + + if( nwritten == (size_t) -1 ) { + im_error_system( errno, "im__write", + _( "write failed" ) ); + return( -1 ); + } + + buf = (void *) ((char *) buf + nwritten); + count -= nwritten; + } while( count > 0 ); + + return( 0 ); +} + +/* Load up a file as a string. + */ +char * +im__file_read( FILE *fp, const char *name, unsigned int *length_out ) +{ + long len; + size_t read; + char *str; + + /* Find length. + */ + fseek( fp, 0L, 2 ); + len = ftell( fp ); + if( len > 20 * 1024 * 1024 ) { + /* Seems crazy! + */ + im_error( "im__file_read", _( "\"%s\" too long" ), name ); + return( NULL ); + } + + if( len == -1 ) { + int size; + + /* Can't get length: read in chunks and realloc() to end of + * file. + */ + str = NULL; + len = 0; + size = 0; + do { + size += 1024; + if( !(str = realloc( str, size )) ) { + im_error( "im__file_read", + _( "out of memory" ) ); + return( NULL ); + } + + /* -1 to allow space for an extra NULL we add later. + */ + read = fread( str + len, sizeof( char ), + (size - len - 1) / sizeof( char ), + fp ); + len += read; + } while( !feof( fp ) ); + +#ifdef DEBUG + printf( "read %d bytes from unseekable stream\n", len ); +#endif /*DEBUG*/ + } + else { + /* Allocate memory and fill. + */ + if( !(str = im_malloc( NULL, len + 1 )) ) + return( NULL ); + rewind( fp ); + read = fread( str, sizeof( char ), (size_t) len, fp ); + if( read != (size_t) len ) { + im_free( str ); + im_error( "im__file_read", + _( "error reading from file \"%s\"" ), name ); + return( NULL ); + } + } + + str[len] = '\0'; + + if( length_out ) + *length_out = len; + + return( str ); +} + +/* Load from a filename as a string. + */ +char * +im__file_read_name( const char *name, unsigned int *length_out ) +{ + FILE *fp; + char *buffer; + +#ifdef BINARY_OPEN + if( !(fp = fopen( name, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( name, "r" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im__file_read_name", + _( "unable to open file \"%s\"" ), name ); + return( NULL ); + } + if( !(buffer = im__file_read( fp, name, length_out )) ) { + fclose( fp ); + return( NULL ); + } + fclose( fp ); + + return( buffer ); +} + +/* Alloc/free a GValue. + */ +static GValue * +im__gvalue_new( GType type ) +{ + GValue *value; + + value = g_new0( GValue, 1 ); + g_value_init( value, type ); + + return( value ); +} + +static GValue * +im__gvalue_copy( GValue *value ) +{ + GValue *value_copy; + + value_copy = im__gvalue_new( G_VALUE_TYPE( value ) ); + g_value_copy( value, value_copy ); + + return( value_copy ); +} + +static void +im__gvalue_free( GValue *value ) +{ + g_value_unset( value ); + g_free( value ); +} + +GValue * +im__gvalue_ref_string_new( const char *text ) +{ + GValue *value; + + value = im__gvalue_new( IM_TYPE_REF_STRING ); + im_ref_string_set( value, text ); + + return( value ); +} + +/* Free a GSList of GValue. + */ +void +im__gslist_gvalue_free( GSList *list ) +{ + g_slist_foreach( list, (GFunc) im__gvalue_free, NULL ); + g_slist_free( list ); +} + +/* Copy a GSList of GValue. + */ +GSList * +im__gslist_gvalue_copy( const GSList *list ) +{ + GSList *copy; + const GSList *p; + + copy = NULL; + + for( p = list; p; p = p->next ) + copy = g_slist_prepend( copy, + im__gvalue_copy( (GValue *) p->data ) ); + + copy = g_slist_reverse( copy ); + + return( copy ); +} + +/* Merge two GSList of GValue ... append to a all elements in b which are not + * in a. Return the new value of a. Works for any vips refcounted type + * (string, blob, etc.). + */ +GSList * +im__gslist_gvalue_merge( GSList *a, const GSList *b ) +{ + const GSList *i, *j; + GSList *tail; + + tail = NULL; + + for( i = b; i; i = i->next ) { + GValue *value = (GValue *) i->data; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_REF_STRING ); + + for( j = a; j; j = j->next ) { + GValue *value2 = (GValue *) j->data; + + assert( G_VALUE_TYPE( value2 ) == IM_TYPE_REF_STRING ); + + /* Just do a pointer compare ... good enough 99.9% of + * the time. + */ + if( im_ref_string_get( value ) == + im_ref_string_get( value2 ) ) + break; + } + + if( !j ) + tail = g_slist_prepend( tail, + im__gvalue_copy( value ) ); + } + + a = g_slist_concat( a, g_slist_reverse( tail ) ); + + return( a ); +} + +/* Make a char* from GSList of GValue. Each GValue should be a ref_string. + * free the result. Empty list -> "", not NULL. Join strings with '\n'. + */ +char * +im__gslist_gvalue_get( const GSList *list ) +{ + const GSList *p; + size_t length; + char *all; + char *q; + + /* Need to estimate length first. + */ + length = 0; + for( p = list; p; p = p->next ) { + GValue *value = (GValue *) p->data; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_REF_STRING ); + + /* +1 for the newline we will add for each item. + */ + length += im_ref_string_get_length( value ) + 1; + } + + if( length == 0 ) + return( NULL ); + + /* More than 10MB of history? Madness! + */ + assert( length < 10 * 1024 * 1024 ); + + /* +1 for '\0'. + */ + if( !(all = im_malloc( NULL, length + 1 )) ) + return( NULL ); + + q = all; + for( p = list; p; p = p->next ) { + GValue *value = (GValue *) p->data; + + strcpy( q, im_ref_string_get( value ) ); + q += im_ref_string_get_length( value ); + strcpy( q, "\n" ); + q += 1; + } + + assert( q - all == length ); + + return( all ); +} diff --git a/libsrc/iofuncs/vbuf.c b/libsrc/iofuncs/vbuf.c new file mode 100644 index 00000000..5735c26a --- /dev/null +++ b/libsrc/iofuncs/vbuf.c @@ -0,0 +1,332 @@ +/* Some basic util functions. + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Largest string we can append in one operation. + */ +#define MAX_STRSIZE (16000) + +void +im_buf_rewind( VBuf *buf ) +{ + buf->i = 0; + buf->lasti = 0; + buf->full = FALSE; + + if( buf->base ) + buf->base[0] = '\0'; +} + +/* Power on init. + */ +void +im_buf_init( VBuf *buf ) +{ + buf->base = NULL; + buf->mx = 0; + buf->dynamic = FALSE; + im_buf_rewind( buf ); +} + +/* Reset to power on state ... only needed for dynamic bufs. + */ +void +im_buf_destroy( VBuf *buf ) +{ + if( buf->dynamic ) { + if( buf->base ) { + im_free( buf->base ); + buf->base = NULL; + } + } + + im_buf_init( buf ); +} + +/* Set to a static string. + */ +void +im_buf_set_static( VBuf *buf, char *base, int mx ) +{ + assert( mx >= 4 ); + + im_buf_destroy( buf ); + + buf->base = base; + buf->mx = mx; + buf->dynamic = FALSE; + im_buf_rewind( buf ); +} + +void +im_buf_init_static( VBuf *buf, char *base, int mx ) +{ + im_buf_init( buf ); + im_buf_set_static( buf, base, mx ); +} + +/* Set to a dynamic string. + */ +void +im_buf_set_dynamic( VBuf *buf, int mx ) +{ + assert( mx >= 4 ); + + if( buf->mx == mx && buf->dynamic ) + /* No change? + */ + im_buf_rewind( buf ); + else { + im_buf_destroy( buf ); + + if( !(buf->base = IM_ARRAY( NULL, mx, char )) ) + /* No error return, so just block writes. + */ + buf->full = TRUE; + else { + buf->mx = mx; + buf->dynamic = TRUE; + im_buf_rewind( buf ); + } + } +} + +void +im_buf_init_dynamic( VBuf *buf, int mx ) +{ + im_buf_init( buf ); + im_buf_set_dynamic( buf, mx ); +} + +/* Append at most sz chars from string to buf. sz < 0 means unlimited. + * FALSE on overflow. + */ +gboolean +im_buf_appendns( VBuf *buf, const char *str, int sz ) +{ + int len; + int n; + int avail; + int cpy; + + if( buf->full ) + return( FALSE ); + + /* Amount we want to copy. + */ + len = strlen( str ); + if( sz >= 0 ) + n = IM_MIN( sz, len ); + else + n = len; + + /* Space available. + */ + avail = buf->mx - buf->i - 4; + + /* Amount we actually copy. + */ + cpy = IM_MIN( n, avail ); + + strncpy( buf->base + buf->i, str, cpy ); + buf->i += cpy; + + if( buf->i >= buf->mx - 4 ) { + buf->full = TRUE; + strcpy( buf->base + buf->mx - 4, "..." ); + buf->i = buf->mx - 1; + return( FALSE ); + } + + return( TRUE ); +} + +/* Append a string to a buf. Error on overflow. + */ +gboolean +im_buf_appends( VBuf *buf, const char *str ) +{ + return( im_buf_appendns( buf, str, -1 ) ); +} + +/* Append a character to a buf. Error on overflow. + */ +gboolean +im_buf_appendc( VBuf *buf, char ch ) +{ + char tiny[2]; + + tiny[0] = ch; + tiny[1] = '\0'; + + return( im_buf_appendns( buf, tiny, 1 ) ); +} + +/* Append a double, non-localised. Useful for config files etc. + */ +gboolean +im_buf_appendg( VBuf *buf, double g ) +{ + char text[G_ASCII_DTOSTR_BUF_SIZE]; + + g_ascii_dtostr( text, sizeof( text ), g ); + + return( im_buf_appends( buf, text ) ); +} + + +/* Swap the rightmost occurence of old for new. + */ +gboolean +im_buf_change( VBuf *buf, const char *old, const char *new ) +{ + int olen = strlen( old ); + int nlen = strlen( new ); + int i; + + if( buf->full ) + return( FALSE ); + if( buf->i - olen + nlen > buf->mx - 4 ) { + buf->full = TRUE; + return( FALSE ); + } + + /* Find pos of old. + */ + for( i = buf->i - olen; i > 0; i-- ) + if( im_isprefix( old, buf->base + i ) ) + break; + assert( i >= 0 ); + + /* Move tail of buffer to make right-size space for new. + */ + memmove( buf->base + i + nlen, buf->base + i + olen, + buf->i - i - olen ); + + /* Copy new in. + */ + memcpy( buf->base + i, new, nlen ); + buf->i = i + nlen + (buf->i - i - olen); + + return( TRUE ); +} + +/* Remove the last character. + */ +gboolean +im_buf_removec( VBuf *buf, char ch ) +{ + if( buf->full ) + return( FALSE ); + + if( buf->i <= 0 ) + return( FALSE ); + buf->i -= 1; + assert( buf->base[buf->i] == ch ); + + return( TRUE ); +} + +/* Append to a buf, args as printf. FALSE on overflow. + */ +gboolean +im_buf_appendf( VBuf *buf, const char *fmt, ... ) +{ + va_list ap; + char str[MAX_STRSIZE]; + + va_start( ap, fmt ); + (void) im_vsnprintf( str, MAX_STRSIZE, fmt, ap ); + va_end( ap ); + + return( im_buf_appends( buf, str ) ); +} + +/* Append to a buf, args as vprintf. Error on overflow. + */ +gboolean +im_buf_vappendf( VBuf *buf, const char *fmt, va_list ap ) +{ + char str[MAX_STRSIZE]; + + (void) im_vsnprintf( str, MAX_STRSIZE, fmt, ap ); + + return( im_buf_appends( buf, str ) ); +} + +/* Read all text from buffer. + */ +const char * +im_buf_all( VBuf *buf ) +{ + buf->base[buf->i] = '\0'; + + return( buf->base ); +} + +gboolean +im_buf_isempty( VBuf *buf ) +{ + return( buf->i == 0 ); +} + +gboolean +im_buf_isfull( VBuf *buf ) +{ + return( buf->full ); +} + +/* Buffer length ... still need to do im_buf_all(). + */ +int +im_buf_len( VBuf *buf ) +{ + return( buf->i ); +} diff --git a/libsrc/iofuncs/window.c b/libsrc/iofuncs/window.c new file mode 100644 index 00000000..6539ea44 --- /dev/null +++ b/libsrc/iofuncs/window.c @@ -0,0 +1,388 @@ +/* Manage sets of mmap buffers on an image. + * + * 30/10/06 + * - from region.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_TOTAL +#define DEBUG_ENVIRONMENT +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include + +#include +#include +#include + +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Sanity checking ... write to this during read tests to make sure we don't + * get optimised out. + */ +int im__read_test; + +/* Add this many lines above and below the mmap() window. + */ +int im__window_margin = IM__WINDOW_MARGIN; + +/* Track global mmap usage. + */ +#ifdef DEBUG_TOTAL +static int total_mmap_usage = 0; +static int max_mmap_usage = 0; +#endif /*DEBUG_TOTAL*/ + +static int +im_window_unmap( im_window_t *window ) +{ + /* unmap the old window + */ + if( window->baseaddr ) { + if( im__munmap( window->baseaddr, window->length ) ) + return( -1 ); + +#ifdef DEBUG_TOTAL + g_mutex_lock( im__global_lock ); + total_mmap_usage -= window->length; + assert( total_mmap_usage >= 0 ); + g_mutex_unlock( im__global_lock ); +#endif /*DEBUG_TOTAL*/ + + window->data = NULL; + window->baseaddr = NULL; + window->length = 0; + } + + return( 0 ); +} + +static int +im_window_free( im_window_t *window ) +{ + assert( window->ref_count == 0 ); + +#ifdef DEBUG + printf( "** im_window_free: window top = %d, height = %d (%p)\n", + window->top, window->height, window ); +#endif /*DEBUG*/ + + if( im_window_unmap( window ) ) + return( -1 ); + + window->im = NULL; + + im_free( window ); + + return( 0 ); +} + +int +im_window_unref( im_window_t *window ) +{ + IMAGE *im = window->im; + + g_mutex_lock( im->sslock ); + +#ifdef DEBUG + printf( "im_window_unref: window top = %d, height = %d, count = %d\n", + window->top, window->height, window->ref_count ); +#endif /*DEBUG*/ + + assert( window->ref_count > 0 ); + + window->ref_count -= 1; + + if( window->ref_count == 0 ) { + assert( g_slist_find( im->windows, window ) ); + im->windows = g_slist_remove( im->windows, window ); + +#ifdef DEBUG + printf( "im_window_unref: %d windows left\n", + g_slist_length( im->windows ) ); +#endif /*DEBUG*/ + + if( im_window_free( window ) ) { + g_mutex_unlock( im->sslock ); + return( -1 ); + } + } + + g_mutex_unlock( im->sslock ); + + return( 0 ); +} + +#ifdef DEBUG_TOTAL +static void +trace_mmap_usage( void ) +{ + g_mutex_lock( im__global_lock ); + { + static int last_total = 0; + int total = total_mmap_usage / (1024 * 1024); + int max = max_mmap_usage / (1024 * 1024); + + if( total != last_total ) { + printf( "im_window_set: current mmap " + "usage of ~%dMB (high water mark %dMB)\n", + total, max ); + last_total = total; + } + } + g_mutex_unlock( im__global_lock ); +} +#endif /*DEBUG_TOTAL*/ + +static int +im_getpagesize() +{ + static int pagesize = 0; + + if( !pagesize ) { +#ifdef OS_WIN32 + SYSTEM_INFO si; + + GetSystemInfo( &si ); + + pagesize = si.dwAllocationGranularity; +#else /*OS_WIN32*/ + pagesize = getpagesize(); +#endif /*OS_WIN32*/ + +#ifdef DEBUG_TOTAL + printf( "im_getpagesize: 0x%x\n", pagesize ); +#endif /*DEBUG_TOTAL*/ + } + + return( pagesize ); +} + +/* Map a window into a file. + */ +static int +im_window_set( im_window_t *window, int top, int height ) +{ + int pagesize = im_getpagesize(); + + void *baseaddr; + gint64 start, end, pagestart; + size_t length, pagelength; + + /* Calculate start and length for our window. + */ + start = window->im->sizeof_header + + (gint64) IM_IMAGE_SIZEOF_LINE( window->im ) * top; + length = (size_t) IM_IMAGE_SIZEOF_LINE( window->im ) * height; + + pagestart = start - start % pagesize; + end = start + length; + pagelength = end - pagestart; + + if( !(baseaddr = im__mmap( window->im->fd, 0, pagelength, pagestart )) ) + return( -1 ); + + window->baseaddr = baseaddr; + window->length = pagelength; + + window->data = (char *) baseaddr + (start - pagestart); + window->top = top; + window->height = height; + + /* Sanity check ... make sure the data pointer is readable. + */ + im__read_test &= window->data[0]; + +#ifdef DEBUG_TOTAL + g_mutex_lock( im__global_lock ); + total_mmap_usage += window->length; + if( total_mmap_usage > max_mmap_usage ) + max_mmap_usage = total_mmap_usage; + g_mutex_unlock( im__global_lock ); + trace_mmap_usage(); +#endif /*DEBUG_TOTAL*/ + + return( 0 ); +} + +/* Make a new window. + */ +static im_window_t * +im_window_new( IMAGE *im, int top, int height ) +{ + im_window_t *window; + + if( !(window = IM_NEW( NULL, im_window_t )) ) + return( NULL ); + + window->ref_count = 0; + window->im = im; + window->top = 0; + window->height = 0; + window->data = NULL; + window->baseaddr = NULL; + window->length = 0; + + if( im_window_set( window, top, height ) ) { + im_window_free( window ); + return( NULL ); + } + + im->windows = g_slist_prepend( im->windows, window ); + window->ref_count += 1; + +#ifdef DEBUG + printf( "** im_window_new: window top = %d, height = %d (%p)\n", + window->top, window->height, window ); +#endif /*DEBUG*/ + + return( window ); +} + +/* A request for an area of pixels. + */ +typedef struct { + int top; + int height; +} request_t; + +static void * +im_window_fits( im_window_t *window, request_t *req ) +{ + if( window->top <= req->top && + window->top + window->height >= req->top + req->height ) + return( window ); + + return( NULL ); +} + +/* Find an existing window that fits within top/height and return a ref. + */ +static im_window_t * +im_window_find( IMAGE *im, int top, int height ) +{ + request_t req; + im_window_t *window; + + req.top = top; + req.height = height; + window = im_slist_map2( im->windows, + (VSListMap2Fn) im_window_fits, &req, NULL ); + + if( window ) { + window->ref_count += 1; + +#ifdef DEBUG + printf( "im_window_find: ref window top = %d, height = %d, " + "count = %d\n", + top, height, window->ref_count ); +#endif /*DEBUG*/ + } + + return( window ); +} + +/* Return a ref to a window that encloses top/height. + */ +im_window_t * +im_window_ref( IMAGE *im, int top, int height ) +{ + im_window_t *window; +#ifdef DEBUG_ENVIRONMENT + static const char *str = NULL; +#endif /*DEBUG_ENVIRONMENT*/ + +#ifdef DEBUG_ENVIRONMENT + if( !str ) { + if( (str = g_getenv( "IM_WINDOW_MARGIN" )) ) { + printf( "iofuncs/region.c: " + "compiled with DEBUG_ENVIRONMENT enabled\n" ); + im__window_margin = atoi( str ); + printf( "im_region_create: setting window margin to %d " + "from environment\n", im__window_margin ); + } + else + str = "done"; + } +#endif /*DEBUG_ENVIRONMENT*/ + + g_mutex_lock( im->sslock ); + + if( !(window = im_window_find( im, top, height )) ) { + /* No existing window ... make a new one. + */ + top -= im__window_margin; + height += im__window_margin * 2; + top = IM_CLIP( 0, top, im->Ysize - 1 ); + height = IM_CLIP( 0, height, im->Ysize - top ); + + if( !(window = im_window_new( im, top, height )) ) { + g_mutex_unlock( im->sslock ); + return( NULL ); + } + } + + g_mutex_unlock( im->sslock ); + + return( window ); +} + +void +im_window_print( im_window_t *window ) +{ + printf( "im_window_t: %p ref_count = %d, ", window, window->ref_count ); + printf( "im = %p, ", window->im ); + printf( "top = %d, ", window->top ); + printf( "height = %d, ", window->height ); + printf( "data = %p, ", window->data ); + printf( "baseaddr = %p, ", window->baseaddr ); + printf( "length = %zd\n", window->length ); +} diff --git a/libsrc/makedef.pl b/libsrc/makedef.pl new file mode 100755 index 00000000..d84039ef --- /dev/null +++ b/libsrc/makedef.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +# update vips.def from "nm" output of the installed library +# +# not very portable :-( eg mac os x and win32 will fail horribly +# +# pass in the install prefix ... or type "make vips.def" + +open DATA, "nm -B @ARGV[0]/lib/libvips.so |"; + +while( ) { + next if ! /^[a-f0-9]+ T (im_[a-zA-Z].*)$/ && + ! /^[a-f0-9]+ T (error_exit)$/; + push @names, $1; +} + +print "EXPORTS\n"; +foreach $i (sort @names) { + print "\t$i\n"; +} diff --git a/libsrc/matrix/Makefile.am b/libsrc/matrix/Makefile.am new file mode 100644 index 00000000..54666042 --- /dev/null +++ b/libsrc/matrix/Makefile.am @@ -0,0 +1,13 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libmatrix.la + +libmatrix_la_SOURCES = \ + im_matcat.c \ + im_matinv.c \ + im_matmul.c \ + im_mattrn.c \ + matalloc.c \ + matrix_dispatch.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/matrix/im_matcat.c b/libsrc/matrix/im_matcat.c new file mode 100644 index 00000000..78e837dd --- /dev/null +++ b/libsrc/matrix/im_matcat.c @@ -0,0 +1,88 @@ +/* @(#) combine two masks. Result mask is made and returned. + * @(#) Pass in the name to set in the creation of the mask. + * @(#) DOUBLEMASK * + * @(#) im_matcat( in1, in2, name ); + * @(#) DOUBLEMASK *in1, *in2; + * @(#) char *name; + * @(#) + * @(#) return NULL for error. + * + * 1994, K. Martinez + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* MATRIX concatenate (join columns ie add mask to bottom of another) + */ +DOUBLEMASK * +im_matcat( DOUBLEMASK *in1, DOUBLEMASK *in2, const char *name ) +{ + int newxsize, newysize; + DOUBLEMASK *mat; + double *out; + + /* matrices must be same width + */ + if( in1->xsize != in2->xsize ) { + im_errormsg( "im_matcat: matrices must be same width" ); + return( NULL ); + } + + newxsize = in1->xsize; + newysize = in1->ysize + in2->ysize; + + /* Allocate output matrix. + */ + if( !(mat = im_create_dmask( name, newxsize, newysize )) ) { + im_errormsg( "im_matcat: unable to allocate output matrix" ); + return( NULL ); + } + + /* copy first matrix then add second on the end + */ + memcpy( mat->coeff, in1->coeff, + in1->xsize * in1->ysize * sizeof( double ) ); + out = mat->coeff + in1->xsize * in1->ysize; + memcpy( out, in2->coeff, + in2->xsize * in2->ysize * sizeof( double ) ); + + return( mat ); +} diff --git a/libsrc/matrix/im_matinv.c b/libsrc/matrix/im_matinv.c new file mode 100644 index 00000000..cded8686 --- /dev/null +++ b/libsrc/matrix/im_matinv.c @@ -0,0 +1,504 @@ +/* @(#) Allocate, and return a pointer to, a DOUBLEMASK representing the LU + * @(#) decomposition of the matrix in DOUBLEMASK *mat. Give it the filename + * @(#) member *name. Returns NULL on error. Scale and offset are ignored. + * @(#) + * @(#) DOUBLEMASK *im_lu_decomp( const DOUBLEMASK *mat, const char *name ); + * @(#) + * @(#) Solve the system of linear equations Ax=b, where matrix A has already + * @(#) been decomposed into LU form in DOUBLEMASK *lu. Input vector b is in + * @(#) vec and is overwritten with vector x. + * @(#) + * @(#) int im_lu_solve( const DOUBLEMASK *lu, double *vec ); + * @(#) + * @(#) Allocate, and return a pointer to, a DOUBLEMASK representing the + * @(#) inverse of the matrix represented in mat. Give it the filename + * @(#) member *name. Returns NULL on error. Scale and offset are ignored. + * @(#) + * @(#) DOUBLEMASK *im_matinv( const DOUBLEMASK *mat, const char *name ); + * @(#) + * @(#) Invert the matrix represented by the DOUBLEMASK *mat, and store + * @(#) it in the place of *mat. Returns -1 on error. Scale and offset + * @(#) are ignored. + * @(#) + * @(#) int im_matinv_inplace( DOUBLEMASK *mat ); + * + * Author: Tom Vajzovic + * Copyright: 2006, Tom Vajzovic + * Written on: 2006-09-08 + * + * undated: + * - page 43-45 of numerical recipes in C 1998 + * + * 2006-09-08 tcv: + * - complete rewrite; algorithm unchanged + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/** HEADERS **/ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + + +/** CONSTANTS **/ + +#define TOO_SMALL ( 2.0 * DBL_MIN ) +/* DBL_MIN is smallest *normalized* double precision float */ + + +/** MACROS **/ + +#define MATRIX( mask, i, j ) ( (mask)-> coeff[ (j) + (i) * (mask)-> xsize ] ) +/* use DOUBLEMASK or INTMASK as matrix type */ + + +/** LOCAL FUNCTION DECLARATIONS **/ + +static int +mat_inv_lu( + DOUBLEMASK *inv, + const DOUBLEMASK *lu + ); + +static int +mat_inv_direct( + DOUBLEMASK *inv, + const DOUBLEMASK *mat, + const char *function_name + ); + + +/** EXPORTED FUNCTION DEFINITIONS **/ + +DOUBLEMASK * +im_lu_decomp( + const DOUBLEMASK *mat, + const char *name +){ +#define FUNCTION_NAME "im_lu_decomp" +/* This function takes any square NxN DOUBLEMASK treats it as a matrix. + * It allocates a DOUBLEMASK which is (N+1)xN. + * + * It calculates the PLU decomposition, storing the upper and diagonal parts + * of U, together with the lower parts of L, as an NxN matrix in the first + * N rows of the new matrix. The diagonal parts of L are all set to unity + * and are not stored. + * + * The final row of the new DOUBLEMASK has only integer entries, which + * represent the row-wise permutations made by the permuatation matrix P. + * + * The scale and offset members of the input DOUBLEMASK are ignored. + * + * See: + * PRESS, W. et al, 1992. Numerical Recipies in C; The Art of Scientific + * Computing, 2nd ed. Cambridge: Cambridge University Press, pp. 43-50. + */ + int i, j, k; + double *row_scale; + DOUBLEMASK *lu; + + if( mat-> xsize != mat-> ysize ){ + im_error( FUNCTION_NAME, "non-square matrix" ); + return NULL; + } +#define N ( mat -> xsize ) + + lu= im_create_dmask( name, N, N + 1 ); + row_scale= IM_ARRAY( NULL, N, double ); + + if( ! row_scale || ! lu ){ + im_free_dmask( lu ); + im_free( row_scale ); + return NULL; + } + /* copy all coefficients and then perform decomposition in-place */ + + memcpy( lu-> coeff, mat-> coeff, N * N * sizeof( double ) ); + +#define LU( i, j ) MATRIX( lu, (i), (j) ) +#define perm ( lu-> coeff + N * N ) + + for( i= 0; i < N; ++i ){ + + row_scale[ i ]= 0.0; + + for( j= 0; j < N; ++j ){ + double abs_val= fabs( LU( i, j ) ); + + /* find largest in each ROW */ + + if( abs_val > row_scale[ i ] ) + row_scale[ i ]= abs_val; + } + if( ! row_scale[ i ] ){ + im_error( FUNCTION_NAME, "singular matrix" ); + im_free_dmask( lu ); + im_free( row_scale ); + return NULL; + } + /* fill array with scaling factors for each ROW */ + + row_scale[ i ]= 1.0 / row_scale[ i ]; + } + for( j= 0; j < N; ++j ){ /* loop over COLs */ + double max= -1.0; + int i_of_max; + + /* not needed, but stops a compiler warning */ + i_of_max= 0; + + /* loop over ROWS in upper-half, except diagonal */ + + for( i= 0; i < j; ++i ) + for( k= 0; k < i; ++k ) + LU( i, j )-= LU( i, k ) * LU( k, j ); + + /* loop over ROWS in diagonal and lower-half */ + + for( i= j; i < N; ++i ){ + double abs_val; + + for( k= 0; k < j; ++k ) + LU( i, j )-= LU( i, k ) * LU( k, j ); + + /* find largest element in each COLUMN scaled so that */ + /* largest in each ROW is 1.0 */ + + abs_val= row_scale[ i ] * fabs( LU( i, j ) ); + + if( abs_val > max ){ + max= abs_val; + i_of_max= i; + } + } + if( fabs( LU( i_of_max, j ) ) < TOO_SMALL ){ + /* divisor is near zero */ + im_error( FUNCTION_NAME, "singular or near-singular matrix" ); + im_free_dmask( lu ); + im_free( row_scale ); + return NULL; + } + if( i_of_max != j ){ + /* swap ROWS */ + + for( k= 0; k < N; ++k ){ + double temp= LU( j, k ); + LU( j, k )= LU( i_of_max, k ); + LU( i_of_max, k )= temp; + } + row_scale[ i_of_max ]= row_scale[ j ]; + /* no need to copy this scale back up - we won't use it */ + } + /* record permutation */ + perm[ j ]= i_of_max; + + /* divide by best (largest scaled) pivot found */ + for( i= j + 1; i < N; ++i ) + LU( i, j )/= LU( j, j ); + } + im_free( row_scale ); + + return lu; + +#undef N +#undef LU +#undef perm +#undef FUNCTION_NAME +} + +int +im_lu_solve( + const DOUBLEMASK *lu, + double *vec +){ +#define FUNCTION_NAME "im_lu_solve" + int i, j; + + if( lu-> xsize + 1 != lu-> ysize ){ + im_error( FUNCTION_NAME, "not an LU decomposed matrix" ); + return -1; + } +#define N ( lu -> xsize ) +#define LU( i, j ) MATRIX( lu, (i), (j) ) +#define perm ( lu-> coeff + N * N ) + + for( i= 0; i < N; ++i ){ + int i_perm= perm[ i ]; + + if( i_perm != i ){ + double temp= vec[ i ]; + vec[ i ]= vec[ i_perm ]; + vec[ i_perm ]= temp; + } + for( j= 0; j < i; ++j ) + vec[ i ]-= LU( i, j ) * vec [ j ]; + } + + for( i= N - 1; i >= 0; --i ){ + + for( j= i + 1; j < N; ++j ) + vec[ i ]-= LU( i, j ) * vec [ j ]; + + vec[ i ]/= LU( i, i ); + } + return 0; + +#undef LU +#undef perm +#undef N +#undef FUNCTION_NAME +} + +DOUBLEMASK * +im_matinv( + const DOUBLEMASK *mat, + const char *name +){ +#define FUNCTION_NAME "im_matinv" + + DOUBLEMASK *inv; + + if( mat-> xsize != mat-> ysize ){ + im_error( FUNCTION_NAME, "non-square matrix" ); + return NULL; + } +#define N ( mat -> xsize ) + inv= im_create_dmask( name, N, N ); + if( ! inv ) + return NULL; + + if( N < 4 ){ + if( mat_inv_direct( inv, (const DOUBLEMASK *) mat, FUNCTION_NAME ) ){ + im_free_dmask( inv ); + return NULL; + } + return inv; + } + else { + DOUBLEMASK *lu= im_lu_decomp( mat, "temp" ); + + if( ! lu || mat_inv_lu( inv, (const DOUBLEMASK*) lu ) ){ + im_free_dmask( lu ); + im_free_dmask( inv ); + return NULL; + } + im_free_dmask( lu ); + + return inv; + } +#undef N +#undef FUNCTION_NAME +} + +int +im_matinv_inplace( + DOUBLEMASK *mat +){ +#define FUNCTION_NAME "im_matinv_inplace" + int to_return= 0; + + if( mat-> xsize != mat-> ysize ){ + im_error( FUNCTION_NAME, "non-square matrix" ); + return -1; + } +#define N ( mat -> xsize ) + if( N < 4 ){ + DOUBLEMASK *dup= im_dup_dmask( mat, "temp" ); + if( ! dup ) + return -1; + + to_return= mat_inv_direct( mat, (const DOUBLEMASK*) dup, FUNCTION_NAME ); + + im_free_dmask( dup ); + + return to_return; + } + { + DOUBLEMASK *lu; + + lu= im_lu_decomp( mat, "temp" ); + + if( ! lu || mat_inv_lu( mat, (const DOUBLEMASK*) lu ) ) + to_return= -1; + + im_free_dmask( lu ); + + return to_return; + } +#undef N +#undef FUNCTION_NAME +} + +/* Invert a square size x size matrix stored in matrix[][] + * result is returned in the same matrix + */ +int +im_invmat( + double **matrix, + int size + ){ + + DOUBLEMASK *mat= im_create_dmask( "temp", size, size ); + int i; + int to_return= 0; + + for( i= 0; i < size; ++i ) + memcpy( mat-> coeff + i * size, matrix[ i ], size * sizeof( double ) ); + + to_return= im_matinv_inplace( mat ); + + if( ! to_return ) + for( i= 0; i < size; ++i ) + memcpy( matrix[ i ], mat-> coeff + i * size, size * sizeof( double ) ); + + im_free_dmask( mat ); + + return to_return; +} + + +/** LOCAL FUNCTION DEFINITIONS **/ + +static int +mat_inv_lu( + DOUBLEMASK *inv, + const DOUBLEMASK *lu +){ +#define N ( lu-> xsize ) +#define INV( i, j ) MATRIX( inv, (i), (j) ) + + int i, j; + double *vec= IM_ARRAY( NULL, N, double ); + + if( ! vec ) + return -1; + + for( j= 0; j < N; ++j ){ + + for( i= 0; i < N; ++i ) + vec[ i ]= 0.0; + + vec[ j ]= 1.0; + + im_lu_solve( lu, vec ); + + for( i= 0; i < N; ++i ) + INV( i, j )= vec[ i ]; + } + im_free( vec ); + + inv-> scale= 1.0; + inv-> offset= 0.0; + + return 0; + +#undef N +#undef INV +} + +static int +mat_inv_direct( + DOUBLEMASK *inv, + const DOUBLEMASK *mat, + const char *function_name +){ +#define N ( mat -> xsize ) +#define MAT( i, j ) MATRIX( mat, (i), (j) ) +#define INV( i, j ) MATRIX( inv, (i), (j) ) + + inv-> scale= 1.0; + inv-> offset= 0.0; + + switch( N ){ + case 1: { + if( fabs( MAT( 0, 0 ) ) < TOO_SMALL ){ + im_error( function_name, "singular or near-singular matrix" ); + return -1; + } + INV( 0, 0 )= 1.0 / MAT( 0, 0 ); + return 0; + } + case 2: { + double det= MAT( 0, 0 ) * MAT( 1, 1 ) - MAT( 0, 1 ) * MAT( 1, 0 ); + + if( fabs( det ) < TOO_SMALL ){ + im_error( function_name, "singular or near-singular matrix" ); + return -1; + } + INV( 0, 0 )= MAT( 1, 1 ) / det; + INV( 0, 1 )= -MAT( 0, 1 ) / det; + INV( 1, 0 )= -MAT( 1, 0 ) / det; + INV( 1, 1 )= MAT( 0, 0 ) / det; + + return 0; + } + case 3: { + double det= MAT( 0, 0 ) * ( MAT( 1, 1 ) * MAT( 2, 2 ) - MAT( 1, 2 ) * MAT( 2, 1 ) ) + - MAT( 0, 1 ) * ( MAT( 1, 0 ) * MAT( 2, 2 ) - MAT( 1, 2 ) * MAT( 2, 0 ) ) + + MAT( 0, 2 ) * ( MAT( 1, 0 ) * MAT( 2, 1 ) - MAT( 1, 1 ) * MAT( 2, 0 ) ); + + if( fabs( det ) < TOO_SMALL ){ + im_error( function_name, "singular or near-singular matrix" ); + return -1; + } + INV( 0, 0 )= ( MAT( 1, 1 ) * MAT( 2, 2 ) - MAT( 1, 2 ) * MAT( 2, 1 ) ) / det; + INV( 0, 1 )= ( MAT( 0, 2 ) * MAT( 2, 1 ) - MAT( 0, 1 ) * MAT( 2, 2 ) ) / det; + INV( 0, 2 )= ( MAT( 0, 1 ) * MAT( 1, 2 ) - MAT( 0, 2 ) * MAT( 1, 1 ) ) / det; + + INV( 1, 0 )= ( MAT( 1, 2 ) * MAT( 2, 0 ) - MAT( 1, 0 ) * MAT( 2, 2 ) ) / det; + INV( 1, 1 )= ( MAT( 0, 0 ) * MAT( 2, 2 ) - MAT( 0, 2 ) * MAT( 2, 0 ) ) / det; + INV( 1, 2 )= ( MAT( 0, 2 ) * MAT( 1, 0 ) - MAT( 0, 0 ) * MAT( 1, 2 ) ) / det; + + INV( 2, 0 )= ( MAT( 1, 0 ) * MAT( 2, 1 ) - MAT( 1, 1 ) * MAT( 2, 0 ) ) / det; + INV( 2, 1 )= ( MAT( 0, 1 ) * MAT( 2, 0 ) - MAT( 0, 0 ) * MAT( 2, 1 ) ) / det; + INV( 2, 2 )= ( MAT( 0, 0 ) * MAT( 1, 1 ) - MAT( 0, 1 ) * MAT( 1, 0 ) ) / det; + + return 0; + } + default: + return -1; + } + +#undef N +#undef MAT +#undef INV +} + diff --git a/libsrc/matrix/im_matmul.c b/libsrc/matrix/im_matmul.c new file mode 100644 index 00000000..98c9eb59 --- /dev/null +++ b/libsrc/matrix/im_matmul.c @@ -0,0 +1,107 @@ +/* @(#) Multiplies two DOUBLEMASKs. Result matrix is made and returned. + * @(#) Pass the filename to set for the output. + * @(#) + * @(#) DOUBLEMASK * + * @(#) im_matmul( in1, in2, name ) + * @(#) DOUBLEMASK *in1, *in2; + * @(#) char *name; + * @(#) + * @(#) NULL for error. + * + * Copyright: 1990, K. Martinez and J. Cupitt + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* MATRIX MULTIPLY? + */ +DOUBLEMASK * +im_matmul( DOUBLEMASK *in1, DOUBLEMASK *in2, const char *name ) +{ + int xc, yc, col; + double sum; + DOUBLEMASK *mat; + double *out, *a, *b; + double *s1, *s2; + + /* Check matrix sizes. + */ + if( in1->xsize != in2->ysize ) { + im_errormsg( "im_matmul: bad sizes" ); + return( NULL ); + } + + /* Allocate output matrix. + */ + if( !(mat = im_create_dmask( name, in2->xsize, in1->ysize )) ) { + im_errormsg( "im_matmul: unable to allocate output mask" ); + return( NULL ); + } + + /* Multiply. + */ + out = mat->coeff; + s1 = in1->coeff; + + for( yc = 0; yc < in1->ysize; yc++ ) { + s2 = in2->coeff; + + for( col = 0; col < in2->xsize; col++ ) { + /* Get ready to sweep a row. + */ + sum = 0.0; + a = s1; + b = s2; + + for( sum = 0.0, xc = 0; xc < in1->xsize; xc++ ) { + sum += *a++ * *b; + b += in2->xsize; + } + + *out++ = sum; + s2++; + } + + s1 += in1->xsize; + } + + return( mat ); +} diff --git a/libsrc/matrix/im_mattrn.c b/libsrc/matrix/im_mattrn.c new file mode 100644 index 00000000..fd6549c2 --- /dev/null +++ b/libsrc/matrix/im_mattrn.c @@ -0,0 +1,87 @@ +/* @(#) Transpose a mask. Result mask is made and returned. Pass in the name + * @(#) to set for the output mask. + * @(#) + * @(#) DOUBLEMASK * + * @(#) im_mattrn( in, name ); + * @(#) DOUBLEMASK *in; + * @(#) char *name; + * @(#) + * @(#) NULL for error. + * + * Copyright: 1990, K. Martinez and J. Cupitt + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* MATRIX TRANSPOSE?? + */ +DOUBLEMASK * +im_mattrn( DOUBLEMASK *in, const char *name ) +{ + int xc, yc; + DOUBLEMASK *mat; + double *out, *a, *b; + + /* Allocate output matrix. + */ + if( !(mat = im_create_dmask( name, in->ysize, in->xsize )) ) { + im_errormsg( "im_mattrn: unable to allocate output matrix" ); + return( NULL ); + } + + /* Transpose. + */ + out = mat->coeff; + a = in->coeff; + + for( yc = 0; yc < mat->ysize; yc++ ) { + b = a; + + for( xc = 0; xc < mat->xsize; xc++ ) { + *out++ = *b; + b += in->xsize; + } + + a++; + } + + return( mat ); +} diff --git a/libsrc/matrix/man3/Makefile.am b/libsrc/matrix/man3/Makefile.am new file mode 100644 index 00000000..e2e1c0b2 --- /dev/null +++ b/libsrc/matrix/man3/Makefile.am @@ -0,0 +1,11 @@ +man_MANS = \ +im_lu_decomp.3 \ +im_lu_solve.3 \ +im_matcat.3 \ +im_matinv.3 \ +im_matinv_inplace.3 \ +im_matmul.3 \ +im_mattrn.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/matrix/man3/im_lu_decomp.3 b/libsrc/matrix/man3/im_lu_decomp.3 new file mode 100644 index 00000000..05a51f7b --- /dev/null +++ b/libsrc/matrix/man3/im_lu_decomp.3 @@ -0,0 +1,75 @@ +.TH IM_LU_DECOMP 3 "18 September 2006" +.SH NAME + im_lu_decomp, im_lu_solve \- Solve SLEs by LU decomposition +.SH SYNOPSIS +.nf +.B #include +.sp +.BI "DOUBLEMASK *im_lu_decomp( const DOUBLEMASK " "*mat" ", const char " "*name" " ); +.br + +.BI "int im_lu_solve( const DOUBLEMASK " "*lu" ", double " "*vec" " ); +.fi +.SH DESCRIPTION +.B im_lu_decomp(3) +allocates a DOUBLEMASK representing the LU decomposition of the matrix in +DOUBLEMASK +.IR "*mat" ", +and gives it the filename member +.IR "name" ". +.PP +.B im_lu_solve(3) +solves the system of linear equations (SLE) Ax=b, where matrix A has already +been decomposed into LU form in DOUBLEMASK +.IR "*lu" ". +Input vector b is in +.I *vec +and is overwritten with output vector x. +.PP +DOUBLEMASK +.I *lu +is unaltered by +.BR "im_matinv(3)" ", +and can be used again to solve a different SLE containing matrix A. +.SH NOTES +The scale and offset members of +.I *mat +are ignored. If they are not set to 1.0 and zero respectively, you must first +call +.BR "im_norm_dmask(3)" ". +.PP +To understand the decomposition A=LU, see Press et al. (1992). For the exact +format used to represent the matrices L and U in +.IR "*lu" ", +see the acompanying source code. +.SH ERRORS +If matrix +.I *mat +is singular (non-invertible), or close to singular then +.B im_lu_decomp(3) +will fail, calling +.BR "im_error(3)" ". +.SH RETURN VALUE +.B im_lu_decomp(3) +returns a pointer to the new DOUBLEMASK, or NULL on error. +.PP +.B im_lu_solve(3) +always returns zero, unless +.I lu +was not returned by +.BR "im_lu_decomp(3)" ", +when it returns -1. +.SH SEE ALSO +im_create_dmask(3), im_free_dmask(3), im_norm_dmask(3), im_matinv(3) +.SH REFERENCES +PRESS, W. et al, 1992. Numerical Recipies in C; The Art of Scientific +Computing, 2nd ed. Cambridge: Cambridge University Press, pp. 43-50. +.PP +[Available online: +http://www.library.cornell.edu/nr/bookcpdf.html accessed 2006-09-19] +.SH COPYRIGHT +.br +Copyright 2006, Tom Vajzovic. +.SH AUTHOR +Tom Vajzovic + diff --git a/libsrc/matrix/man3/im_lu_solve.3 b/libsrc/matrix/man3/im_lu_solve.3 new file mode 100644 index 00000000..cfe8df4e --- /dev/null +++ b/libsrc/matrix/man3/im_lu_solve.3 @@ -0,0 +1,2 @@ +.so man3/im_lu_decomp.3 + diff --git a/libsrc/matrix/man3/im_matcat.3 b/libsrc/matrix/man3/im_matcat.3 new file mode 100644 index 00000000..20d77a80 --- /dev/null +++ b/libsrc/matrix/man3/im_matcat.3 @@ -0,0 +1 @@ +.so man3/im_matinv.3 diff --git a/libsrc/matrix/man3/im_matinv.3 b/libsrc/matrix/man3/im_matinv.3 new file mode 100644 index 00000000..147ab6c4 --- /dev/null +++ b/libsrc/matrix/man3/im_matinv.3 @@ -0,0 +1,90 @@ +.TH IM_MATINV 3 "2 May 1991" +.SH NAME +im_matinv, im_matmul, im_mattrn \- matrix operations on DOUBLEMASKs + +.SH SYNOPSIS +.B #include + +.B DOUBLEMASK *im_matinv( const DOUBLEMASK *in, const char *name ); + +.B int im_matinv_inplace( DOUBLEMASK *mat ); + +.B DOUBLEMASK *im_matmul( in1, in2, name ) +.br +.B DOUBLEMASK *in1, *in2; +.br +.B char *name; + +.B DOUBLEMASK *im_matcat( in1, in2, name ) +.br +.B DOUBLEMASK *in1, *in2; +.br +.B char *name; + +.B DOUBLEMASK *im_mattrn( in, name ) +.br +.B DOUBLEMASK *in; +.br +.B char *name; + +.SH DESCRIPTION +These functions treat DOUBLEMASKs as matricies, performing some of the basics +of matrix algebra on them. + +There should be more matrix functions: those implemeneted are just sufficient +for the Gallery's calibration routines. im_matadd, im_matidentity should +really be added. + +None of these functions damage their arguments, except +.BR "im_matinv_inplace(3)" ". + +.B im_matinv(3) +inverts DOUBLEMASK +.IR "in" ", +returning a new DOUBLEMASK, called +.IR "name" ", +which contains the inverse of in. If no inverse exists, NULL is returned and +.B im_error(3) +is called with a diagnostic message. + +.B im_matinv_inplace(3) +is as +.B im_matinv(3) +except that it overwrites its input. + +.B im_matmul() +multiples the matrices held in in1 and in2, returning their product in a +matrix called name. + +.B im_matcat() +returns a new matrix formed by appending matrix in2 to the end of matrix in1. +The two matricies must be the same width. It is useful for combining several +im_measure()s into a single matrix. + +.B im_mattrn() +transposes matrix in, returning the transpose in new matrix called name. +.SH NOTES +.B DO NOT +use matrix inversion to solve systems of linear equations (SLEs). The +routines +.B im_lu_decomp(3) +and +.B im_lu_solve(3) +are more efficient, even for a single SLE. + +.SH RETURN VALUE +The functions returns a new DOUBLEMASK on sucess, and NULL on failure. +.PP +.B im_matinv_inplace(3) +returns zero on success, and -1 on failure. + +.SH SEE\ ALSO +im_create_dmask(3), im_measure(3), etc. im_lu_decomp(3), im_lu_solve(3) + +.SH COPYRIGHT +National Gallery, 1992. Tom Vajzovic, 2006 +.SH AUTHORS +J. Cupitt +.br +Tom Vajzovic + diff --git a/libsrc/matrix/man3/im_matinv_inplace.3 b/libsrc/matrix/man3/im_matinv_inplace.3 new file mode 100644 index 00000000..9f466546 --- /dev/null +++ b/libsrc/matrix/man3/im_matinv_inplace.3 @@ -0,0 +1,2 @@ +.so man3/im_matinv.3 + diff --git a/libsrc/matrix/man3/im_matmul.3 b/libsrc/matrix/man3/im_matmul.3 new file mode 100644 index 00000000..20d77a80 --- /dev/null +++ b/libsrc/matrix/man3/im_matmul.3 @@ -0,0 +1 @@ +.so man3/im_matinv.3 diff --git a/libsrc/matrix/man3/im_mattrn.3 b/libsrc/matrix/man3/im_mattrn.3 new file mode 100644 index 00000000..20d77a80 --- /dev/null +++ b/libsrc/matrix/man3/im_mattrn.3 @@ -0,0 +1 @@ +.so man3/im_matinv.3 diff --git a/libsrc/matrix/matalloc.c b/libsrc/matrix/matalloc.c new file mode 100644 index 00000000..e1e11219 --- /dev/null +++ b/libsrc/matrix/matalloc.c @@ -0,0 +1,241 @@ +/* @(#) Programs for allocating and freeing matrices + * @(#) pages 705- of numerical recipes in C 1998 + * @(#) + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define TINY 1.0e-200 + +/* @(#) Allocates and returns an pointer at the beginning of + * @(#) an integer array array[nl,nh] or + * @(#) float array array[nl,nh] or + * @(#) double array array[nl,nh] + * @(#) + * @(#) Right call + * @(#) int *im_ivector(nl, nh) + * @(#) int nl, nh; + * @(#) returns a pointer to an int array or NULL on error + * @(#) + * @(#) Right call + * @(#) float *im_fvector(nl, nh) + * @(#) int nl, nh; + * @(#) returns a pointer to a float array or NULL on error + * @(#) + * @(#) Right call + * @(#) double *im_dvector(nl, nh) + * @(#) int nl, nh; + * @(#) returns a pointer to a double array or NULL on error + * @(#) + * @(#) The following functions free the array allocated by the functions above + * @(#) + * @(#) void im_free_ivector(v, nl, nh) + * @(#) int *v; + * @(#) int nl, nh; + * @(#) + * @(#) void im_free_fvector(v, nl, nh) + * @(#) float *v; + * @(#) int nl, nh; + * @(#) + * @(#) void im_free_dvector(v, nl, nh) + * @(#) double *v; + * @(#) int nl, nh; + * @(#) + */ + +int * +im_ivector(nl, nh) +int nl, nh; +{ + int *v; + + v = (int *)im_malloc(NULL,(unsigned)(nh - nl + 1) * sizeof(int)); + if (v == NULL) + return(NULL); + else + return(v-nl); +} + +float *im_fvector(nl, nh) +int nl, nh; +{ + float *v; + + v = (float *)im_malloc(NULL,(unsigned)(nh - nl + 1) * sizeof(float)); + if (v == NULL) + return(NULL); + else + return(v-nl); +} + +double *im_dvector(nl, nh) +int nl, nh; +{ + double *v; + + v = (double *)im_malloc(NULL,(unsigned)(nh - nl + 1) * sizeof(double)); + if (v == NULL) + return(NULL); + else + return(v-nl); +} + +void im_free_ivector(v, nl, nh) +int *v; +int nl, nh; +{ + im_free((char*) (v+nl)); +} + +void im_free_fvector(v, nl, nh) +float *v; +int nl, nh; +{ + im_free((char*) (v+nl)); +} + +void im_free_dvector(v, nl, nh) +double *v; +int nl, nh; +{ + im_free((char*) (v+nl)); +} + +/* @(#) Allocates and returns an pointer at the beginning of + * @(#) an int, float or double, two dimensional matrix[nrl,nrh][ncl,nch] + * @(#) + * @(#) Right call + * @(#) int **im_imat_alloc(nrl, nrh, ncl, nch) + * @(#) int nrl, nrh, ncl, nch; + * @(#) returns a pointer to an int matrix or NULL on error + * @(#) + * @(#) float **im_fmat_alloc(nrl, nrh, ncl, nch) + * @(#) int nrl, nrh, ncl, nch; + * @(#) returns a pointer to an int matrix or NULL on error + * @(#) + * @(#) double **im_dmat_alloc(nrl, nrh, ncl, nch) + * @(#) int nrl, nrh, ncl, nch; + * @(#) returns a pointer to a double matrix or NULL on error + * @(#) + * @(#) The following routines free the matrix allocated by the functions above + * @(#) void im_free_imat(m, nrl, nrh, ncl, nch) + * @(#) int **m; + * @(#) int nrl, nrh, ncl, nch; + * @(#) + * @(#) void im_free_fmat(m, nrl, nrh, ncl, nch) + * @(#) float **m; + * @(#) int nrl, nrh, ncl, nch; + * @(#) + * @(#) void im_free_dmat(m, nrl, nrh, ncl, nch) + * @(#) double **m; + * @(#) int nrl, nrh, ncl, nch; + * @(#) + */ +int **im_imat_alloc(nrl, nrh, ncl, nch) +int nrl, nrh, ncl, nch; +{ + int i; + int **m; + + m = (int**)im_malloc(NULL,(unsigned)(nrh-nrl+1) * sizeof(int *)); + if (m == NULL) + return(NULL); + m -= nrl; + + for (i=nrl; i<=nrh; i++) + { + m[i] = (int *)im_malloc(NULL,(unsigned) (nch-ncl+1) * sizeof(int)); + if (m[i] == NULL) + return(NULL); + m[i] -= ncl; + } + return (m); +} + +void im_free_imat(m, nrl, nrh, ncl, nch) +int **m; +int nrl, nrh, ncl, nch; +{ + int i; + + for (i=nrh; i>=nrl; i--) + im_free((char*) (m[i]+ncl)); + im_free((char*) (m+nrl)); +} + +float **im_fmat_alloc(nrl, nrh, ncl, nch) +int nrl, nrh, ncl, nch; +{ + int i; + float **m; + + m = (float**)im_malloc(NULL,(unsigned)(nrh-nrl+1) * sizeof(float *)); + if (m == NULL) + return(NULL); + m -= nrl; + + for (i=nrl; i<=nrh; i++) + { + m[i] = (float *)im_malloc(NULL,(unsigned) (nch-ncl+1) * sizeof(float)); + if (m[i] == NULL) + return(NULL); + m[i] -= ncl; + } + return (m); +} + +void im_free_fmat(m, nrl, nrh, ncl, nch) +float **m; +int nrl, nrh, ncl, nch; +{ + int i; + + for (i=nrh; i>=nrl; i--) + im_free((char*) (m[i]+ncl)); + im_free((char*) (m+nrl)); +} + +double **im_dmat_alloc(nrl, nrh, ncl, nch) +int nrl, nrh, ncl, nch; +{ + int i; + double **m; + + m = (double**)im_malloc(NULL,(unsigned)(nrh-nrl+1) * sizeof(double *)); + if (m == NULL) + return(NULL); + m -= nrl; + + for (i=nrl; i<=nrh; i++) + { + m[i] = (double *)im_malloc(NULL,(unsigned) (nch-ncl+1) * sizeof(double)); + if (m[i] == NULL) + return(NULL); + m[i] -= ncl; + } + return (m); +} + +void im_free_dmat(m, nrl, nrh, ncl, nch) +double **m; +int nrl, nrh, ncl, nch; +{ + int i; + + for (i=nrh; i>=nrl; i--) + im_free((char*) (m[i]+ncl)); + im_free((char*) (m+nrl)); +} diff --git a/libsrc/matrix/matrix_dispatch.c b/libsrc/matrix/matrix_dispatch.c new file mode 100644 index 00000000..e6608fda --- /dev/null +++ b/libsrc/matrix/matrix_dispatch.c @@ -0,0 +1,181 @@ +/* VIPS function dispatch tables for matricies. + * + * J. Cupitt, 14/2/95. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* One matrix in, one out. + */ +static im_arg_desc one_in_one_out[] = { + IM_INPUT_DMASK( "in" ), + IM_OUTPUT_DMASK( "out" ) +}; + +/* Two matricies in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_DMASK( "in1" ), + IM_INPUT_DMASK( "in2" ), + IM_OUTPUT_DMASK( "out" ) +}; + +/* Call im_matinv via arg vector. + */ +static int +matinv_vec( im_object *argv ) +{ + im_mask_object *in = argv[0]; + im_mask_object *out = argv[1]; + + if( !(out->mask = + im_matinv( in->mask, out->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_matinv. + */ +static im_function matinv_desc = { + "im_matinv", /* Name */ + "invert matrix", + 0, /* Flags */ + matinv_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_mattrn via arg vector. + */ +static int +mattrn_vec( im_object *argv ) +{ + im_mask_object *in = argv[0]; + im_mask_object *out = argv[1]; + + if( !(out->mask = + im_mattrn( in->mask, out->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_mattrn. + */ +static im_function mattrn_desc = { + "im_mattrn", /* Name */ + "transpose matrix", + 0, /* Flags */ + mattrn_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_matcat via arg vector. + */ +static int +matcat_vec( im_object *argv ) +{ + im_mask_object *in1 = argv[0]; + im_mask_object *in2 = argv[1]; + im_mask_object *out = argv[2]; + + if( !(out->mask = + im_matcat( in1->mask, in2->mask, out->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_matcat. + */ +static im_function matcat_desc = { + "im_matcat", /* Name */ + "append matrix in2 to the end of matrix in1", + 0, /* Flags */ + matcat_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_matmul via arg vector. + */ +static int +matmul_vec( im_object *argv ) +{ + im_mask_object *in1 = argv[0]; + im_mask_object *in2 = argv[1]; + im_mask_object *out = argv[2]; + + if( !(out->mask = + im_matmul( in1->mask, in2->mask, out->name )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_matmul. + */ +static im_function matmul_desc = { + "im_matmul", /* Name */ + "multiply matrix in1 by matrix in2", + 0, /* Flags */ + matmul_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *matrix_list[] = { + &matcat_desc, + &matinv_desc, + &matmul_desc, + &mattrn_desc +}; + +/* Package of functions. + */ +im_package im__matrix = { + "matrix", + IM_NUMBER( matrix_list ), + matrix_list +}; diff --git a/libsrc/morphology/Makefile.am b/libsrc/morphology/Makefile.am new file mode 100644 index 00000000..1784ef6f --- /dev/null +++ b/libsrc/morphology/Makefile.am @@ -0,0 +1,12 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libmorphology.la + +libmorphology_la_SOURCES = \ + im_cntlines.c \ + im_dilate.c\ + im_erode.c\ + morph_dispatch.c \ + im_profile.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/morphology/im_cntlines.c b/libsrc/morphology/im_cntlines.c new file mode 100644 index 00000000..f02b3355 --- /dev/null +++ b/libsrc/morphology/im_cntlines.c @@ -0,0 +1,131 @@ +/* @(#) Function which calculates the number of transitions + * @(#) between black and white for the horizontal or the vertical + * @(#) direction of an image. black<128 , white>=128 + * @(#) The function calculates the number of transitions for all + * @(#) Xsize or Ysize and returns the mean of the result + * @(#) Input should be binary one channel + * @(#) + * @(#) int im_cntlines(im, nolines, flag) + * @(#) IMAGE *im; + * @(#) float *nolines; + * @(#) int flag; 0 to cnt horizontal and 1 to count vertical lines + * @(#) + * @(#) Returns 0 on success and -1 on error + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : + * + * 19/9/95 JC + * - tidied up + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_cntlines( IMAGE *im, double *nolines, int flag ) +{ + int x, y; + PEL *line; + int cnt; + + /* Is im valid? + */ + if( im_incheck( im ) ) + return( -1 ); + if( im->Coding != IM_CODING_NONE || im->BandFmt != IM_BANDFMT_UCHAR || + im->Bands != 1 ) { + im_errormsg( "im_cntlines: 1-band uchar uncoded only" ); + return( -1 ); + } + if( flag != 0 && flag != 1 ) { + im_errormsg( "im_cntlines: flag should be 0 (horizontal) " + "or 1 (vertical)" ); + return( -1 ); + } + + line = (PEL *) im->data; + if( flag == 1 ) { + /* Count vertical lines. + */ + for( cnt = 0, y = 0; y < im->Ysize; y++ ) { + PEL *p = line; + + for( x = 0; x < im->Xsize - 1; x++ ) { + if( p[0] < (PEL)128 && p[1] >= (PEL)128 ) + cnt++; + else if( p[0] >= (PEL)128 && p[1] < (PEL)128 ) + cnt++; + + p++; + } + + line += im->Xsize; + } + + *nolines = (float) cnt / (2.0 * im->Ysize); + } + else { + /* Count horizontal lines. + */ + for( cnt = 0, y = 0; y < im->Ysize - 1; y++ ) { + PEL *p1 = line; + PEL *p2 = line + im->Xsize; + + for( x = 0; x < im->Xsize; x++ ) { + if( *p1 < (PEL)128 && *p2 >= (PEL)128 ) + cnt++; + else if( *p1 >= (PEL)128 && *p2 < (PEL)128 ) + cnt++; + + p1++; + p2++; + } + + line += im->Xsize; + } + + *nolines = (float)cnt / (2.0 * im->Xsize); + } + + return( 0 ); +} diff --git a/libsrc/morphology/im_dilate.c b/libsrc/morphology/im_dilate.c new file mode 100644 index 00000000..c4af9f50 --- /dev/null +++ b/libsrc/morphology/im_dilate.c @@ -0,0 +1,313 @@ +/* @(#) Function which dilates a binary VASARI format picture with a mask. + * @(#) The mask coefficients are either 255 (object) or 0 (bk) or 128 (any). + * @(#) Input image are binary images with either 0 or 255 values, one channel + * @(#) only. The program dilates a white object on a black background. + * @(#) The center of the mask is at location (m->xsize/2, m->ysize/2) + * @(#) integer division. The mask is expected to have an odd width and + * @(#) height. + * @(#) + * @(#) int im_dilate(in, out, m) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *m; + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * + * 19/9/95 JC + * - rewritten + * 6/7/99 JC + * - small tidies + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our sequence value. + */ +typedef struct { + REGION *ir; /* Input region */ + + int *soff; /* Offsets we check for set */ + int ss; /* ... and number we check for set */ + int *coff; /* Offsets we check for clear */ + int cs; /* ... and number we check for clear */ +} SeqInfo; + +/* Stop function. + */ +static int +stop_dilate( SeqInfo *seq, IMAGE *in ) +{ + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Start function. + */ +static void * +start_dilate( IMAGE *out, IMAGE *in, INTMASK *msk ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + int sz = msk->xsize * msk->ysize; + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->soff = NULL; + seq->ss = 0; + seq->coff = NULL; + seq->cs = 0; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->soff = IM_ARRAY( out, sz, int ); + seq->coff = IM_ARRAY( out, sz, int ); + if( !seq->ir || !seq->soff || !seq->coff ) { + stop_dilate( seq, in ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* Dilate! + */ +static int +gen_dilate( REGION *or, SeqInfo *seq, IMAGE *in, INTMASK *msk ) +{ + REGION *ir = seq->ir; + + int *soff = seq->soff; + int *coff = seq->coff; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int *t; + + int x, y; + int found, i; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += msk->xsize - 1; + s.height += msk->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Scan mask, building offsets we check when processing. + */ + seq->ss = 0; + seq->cs = 0; + for( t = msk->coeff, y = 0; y < msk->ysize; y++ ) + for( x = 0; x < msk->xsize; x++, t++ ) + switch( *t ) { + case 255: + soff[seq->ss++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + break; + + case 128: + break; + + case 0: + coff[seq->cs++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + break; + + default: + im_errormsg( "im_dilate: bad mask element " + "(%d should be 0, 128 or 255)", *t ); + return( -1 ); + } + + /* Dilate! + */ + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Loop along line. + */ + for( x = 0; x < sz; x++, q++, p++ ) { + /* Search for a hit on the set list. + */ + found = 0; + for( i = 0; i < seq->ss; i++ ) + if( p[soff[i]] ) { + /* Found a match! Set this output + * pixel and continue. + */ + *q = 255; + found = 1; + break; + } + + /* No set pixels ... search for a hit in the clear + * pixels. + */ + if( !found ) + for( i = 0; i < seq->cs; i++ ) + if( !p[coff[i]] ) { + /* Found a match! Set this + * output pixel and continue. + */ + *q = 255; + found = 1; + break; + } + + if( !found ) + /* All matches failed. Clear this output pixel. + */ + *q = 0; + + } + } + + return( 0 ); +} + +/* Dilate an image. + */ +int +im_dilate_raw( IMAGE *in, IMAGE *out, INTMASK *m ) +{ + INTMASK *msk; + + /* Check mask has odd number of elements in width and height. + */ + if( m->xsize < 1 || !(m->xsize & 0x1) || + m->ysize < 1 || !(m->ysize & 0x1) ) { + im_errormsg( "im_dilate: mask size not odd" ); + return( -1 ); + } + + /* Standard checks. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bbits != 8 || + in->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_dilate: uchar uncoded only" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= m->xsize - 1; + out->Ysize -= m->ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_dilate: image too small for mask" ); + return( -1 ); + } + + /* Take a copy of m. + */ + if( !(msk = im_dup_imask( m, "conv_mask" )) ) + return( -1 ); + if( im_add_close_callback( out, + (im_callback_fn) im_free_imask, msk, NULL ) ) { + im_free_imask( msk ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, start_dilate, gen_dilate, stop_dilate, in, msk ) ) + return( -1 ); + + out->Xoffset = -m->xsize / 2; + out->Yoffset = -m->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_dilate( IMAGE *in, IMAGE *out, INTMASK *m ) +{ + IMAGE *t1 = im_open_local( out, "im_dilate:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, m->xsize / 2, m->ysize / 2, + in->Xsize + m->xsize - 1, + in->Ysize + m->ysize - 1 ) || + im_dilate_raw( t1, out, m ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/morphology/im_erode.c b/libsrc/morphology/im_erode.c new file mode 100644 index 00000000..56683c6e --- /dev/null +++ b/libsrc/morphology/im_erode.c @@ -0,0 +1,311 @@ +/* @(#) Function which erodes a binary VASARI format picture with a mask. + * @(#) The mask coefficients are either 255 (object) or 0 (bk) or 128 (any). + * @(#) Input image are binary images with either 0 or 255 values, one channel + * @(#) only. The program erodes a white object on a black background. + * @(#) The center of the mask is at location (m->xsize/2, m->ysize/2) + * @(#) integer division. The mask is expected to have an odd width and + * @(#) height. + * @(#) + * @(#) int im_erode(in, out, m) + * @(#) IMAGE *in, *out; + * @(#) INTMASK *m; + * @(#) + * @(#) Returns either 0 (sucess) or -1 (fail) + * + * 19/9/95 JC + * - rewrite + * 6/7/99 JC + * - checks and small tidies + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Our sequence value. + */ +typedef struct { + REGION *ir; /* Input region */ + + int *soff; /* Offsets we check for set */ + int ss; /* ... and number we check for set */ + int *coff; /* Offsets we check for clear */ + int cs; /* ... and number we check for clear */ +} SeqInfo; + +/* Stop function. + */ +static int +stop_erode( SeqInfo *seq, IMAGE *in ) +{ + /* Free attached objects. + */ + if( seq->ir ) { + im_region_free( seq->ir ); + seq->ir = NULL; + } + + return( 0 ); +} + +/* Start function. + */ +static void * +start_erode( IMAGE *out, IMAGE *in, INTMASK *msk ) +{ + SeqInfo *seq = IM_NEW( out, SeqInfo ); + int sz = msk->xsize * msk->ysize; + + if( !seq ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->soff = NULL; + seq->ss = 0; + seq->coff = NULL; + seq->cs = 0; + + /* Attach region and arrays. + */ + seq->ir = im_region_create( in ); + seq->soff = IM_ARRAY( out, sz, int ); + seq->coff = IM_ARRAY( out, sz, int ); + if( !seq->ir || !seq->soff || !seq->coff ) { + stop_erode( seq, in ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* Erode! + */ +static int +gen_erode( REGION *or, SeqInfo *seq, IMAGE *in, INTMASK *msk ) +{ + REGION *ir = seq->ir; + + int *soff = seq->soff; + int *coff = seq->coff; + + Rect *r = &or->valid; + Rect s; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + int sz = IM_REGION_N_ELEMENTS( or ); + + int *t; + + int x, y; + int found, i; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += msk->xsize - 1; + s.height += msk->ysize - 1; + if( im_prepare( ir, &s ) ) + return( -1 ); + + /* Scan mask, building offsets we check when processing. + */ + seq->ss = 0; + seq->cs = 0; + for( t = msk->coeff, y = 0; y < msk->ysize; y++ ) + for( x = 0; x < msk->xsize; x++, t++ ) + switch( *t ) { + case 255: + soff[seq->ss++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + break; + + case 128: + break; + + case 0: + coff[seq->cs++] = + IM_REGION_ADDR( ir, x + le, y + to ) - + IM_REGION_ADDR( ir, le, to ); + break; + + default: + im_errormsg( "im_erode: bad mask element " + "(%d should be 0, 128 or 255)", *t ); + return( -1 ); + } + + /* Erode! + */ + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + /* Loop along line. + */ + for( x = 0; x < sz; x++, q++, p++ ) { + /* Check all set pixels are set. + */ + found = 0; + for( i = 0; i < seq->ss; i++ ) + if( !p[soff[i]] ) { + /* Found a mismatch! Clear this output + * pixel and continue. + */ + *q = 0; + found = 1; + break; + } + + /* Check all clear pixels are clear. + */ + if( !found ) + for( i = 0; i < seq->cs; i++ ) + if( p[coff[i]] ) { + /* Found a mismatch! Clear this + * output pixel and continue. + */ + *q = 0; + found = 1; + break; + } + + if( !found ) + /* No mismatches found - set output pixel. + */ + *q = 255; + } + } + + return( 0 ); +} + +/* Erode an image. + */ +int +im_erode_raw( IMAGE *in, IMAGE *out, INTMASK *m ) +{ + INTMASK *msk; + + /* Check mask has odd number of elements in width and height. + */ + if( m->xsize < 1 || !(m->xsize & 0x1) || + m->ysize < 1 || !(m->ysize & 0x1) ) { + im_errormsg( "im_erode: mask size not odd" ); + return( -1 ); + } + + /* Standard checks. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->Bbits != 8 || + in->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_erode: 1-band uchar uncoded only" ); + return( -1 ); + } + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output + * would be 1x1. + */ + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize -= m->xsize - 1; + out->Ysize -= m->ysize - 1; + if( out->Xsize <= 0 || out->Ysize <= 0 ) { + im_errormsg( "im_erode: image too small for mask" ); + return( -1 ); + } + + /* Take a copy of m. + */ + if( !(msk = im_dup_imask( m, "conv_mask" )) ) + return( -1 ); + if( im_add_close_callback( out, + (im_callback_fn) im_free_imask, msk, NULL ) ) { + im_free_imask( msk ); + return( -1 ); + } + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, start_erode, gen_erode, stop_erode, in, msk ) ) + return( -1 ); + + out->Xoffset = -m->xsize / 2; + out->Yoffset = -m->ysize / 2; + + return( 0 ); +} + +/* The above, with a border to make out the same size as in. + */ +int +im_erode( IMAGE *in, IMAGE *out, INTMASK *m ) +{ + IMAGE *t1 = im_open_local( out, "im_erode:1", "p" ); + + if( !t1 || + im_embed( in, t1, 1, m->xsize / 2, m->ysize / 2, + in->Xsize + m->xsize - 1, + in->Ysize + m->ysize - 1 ) || + im_erode_raw( t1, out, m ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libsrc/morphology/im_profile.c b/libsrc/morphology/im_profile.c new file mode 100644 index 00000000..6d7dd700 --- /dev/null +++ b/libsrc/morphology/im_profile.c @@ -0,0 +1,132 @@ +/* @(#) dir = 0: + * @(#) For each vertical line, find the position of the first + * @(#) non-zero pixel from the top. Output is USHORT with + * @(#) width = input width, height = 1. + * @(#) + * @(#) dir = 1: + * @(#) For each horizontal line, find the position of the first + * @(#) non-zero pixel from the left. Output is USHORT with + * @(#) width = 1, height = input height + * @(#) + * @(#) int im_profile( IMAGE *in, IMAGE *out, int dir ) + * @(#) + * @(#) Returns 0 on success and non-zero on error + * + * 11/8/99 JC + * - from im_cntlines() + * 22/4/04 + * - now outputs horizontal/vertical image + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_profile( IMAGE *in, IMAGE *out, int dir ) +{ + int x, y; + unsigned short *buf; + + /* Check im. + */ + if( im_iocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE || in->BandFmt != IM_BANDFMT_UCHAR || + in->Bands != 1 ) { + im_errormsg( "im_profile: 1-band uchar uncoded only" ); + return( -1 ); + } + if( dir != 0 && dir != 1 ) { + im_errormsg( "im_profile: dir not 0 or 1" ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Type = IM_TYPE_HISTOGRAM; + if( dir == 0 ) { + out->Xsize = in->Xsize; + out->Ysize = 1; + } + else { + out->Xsize = 1; + out->Ysize = in->Ysize; + } + out->BandFmt = IM_BANDFMT_USHORT; + out->Bbits = IM_BBITS_SHORT; + if( im_setupout( out ) ) + return( -1 ); + if( !(buf = IM_ARRAY( out, out->Xsize, unsigned short )) ) + return( -1 ); + + if( dir == 0 ) { + /* Find vertical lines. + */ + for( x = 0; x < in->Xsize; x++ ) { + PEL *p = (PEL *) IM_IMAGE_ADDR( in, x, 0 ); + int lsk = IM_IMAGE_SIZEOF_LINE( in ); + + for( y = 0; y < in->Ysize; y++ ) + if( p[y * lsk] ) + break; + + buf[x] = y; + } + + if( im_writeline( 0, out, (PEL *) buf ) ) + return( -1 ); + } + else { + /* Count horizontal lines. + */ + for( y = 0; y < in->Ysize; y++ ) { + PEL *p = (PEL *) IM_IMAGE_ADDR( in, 0, y ); + + for( x = 0; x < in->Xsize; x++ ) + if( p[x] ) + break; + + buf[0] = x; + + if( im_writeline( y, out, (PEL *) buf ) ) + return( -1 ); + } + } + + + return( 0 ); +} diff --git a/libsrc/morphology/man3/Makefile.am b/libsrc/morphology/man3/Makefile.am new file mode 100644 index 00000000..89e85ccd --- /dev/null +++ b/libsrc/morphology/man3/Makefile.am @@ -0,0 +1,9 @@ +man_MANS = \ + im_cntlines.3 \ + im_dilate.3 \ + im_dilate_raw.3 \ + im_erode.3 \ + im_erode_raw.3 \ + im_profile.3 + +EXTRA_DIST = ${man_MANS} diff --git a/libsrc/morphology/man3/im_cntlines.3 b/libsrc/morphology/man3/im_cntlines.3 new file mode 100644 index 00000000..6fe446b7 --- /dev/null +++ b/libsrc/morphology/man3/im_cntlines.3 @@ -0,0 +1,57 @@ +.TH LINES 3 "14 May 1991" +.SH NAME +im_cntlines, im_profile \- calculate transitions between black and white pels horizontally or vertically +.SH SYNOPSIS +.B #include + +.B int im_profile(in, out, dir) +.br +.B IMAGE *in, *out; +.br +.B int dir; + +.B int im_cntlines(in, nolines, dir) +.br +.B IMAGE *in; +.br +.B double *nolines; +.br +.B int dir; + +.SH DESCRIPTION + +.B im_profile(3) +searches inward from the edge of the image and finds the first non-zero pixel. +It outputs an image containing a list of the offsets for each row or column. + +If +.B dir +==0, then +.B im_profile(3) +searches down from the top edge, writing an image as wide as the input +image, but only 1 pixel high, containing the number of pixels down to the +first non-zero pixel for each column of input pixels. + +If +.B dir +==1, then +.B im_profile(3) +searches across from the left edge, writing an image as high as the input +image, but only 1 pixel wide, containing the number of pixels across to the +first non-zero pixel for each row of input pixels. + +.B im_cntlines(3) +calculates the number of transitions between black and white pixels of +an image. If dir is 1 then all transitions across the +vertical direction are calculated for all Xsize lines of the image. If dir +is 0 then all transitions along the horizontal direction for all Ysize +lines are calculated. The function returns the number of transitions +divided by twice the number of the corresponding Xsize of Ysize lines. +The program is primarily used to calculate the number of unbroken horizontal +(dir=0) or vertical lines (dir=1) that exist within an image. +Input image in can have only one channel. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_erode(3), im_dilate(3). diff --git a/libsrc/morphology/man3/im_dilate.3 b/libsrc/morphology/man3/im_dilate.3 new file mode 100644 index 00000000..8a97d741 --- /dev/null +++ b/libsrc/morphology/man3/im_dilate.3 @@ -0,0 +1,72 @@ +.TH IM_DILATE 3 "14 May 1991" +.SH NAME +im_dilate, im_dilate_raw, im_erode, im_erode_raw \- perform morphological operations on a white object against a black background +.SH SYNOPSIS +.B #include + +.B int im_dilate(in, out, m) +.br +.B IMAGE *in, *out; +.br +.B INTMASK *m; + +.B int im_erode(in, out, m) +.br +.B IMAGE *in, *out; +.br +.B INTMASK *m; + +.B int im_dilate_raw(in, out, m) +.br +.B IMAGE *in, *out; +.br +.B INTMASK *m; + +.B int im_erode_raw(in, out, m) +.br +.B IMAGE *in, *out; +.br +.B INTMASK *m; + +.SH DESCRIPTION +The above functions are applications of morphological operations on one +channel binary images ie. images with pixels that are either 0 (black) or 255 +(white). All functions assume that input images contain white objects against +a black background. + +Mask coefficients can be either 0 (for object) or 255 (for background) or 128 +(for do not care). + +The mask should have odd length sides and the origin of the mask is at location +(m-\>xsize/2,m-\>ysize/2) integer division. All algorithms have been based on +the book "Fundamentals of Digital Image Processing" by A. Jain, pp 384-388, +Prentice-Hall, 1989. Essentially, im_dilate(3) sets pixels in the output if +*any* part of the mask matches, whereas im_erode(3) sets pixels only if *all* +of the mask matches. + +im_dilate(3) +dilates the image pointed by in, according to the mask pointed by m and writes +the result in the location pointed by the IMAGE descriptor out. The output +image is the same size as the input, with a black border in the manner of +im_conv(3). + +im_dilate_raw(3) +works as im_dilate(3), but does not add a black border. + +im_erode(3) +erodes the image pointed by in, according to the mask pointed by m and writes +the result in the location pointed by the IMAGE descriptor out. Again, the +output image is forced to have the same size as the input. + +im_erode_raw(3) +works as im_erode(3), but does not add a black border. + +See the boolean operations im_and(3), im_or(3) and im_eor(3) for analogues +of the usual set difference and set union operations. + +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_read_imask(3), im_conv(3), im_and(3), im_rotate_imask(3). +.SH COPYRIGHT +1991-1995, Birkbeck College and the National Gallery diff --git a/libsrc/morphology/man3/im_dilate_raw.3 b/libsrc/morphology/man3/im_dilate_raw.3 new file mode 100644 index 00000000..1fdf30e4 --- /dev/null +++ b/libsrc/morphology/man3/im_dilate_raw.3 @@ -0,0 +1 @@ +.so man3/im_dilate.3 diff --git a/libsrc/morphology/man3/im_erode.3 b/libsrc/morphology/man3/im_erode.3 new file mode 100644 index 00000000..1fdf30e4 --- /dev/null +++ b/libsrc/morphology/man3/im_erode.3 @@ -0,0 +1 @@ +.so man3/im_dilate.3 diff --git a/libsrc/morphology/man3/im_erode_raw.3 b/libsrc/morphology/man3/im_erode_raw.3 new file mode 100644 index 00000000..1fdf30e4 --- /dev/null +++ b/libsrc/morphology/man3/im_erode_raw.3 @@ -0,0 +1 @@ +.so man3/im_dilate.3 diff --git a/libsrc/morphology/man3/im_profile.3 b/libsrc/morphology/man3/im_profile.3 new file mode 100644 index 00000000..a181029c --- /dev/null +++ b/libsrc/morphology/man3/im_profile.3 @@ -0,0 +1 @@ +.so man3/im_cntlines.3 diff --git a/libsrc/morphology/morph_dispatch.c b/libsrc/morphology/morph_dispatch.c new file mode 100644 index 00000000..86d1e5b4 --- /dev/null +++ b/libsrc/morphology/morph_dispatch.c @@ -0,0 +1,213 @@ +/* VIPS function dispatch tables for morphology. + * + * J. Cupitt, 19/9/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Args to im_profile. + */ +static im_arg_desc profile_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "direction" ) +}; + +/* Call im_profile via arg vector. + */ +static int +profile_vec( im_object *argv ) +{ + int dir = *((int *) argv[2]); + + return( im_profile( argv[0], argv[1], dir ) ); +} + +/* Description of im_profile. + */ +static im_function profile_desc = { + "im_profile", /* Name */ + "find first horizontal/vertical edge", /* Descr. */ + IM_FN_TRANSFORM, /* Flags */ + profile_vec, /* Dispatch function */ + IM_NUMBER( profile_args ), /* Size of arg list */ + profile_args /* Arg list */ +}; + +/* Args to im_erode. + */ +static im_arg_desc erode_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_IMASK( "mask" ) +}; + +/* Call im_dilate via arg vector. + */ +static int +dilate_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_dilate( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_dilate. + */ +static im_function dilate_desc = { + "im_dilate", /* Name */ + "dilate image with mask, adding a black border", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + dilate_vec, /* Dispatch function */ + IM_NUMBER( erode_args ), /* Size of arg list */ + erode_args /* Arg list */ +}; + +/* Call im_dilate_raw via arg vector. + */ +static int +dilate_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_dilate_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_dilate_raw. + */ +static im_function dilate_raw_desc = { + "im_dilate_raw", /* Name */ + "dilate image with mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + dilate_raw_vec, /* Dispatch function */ + IM_NUMBER( erode_args ), /* Size of arg list */ + erode_args /* Arg list */ +}; + +/* Call im_erode via arg vector. + */ +static int +erode_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_erode( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_erode. + */ +static im_function erode_desc = { + "im_erode", /* Name */ + "erode image with mask, adding a black border", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + erode_vec, /* Dispatch function */ + IM_NUMBER( erode_args ), /* Size of arg list */ + erode_args /* Arg list */ +}; + +/* Call im_erode_raw via arg vector. + */ +static int +erode_raw_vec( im_object *argv ) +{ + im_mask_object *mo = argv[2]; + + return( im_erode_raw( argv[0], argv[1], mo->mask ) ); +} + +/* Description of im_erode_raw. + */ +static im_function erode_raw_desc = { + "im_erode_raw", /* Name */ + "erode image with mask", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + erode_raw_vec, /* Dispatch function */ + IM_NUMBER( erode_args ), /* Size of arg list */ + erode_args /* Arg list */ +}; + +/* Args to im_cntlines. + */ +static im_arg_desc cntlines_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DOUBLE( "nlines" ), + IM_INPUT_INT( "direction" ) +}; + +/* Call im_cntlines via arg vector. + */ +static int +cntlines_vec( im_object *argv ) +{ + double *out = (double *) argv[1]; + int dir = *((int *) argv[2]); + + return( im_cntlines( argv[0], out, dir ) ); +} + +/* Description of im_cntlines. + */ +static im_function cntlines_desc = { + "im_cntlines", /* Name */ + "count horizontal or vertical lines", + 0, /* Flags */ + cntlines_vec, /* Dispatch function */ + IM_NUMBER( cntlines_args ), /* Size of arg list */ + cntlines_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *morph_list[] = { + &cntlines_desc, + &dilate_desc, + &dilate_raw_desc, + &erode_desc, + &erode_raw_desc, + &profile_desc +}; + +/* Package of functions. + */ +im_package im__morphology = { + "morphology", + IM_NUMBER( morph_list ), + morph_list +}; diff --git a/libsrc/mosaicing/Makefile.am b/libsrc/mosaicing/Makefile.am new file mode 100644 index 00000000..c39d0980 --- /dev/null +++ b/libsrc/mosaicing/Makefile.am @@ -0,0 +1,28 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libmosaicing.la + +libmosaicing_la_SOURCES = \ + im_affine.c \ + match.c \ + mosaic1.c \ + mosaicing_dispatch.c \ + similarity.c \ + global_balance.c \ + im_avgdxdy.c \ + im_chkpair.c \ + im_clinear.c \ + im_improve.c \ + im_initialize.c \ + im_lrcalcon.c \ + im_lrmerge.c \ + im_lrmosaic.c \ + im_tbcalcon.c \ + im_tbmerge.c \ + im_remosaic.c \ + im_tbmosaic.c \ + merge.h \ + global_balance.h \ + mosaic.h + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/mosaicing/global_balance.c b/libsrc/mosaicing/global_balance.c new file mode 100644 index 00000000..ba7db608 --- /dev/null +++ b/libsrc/mosaicing/global_balance.c @@ -0,0 +1,1747 @@ +/* Parse ".desc" files from mosaiced images to generate (x,y) offsets for + * every sub-image. Find all overlap stats and solve balancing with LMS. + * Regenerate mosaic, with balancing fixed. + * + * 1/12/93 JC + * - first version, unfinished! + * 6/9/95 JC + * - LMS fixed, now works, more or less + * 12/9/95 JC + * - now does positions correctly too + * - ignores trivial overlaps + * 19/9/95 JC + * - prints correct number of balance factors! + * 10/11/95 JC + * - now tracks im_copy() calls too, so you can save sub-images + * 12/1/96 JC + * - slightly clearer diagnostics + * - better centre of factors around 1.0 with log() average + * 1/3/96 JC + * - new im_global_balance_float variant lets our caller adjust factor + * range if output has burn-out + * - im_global_balance_search uses the above to produce scaled output ... + * very slow! + * 11/3/96 JC + * - now tries current directory too for input files + * 22/3/96 JC + * - horrible bug in position finding! now fixed + * 1/8/97 JC + * - revised for new mosaic functions and non-square images + * 12/9/97 JC + * - code for im_lrmosaic1() support + * - output type == input type, so works for short images too + * 6/1/99 JC + * - new gamma parameter, do scale in linear space + * - removed _search version, as can now be done with ip + * - renamed _float to f suffix, in line with im_conv()/im_convf() + * 15/2/00 JC + * - balancef() did not scale in linear space + * 2/2/01 JC + * - added tunable max blend width + * 7/11/01 JC + * - global_balance.h broken out for im_remosaic() + * 25/02/02 JC + * - better transform function scheme + * 21/3/01 JC + * - quicker bailout on error + * 8/11/02 JC + * - add <> around file names so you can have spaces :( + * 9/12/02 JC + * - track original params and always reuse them ... makes us proof + * against geo reconstruct errors + * 10/3/03 JC + * - weed out overlaps which contain only transparent pixels + * 4/1/07 + * - switch to new history thing, switch im_errormsg() too + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Strategy: build a tree describing the file + * relationships in the desc file, then walk that passing constraints + * back up to the root. Look up file names in symbol_table. + */ + +/* Define for debug output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "merge.h" +#include "global_balance.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define MAX_ITEMS (50) + +/* How pix an overlap has to be (in pixels) before we think it's trivial and + * we ignore it. + */ +#define TRIVIAL (20 * 20) + +/* Break a string into a list of strings. Write '\0's into the string. out + * needs to be MAX_FILES long. -1 for error, otherwise number of args found. + + " " + + out[0] = "fred" + out[1] = "jim poop" + out[2] = "sn aff le" + + */ +static int +break_items( char *line, char **out ) +{ + int i; + char *p; + + for( i = 0; i < MAX_ITEMS; i++ ) { + /* Skip to first '<'. + */ + if( !(p = strchr( line, '<' )) ) + break; + + out[i] = line = p + 1; + + if( !(p = strchr( line, '>' )) ) { + im_error( "break_files", _( "no matching '>'" ) ); + return( -1 ); + } + + *p = '\0'; + line = p + 1; + } + + if( i == MAX_ITEMS ) { + im_error( "break_files", _( "too many items" ) ); + return( -1 ); + } + + return( i ); +} + +/* Try to open a file. If full path fails, try the current directory. + */ +IMAGE * +im__global_open_image( SymbolTable *st, char *name ) +{ + IMAGE *im; + + if( (im = im_open_local( st->im, name, "r" )) ) + return( im ); + if( (im = im_open_local( st->im, im_skip_dir( name ), "r" )) ) + return( im ); + + return( NULL ); +} + +static int +junk_node( JoinNode *node ) +{ + IM_FREEF( g_slist_free, node->overlaps ); + + return( 0 ); +} + +/* Hash from a filename to an index into symbol_table. + */ +static int +hash( char *n ) +{ + int i; + int r = 0; + int l = strlen( n ); + + for( i = 0; i < l; i++ ) + r = ((r + n[i]) * 43) & 0xffffff; + + return( r % SYM_TAB_SIZE ); +} + +/* Make a leaf for a file. + */ +static JoinNode * +build_node( SymbolTable *st, char *name ) +{ + JoinNode *node = IM_NEW( st->im, JoinNode ); + int n = hash( name ); + + /* Fill fields. + */ + if( !node || !(node->name = im_strdup( st->im, name )) ) + return( NULL ); + + node->type = JOIN_LEAF; + node->dirty = 0; + node->mwidth = -2; + node->st = st; + im__transform_init( &node->cumtrn ); + node->trnim = NULL; + node->arg1 = NULL; + node->arg2 = NULL; + node->overlaps = NULL; + node->im = NULL; + node->index = 0; + + if( im_add_close_callback( st->im, + (im_callback_fn) junk_node, node, NULL ) ) + return( NULL ); + + /* Try to open. + */ + if( (node->im = im__global_open_image( st, name )) ) { + /* There is a file there - set width and height. + */ + node->cumtrn.oarea.width = node->im->Xsize; + node->cumtrn.oarea.height = node->im->Ysize; + } + else { + /* Clear the error buffer to lessen confusion. + */ + im_clear_error_string(); + } + + st->table[n] = g_slist_prepend( st->table[n], node ); + + return( node ); +} + +/* Make a new overlap struct. + */ +static OverlapInfo * +build_overlap( JoinNode *node, JoinNode *other, Rect *overlap ) +{ + OverlapInfo *lap = IM_NEW( node->st->im, OverlapInfo ); + + if( !lap ) + return( NULL ); + + lap->node = node; + lap->other = other; + lap->overlap = *overlap; + lap->nstats = NULL; + lap->ostats = NULL; + node->overlaps = g_slist_prepend( node->overlaps, lap ); + node->st->novl++; + + return( lap ); +} + +static void +overlap_destroy( OverlapInfo *lap ) +{ + JoinNode *node = lap->node; + + node->overlaps = g_slist_remove( node->overlaps, lap ); + assert( node->st->novl > 0 ); + node->st->novl--; +} + +static int +junk_table( SymbolTable *st ) +{ + int i; + + for( i = 0; i < st->sz; i++ ) + IM_FREEF( g_slist_free, st->table[i] ); + + return( 0 ); +} + +/* Build a new symbol table. + */ +SymbolTable * +im__build_symtab( IMAGE *out, int sz ) +{ + SymbolTable *st = IM_NEW( out, SymbolTable ); + int i; + + if( !st ) + return( NULL ); + if( !(st->table = IM_ARRAY( out, sz, GSList * )) ) + return( NULL ); + st->sz = sz; + st->im = out; + st->novl = 0; + st->nim = 0; + st->njoin = 0; + st->root = NULL; + st->leaf = NULL; + st->fac = NULL; + + if( im_add_close_callback( out, + (im_callback_fn) junk_table, st, NULL ) ) + return( NULL ); + + for( i = 0; i < sz; i++ ) + st->table[i] = NULL; + + return( st ); +} + +/* Does this node have this file name? + */ +static JoinNode * +test_name( JoinNode *node, char *name ) +{ + if( strcmp( node->name, name ) == 0 ) + return( node ); + else + return( NULL ); +} + +/* Look up a filename in the symbol_table. + */ +static JoinNode * +find_node( SymbolTable *st, char *name ) +{ + return( im_slist_map2( st->table[hash( name )], + (VSListMap2Fn) test_name, name, NULL ) ); +} + +/* Given a name: return either the existing node for that name, or a new node + * we have made. + */ +static JoinNode * +add_node( SymbolTable *st, char *name ) +{ + JoinNode *node; + + if( !(node = find_node( st, name )) && + !(node = build_node( st, name )) ) + return( NULL ); + + return( node ); +} + +/* Map a user function over the whole of the symbol table. + */ +void * +im__map_table( SymbolTable *st, void *(*fn)(), void *a, void *b ) +{ + int i; + void *r; + + for( i = 0; i < st->sz; i++ ) + if( (r = im_slist_map2( st->table[i], + (VSListMap2Fn) fn, a, b )) ) + return( r ); + + return( NULL ); +} + +/* Set the dirty field on a join. + */ +static void * +set_dirty( JoinNode *node, int state ) +{ + node->dirty = state; + + return( NULL ); +} + +/* Clean the whole table. + */ +static void +clean_table( SymbolTable *st ) +{ + (void) im__map_table( st, set_dirty, (void *) 0, NULL ); +} + +/* Do geometry calculations on a node, assuming geo is up to date for any + * children. + */ +static void +calc_geometry( JoinNode *node ) +{ + Rect um; + + switch( node->type ) { + case JOIN_LR: + case JOIN_TB: + case JOIN_LRROTSCALE: + case JOIN_TBROTSCALE: + /* Join two areas. + */ + im_rect_unionrect( &node->arg1->cumtrn.oarea, + &node->arg2->cumtrn.oarea, &um ); + node->cumtrn.iarea.left = 0; + node->cumtrn.iarea.top = 0; + node->cumtrn.iarea.width = um.width; + node->cumtrn.iarea.height = um.height; + im__transform_set_area( &node->cumtrn ); + break; + + case JOIN_CP: + /* Copy from child. + */ + node->cumtrn = node->arg1->cumtrn; + break; + + case JOIN_LEAF: + /* Just use leaf dimensions, if there are any. + */ + if( node->im ) { + node->cumtrn.iarea.left = 0; + node->cumtrn.iarea.top = 0; + node->cumtrn.iarea.width = node->im->Xsize; + node->cumtrn.iarea.height = node->im->Ysize; + im__transform_set_area( &node->cumtrn ); + } + break; + + default: + error_exit( "internal error #98356" ); + /*NOTREACHED*/ + } +} + +/* Propogate a transform down a tree. If dirty is set, we've been here before, + * so there is a doubling up of this node. If this is a leaf, then we have the + * same leaf twice (which, in fact, we can cope with); if this is a node, we + * have circularity. + */ +static int +propogate_transform( JoinNode *node, Transformation *trn ) +{ + if( !node ) + return( 0 ); + + if( node->dirty && node->arg1 && node->arg2 ) { + im_error( "im_global_balance", _( "circularity detected" ) ); + return( -1 ); + } + node->dirty = 1; + + /* Transform our children. + */ + if( propogate_transform( node->arg1, trn ) || + propogate_transform( node->arg2, trn ) ) + return( -1 ); + + /* Transform us, and recalculate our position and size. + */ + im__transform_add( &node->cumtrn, trn, &node->cumtrn ); + calc_geometry( node ); + + return( 0 ); +} + +/* Ah ha! A leaf is actually made up of two smaller files with an lr or a tb + * merge. Turn a leaf node into a join node. Propogate the transform down + * arg2's side of the tree. + */ +static int +make_join( SymbolTable *st, JoinType type, + JoinNode *arg1, JoinNode *arg2, JoinNode *out, + double a, double b, double dx, double dy, int mwidth ) +{ + Transformation trn; + + /* Check output is ok. + */ + if( out->type != JOIN_LEAF ) { + im_error( "im_global_balance", + _( "image \"%s\" used twice as output" ), out->name ); + return( -1 ); + } + + /* Fill fields. + */ + out->type = type; + out->mwidth = mwidth; + out->a = a; + out->b = b; + out->dx = dx; + out->dy = dy; + out->arg1 = arg1; + out->arg2 = arg2; + out->thistrn.a = a; + out->thistrn.b = -b; + out->thistrn.c = b; + out->thistrn.d = a; + out->thistrn.dx = dx; + out->thistrn.dy = dy; + + /* Clean the table and propogate the transform down the RHS of the + * graph. + */ + clean_table( st ); + if( propogate_transform( arg2, &out->thistrn ) ) + return( -1 ); + + /* Find the position and size of our output. + */ + calc_geometry( out ); + + /* Now normalise the result, so that out is at (0,0) again. + */ + trn.a = 1.0; + trn.b = 0.0; + trn.c = 0.0; + trn.d = 1.0; + trn.dx = -out->cumtrn.oarea.left; + trn.dy = -out->cumtrn.oarea.top; + clean_table( st ); + if( propogate_transform( out, &trn ) ) + return( -1 ); + + return( 0 ); +} + +/* Make a copy node. + */ +static int +make_copy( SymbolTable *st, JoinNode *before, JoinNode *after ) +{ + /* Check output is ok. + */ + if( after->type != JOIN_LEAF ) { + im_error( "im_global_balance", + _( "image \"%s\" used twice as output" ), after->name ); + return( -1 ); + } + + /* Fill fields. + */ + after->type = JOIN_CP; + after->arg1 = before; + after->arg2 = NULL; + + /* Copy over the position and size from the before to the after. + */ + calc_geometry( after ); + + return( 0 ); +} + +/* Process a single .desc line. + */ +static int +process_line( SymbolTable *st, const char *text ) +{ + char line[1024]; + +#ifdef DEBUG + printf( "read: %s\n", text ); +#endif /*DEBUG*/ + + /* We destroy line during the parse. + */ + im_strncpy( line, text, 1024 ); + + if( im_isprefix( "#LRJOIN ", line ) || + im_isprefix( "#TBJOIN ", line ) ) { + /* Yes: magic join command. Break into tokens. Format is eg. + + #LRJOIN [] + + */ + char *item[MAX_ITEMS]; + int nitems; + JoinType type; + JoinNode *arg1, *arg2, *join; + int dx, dy, mwidth; + + if( (nitems = break_items( line, item )) < 0 ) + return( -1 ); + if( nitems != 5 && nitems != 6 ) { + im_error( "global_balance", + _( "bad number of args in join line" ) ); + return( -1 ); + } + + if( !(arg1 = add_node( st, item[0] )) || + !(arg2 = add_node( st, item[1] )) || + !(join = add_node( st, item[2] )) ) + return( -1 ); + dx = atoi( item[3] ); + dy = atoi( item[4] ); + if( nitems == 6 ) + mwidth = atoi( item[5] ); + else + mwidth = -1; + if( im_isprefix( "#LRJOIN ", line ) ) + type = JOIN_LR; + else + type = JOIN_TB; + + if( make_join( st, type, arg1, arg2, + join, 1.0, 0.0, dx, dy, mwidth ) ) + return( -1 ); + } + else if( im_isprefix( "#LRROTSCALE ", line ) || + im_isprefix( "#TBROTSCALE ", line ) ) { + /* Rot + scale. Format is eg. + + #LRROTSCALE \ + [] + + */ + char *item[MAX_ITEMS]; + int nitems; + JoinType type; + JoinNode *arg1, *arg2, *join; + double a, b, dx, dy; + int mwidth; + + if( (nitems = break_items( line, item )) < 0 ) + return( -1 ); + if( nitems != 7 && nitems != 8 ) { + im_error( "global_balance", + _( "bad number of args in join1 line" ) ); + return( -1 ); + } + + if( !(arg1 = add_node( st, item[0] )) || + !(arg2 = add_node( st, item[1] )) || + !(join = add_node( st, item[2] )) ) + return( -1 ); + a = g_ascii_strtod( item[3], NULL ); + b = g_ascii_strtod( item[4], NULL ); + dx = g_ascii_strtod( item[5], NULL ); + dy = g_ascii_strtod( item[6], NULL ); + if( nitems == 8 ) + mwidth = atoi( item[7] ); + else + mwidth = -1; + if( im_isprefix( "#LRROTSCALE ", line ) ) + type = JOIN_LRROTSCALE; + else + type = JOIN_TBROTSCALE; + + if( make_join( st, type, arg1, arg2, + join, a, b, dx, dy, mwidth ) ) + return( -1 ); + } + else if( im_isprefix( "copy ", line ) ) { + /* im_copy() call ... make a JOIN_CP node. + */ + char *item[MAX_ITEMS]; + int nitems; + JoinNode *before, *after; + + if( (nitems = break_items( line, item )) < 0 ) + return( -1 ); + if( nitems != 2 ) { + im_error( "global_balance", + _( "bad number of args in copy line" ) ); + return( -1 ); + } + + if( !(before = add_node( st, item[0] )) || + !(after = add_node( st, item[1] )) || + make_copy( st, before, after ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Set the dirty flag on any nodes we reference. + */ +static void * +set_referenced( JoinNode *node ) +{ + if( node->arg1 ) + node->arg1->dirty = 1; + if( node->arg2 ) + node->arg2->dirty = 1; + + return( NULL ); +} + +/* Is this a root node? Should be clean. + */ +static void * +is_root( JoinNode *node ) +{ + if( !node->dirty ) + return( (void *) node ); + else + return( NULL ); +} + +/* Scan the symbol table, looking for a node which no node references. + */ +static JoinNode * +find_root( SymbolTable *st ) +{ + JoinNode *root; + JoinNode *notroot; + + /* Clean the table, then scan it, setting all pointed-to nodes dirty. + */ + clean_table( st ); + im__map_table( st, set_referenced, NULL, NULL ); + + /* Look for the first clean symbol. + */ + root = (JoinNode *) im__map_table( st, is_root, NULL, NULL ); + + /* No root? Hot dang! + */ + if( !root ) { + im_error( "im_global_balance", + _( "mosaic root not found in desc file\n" + "is this really a mosaiced image?" ) ); + return( NULL ); + } + + /* Now dirty that - then if there are any more clean symbols, we have + * more than one root. + */ + root->dirty = 1; + if( (notroot = im__map_table( st, is_root, NULL, NULL )) ) { + im_error( "im_global_balance", _( "more than one root" ) ); + return( NULL ); + } + + return( root ); +} + +/* Walk history_list and parse each line. + */ +int +im__parse_desc( SymbolTable *st, IMAGE *in ) +{ + GSList *p; + + for( p = in->history_list; p; p = p->next ) { + GValue *value = (GValue *) p->data; + + assert( G_VALUE_TYPE( value ) == IM_TYPE_REF_STRING ); + + if( process_line( st, im_ref_string_get( value ) ) ) + return( -1 ); + } + + /* Find root. + */ + if( !(st->root = find_root( st )) ) + return( -1 ); + + return( 0 ); +} + +/* Count and index all leaf images. + */ +static void * +count_leaves( JoinNode *node ) +{ + if( node->type == JOIN_LEAF ) { + node->index = node->st->nim; + node->st->nim++; + } + + return( NULL ); +} + +#ifdef DEBUG +/* Print a JoinNode. + */ +static void +print_node( JoinNode *node ) +{ + printf( "%s, position %dx%d, size %dx%d, index %d\n", + im_skip_dir( node->name ), + node->cumtrn.oarea.left, node->cumtrn.oarea.top, + node->cumtrn.oarea.width, node->cumtrn.oarea.height, + node->index ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print a leaf. + */ +static void * +print_leaf( JoinNode *node ) +{ + if( node->type == JOIN_LEAF ) + print_node( node ); + + return( NULL ); +} +#endif /*DEBUG*/ + +/* Count all join nodes. + */ +static void * +count_joins( JoinNode *node ) +{ + if( node->type == JOIN_TB || + node->type == JOIN_LR || + node->type == JOIN_LRROTSCALE || + node->type == JOIN_TBROTSCALE ) + node->st->njoin++; + + return( NULL ); +} + +#ifdef DEBUG +/* Print a few spaces. + */ +static void +spc( int n ) +{ + int i; + + for( i = 0; i < n; i++ ) + printf( " " ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +static char * +JoinType2char( JoinType type ) +{ + switch( type ) { + case JOIN_LR: return( "JOIN_LR" ); + case JOIN_TB: return( "JOIN_TB" ); + case JOIN_LRROTSCALE: return( "JOIN_LRROTSCALE" ); + case JOIN_TBROTSCALE: return( "JOIN_TBROTSCALE" ); + case JOIN_CP: return( "JOIN_CP" ); + case JOIN_LEAF: return( "JOIN_LEAF" ); + + default: + error_exit( "internal error #9275" ); + /*NOTEACHED*/ + + return( NULL ); + } +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print a join node. + */ +static void * +print_joins( JoinNode *node, int indent ) +{ + switch( node->type ) { + case JOIN_TB: + case JOIN_LR: + case JOIN_TBROTSCALE: + case JOIN_LRROTSCALE: + spc( indent ); + printf( "%s to make %s, size %dx%d, pos. %dx%d, of:\n", + JoinType2char( node->type ), + im_skip_dir( node->name ), + node->cumtrn.oarea.width, node->cumtrn.oarea.height, + node->cumtrn.oarea.left, node->cumtrn.oarea.top ); + spc( indent ); + printf( "reference:\n" ); + print_joins( node->arg1, indent+2 ); + spc( indent ); + printf( "secondary:\n" ); + print_joins( node->arg2, indent+2 ); + break; + + case JOIN_CP: + spc( indent ); + printf( "copy to make %s of:\n", im_skip_dir( node->name ) ); + print_joins( node->arg1, indent+2 ); + break; + + case JOIN_LEAF: + spc( indent ); + printf( "input image %s\n", im_skip_dir( node->name ) ); + break; + } + + return( NULL ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print an overlap. + */ +static void * +print_overlap( OverlapInfo *lap ) +{ + printf( "-> %s overlaps with %s; (this, other) = (%.4G, %.4G)\n", + im_skip_dir( lap->node->name ), + im_skip_dir( lap->other->name ), + lap->nstats->coeff[4], + lap->ostats->coeff[4] ); + + return( NULL ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print the overlaps on a leaf. + */ +static void * +print_overlaps( JoinNode *node ) +{ + if( node->type == JOIN_LEAF && g_slist_length( node->overlaps ) > 0 ) { + printf( "overlap of %s with:\n", im_skip_dir( node->name ) ); + im_slist_map2( node->overlaps, + (VSListMap2Fn) print_overlap, NULL, NULL ); + } + + return( NULL ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print and accumulate the error on an overlap. + */ +static void * +print_overlap_error( OverlapInfo *lap, double *fac, double *total ) +{ + double na = lap->nstats->coeff[4]; + double oa = lap->ostats->coeff[4]; + double err; + + if( fac ) { + na *= fac[lap->node->index]; + oa *= fac[lap->other->index]; + } + + err = na - oa; + + printf( "-> file %s, error = %g\n", + im_skip_dir( lap->other->name ), err ); + *total += err*err; + + return( NULL ); +} +#endif /*DEBUG*/ + +#ifdef DEBUG +/* Print and accumulate the overlap errors on a leaf. + */ +static void * +print_overlap_errors( JoinNode *node, double *fac, double *total ) +{ + if( node->type == JOIN_LEAF && g_slist_length( node->overlaps ) > 0 ) { + printf( "overlap of %s (index %d) with:\n", + im_skip_dir( node->name ), node->index ); + im_slist_map2( node->overlaps, + (VSListMap2Fn) print_overlap_error, fac, total ); + } + + return( NULL ); +} +#endif /*DEBUG*/ + +/* Make a DOUBLEMASK local to an image descriptor. + */ +static DOUBLEMASK * +local_mask( IMAGE *out, DOUBLEMASK *mask ) +{ + if( !mask ) + return( NULL ); + + if( im_add_evalend_callback( out, + (im_callback_fn) im_free_dmask, mask, NULL ) ) { + im_free_dmask( mask ); + return( NULL ); + } + + return( mask ); +} + +/* Extract a rect. + */ +static int +extract_rect( IMAGE *in, IMAGE *out, Rect *r ) +{ + return( im_extract_area( in, out, + r->left, r->top, r->width, r->height ) ); +} + +/* Two images overlap in an area ... make a mask the size of the area, which + * has 255 for every pixel where both images are non-zero. + */ +static int +make_overlap_mask( IMAGE *ref, IMAGE *sec, IMAGE *mask, + Rect *rarea, Rect *sarea ) +{ + IMAGE *t[6]; + + if( im_open_local_array( mask, t, 6, "mytemps", "p" ) || + extract_rect( ref, t[0], rarea ) || + extract_rect( sec, t[1], sarea ) || + im_extract_band( t[0], t[2], 0 ) || + im_extract_band( t[1], t[3], 0 ) || + im_notequalconst( t[2], t[4], 0.0 ) || + im_notequalconst( t[3], t[5], 0.0 ) || + im_andimage( t[4], t[5], mask ) ) + return( -1 ); + + return( 0 ); +} + +/* Find the number of non-zero pixels in a mask image. + */ +static int +count_nonzero( IMAGE *in, gint64 *count ) +{ + double avg; + + if( im_avg( in, &avg ) ) + return( -1 ); + *count = (avg * in->Xsize * in->Ysize ) / 255.0; + + return( 0 ); +} + +/* Find stats on an area of an IMAGE ... consider only pixels for which the + * mask is true. + */ +static DOUBLEMASK * +find_image_stats( IMAGE *in, IMAGE *mask, Rect *area ) +{ + DOUBLEMASK *stats; + IMAGE *t[4]; + gint64 count; + + /* Extract area, build black image, mask out pixels we want. + */ + if( im_open_local_array( in, t, 4, "find_image_stats", "p" ) || + extract_rect( in, t[0], area ) || + im_black( t[1], t[0]->Xsize, t[0]->Ysize, t[0]->Bands ) || + im_clip2fmt( t[1], t[2], t[0]->BandFmt ) || + im_ifthenelse( mask, t[0], t[2], t[3] ) ) + return( NULL ); + + /* Get stats from masked image. + */ + if( !(stats = local_mask( in, im_stats( t[3] ) )) ) + return( NULL ); + + /* Number of non-zero pixels in mask. + */ + if( count_nonzero( mask, &count ) ) + return( NULL ); + + /* And scale masked average to match. + */ + stats->coeff[4] *= (double) count / + ((double) mask->Xsize * mask->Ysize); + + /* Yuk! Zap the deviation column with the pixel count. Used later to + * determine if this is likely to be a significant overlap. + */ + stats->coeff[5] = count; + +#ifdef DEBUG + if( count == 0 ) + im_warn( "global_balance", _( "empty overlap!" ) ); +#endif /*DEBUG*/ + + return( stats ); +} + +/* Find the stats for an overlap struct. + */ +static int +find_overlap_stats( OverlapInfo *lap ) +{ + IMAGE *t1 = im_open_local( lap->node->im, "find_overlap_stats:1", "p" ); + Rect rarea, sarea; + + /* Translate the overlap area into the coordinate scheme for the main + * node. + */ + rarea = lap->overlap; + rarea.left -= lap->node->cumtrn.oarea.left; + rarea.top -= lap->node->cumtrn.oarea.top; + + /* Translate the overlap area into the coordinate scheme for the other + * node. + */ + sarea = lap->overlap; + sarea.left -= lap->other->cumtrn.oarea.left; + sarea.top -= lap->other->cumtrn.oarea.top; + + /* Make a mask for the overlap. + */ + if( make_overlap_mask( lap->node->trnim, lap->other->trnim, t1, + &rarea, &sarea ) ) + return( -1 ); + + /* Find stats for that area. + */ + if( !(lap->nstats = find_image_stats( lap->node->trnim, t1, &rarea )) ) + return( -1 ); + if( !(lap->ostats = find_image_stats( lap->other->trnim, t1, &sarea )) ) + return( -1 ); + + return( 0 ); +} + +/* Sub-fn. of below. + */ +static void * +overlap_eq( OverlapInfo *this, JoinNode *node ) +{ + if( this->other == node ) + return( this ); + else + return( NULL ); +} + +/* Is this an overlapping leaf? If yes, add to overlap list. + */ +static void * +test_overlap( JoinNode *other, JoinNode *node ) +{ + Rect overlap; + OverlapInfo *lap; + + /* Is other a suitable leaf to overlap with node? + */ + if( other->type != JOIN_LEAF || node == other ) + return( NULL ); + + /* Is there an overlap? + */ + im_rect_intersectrect( &node->cumtrn.oarea, &other->cumtrn.oarea, + &overlap ); + if( im_rect_isempty( &overlap ) ) + return( NULL ); + + /* Is this a trivial overlap? Ignore it if it is. + */ + if( overlap.width * overlap.height < TRIVIAL ) + /* Too few pixels. + */ + return( NULL ); + + /* Have we already added this overlap the other way around? ie. is + * node on other's overlap list? + */ + if( im_slist_map2( other->overlaps, + (VSListMap2Fn) overlap_eq, node, NULL ) ) + return( NULL ); + + /* A new overlap - add to overlap list. + */ + if( !(lap = build_overlap( node, other, &overlap )) ) + return( node ); + + /* Calculate overlap statistics. + */ + if( find_overlap_stats( lap ) ) + return( node ); + + /* If the pixel count either masked overlap is trivial, ignore this + * overlap. + */ + if( lap->nstats->coeff[5] < TRIVIAL || + lap->ostats->coeff[5] < TRIVIAL ) { +#ifdef DEBUG + printf( "trivial overlap ... junking\n" ); + printf( "nstats count = %g, ostats count = %g\n", + lap->nstats->coeff[5], lap->ostats->coeff[5] ); + print_overlap( lap ); +#endif /*DEBUG*/ + overlap_destroy( lap ); + } + + return( NULL ); +} + +/* If this is a leaf, look at all other joins for a leaf that overlaps. Aside: + * If this is a leaf, there should be an IMAGE. Flag an error if there is + * not. + */ +static void * +find_overlaps( JoinNode *node, SymbolTable *st ) +{ + if( node->type == JOIN_LEAF ) { + /* Check for image. + */ + if( !node->im ) { + im_error( "im_global_balance", + _( "unable to open \"%s\"" ), node->name ); + return( node ); + } + if( !node->trnim ) + error_exit( "global_balance: sanity failure #9834" ); + + return( im__map_table( st, test_overlap, node, NULL ) ); + } + + return( NULL ); +} + +/* Bundle of variables for matrix creation. + */ +typedef struct { + SymbolTable *st; /* Main table */ + JoinNode *leaf; /* Leaf to be 1.000 */ + DOUBLEMASK *K; /* LHS */ + DOUBLEMASK *M; /* RHS */ + int row; /* Current row */ +} MatrixBundle; + +/* Add a new row for the nominated overlap to the matricies. + */ +static void * +add_nominated( OverlapInfo *ovl, MatrixBundle *bun, double *gamma ) +{ + double *Kp = bun->K->coeff + bun->row; + double *Mp = bun->M->coeff + bun->row*bun->M->xsize; + double ns = pow( ovl->nstats->coeff[4], 1/(*gamma) ); + double os = pow( ovl->ostats->coeff[4], 1/(*gamma) ); + + Kp[0] = ns; + Mp[ovl->other->index - 1] = os; + + bun->row++; + + return( NULL ); +} + +/* Add a new row for an ordinary overlap to the matricies. + */ +static void * +add_other( OverlapInfo *ovl, MatrixBundle *bun, double *gamma ) +{ + double *Mp = bun->M->coeff + bun->row*bun->M->xsize; + double ns = -pow( ovl->nstats->coeff[4], 1/(*gamma) ); + double os = pow( ovl->ostats->coeff[4], 1/(*gamma) ); + + Mp[ovl->node->index - 1] = ns; + Mp[ovl->other->index - 1] = os; + + bun->row++; + + return( NULL ); +} + +/* Add stuff for node to matrix. + */ +static void * +add_row( JoinNode *node, MatrixBundle *bun, double *gamma ) +{ + if( node == bun->leaf ) + im_slist_map2( node->overlaps, + (VSListMap2Fn) add_nominated, bun, gamma ); + else + im_slist_map2( node->overlaps, + (VSListMap2Fn) add_other, bun, gamma ); + + return( NULL ); +} + +/* Fill K and M. leaf is image selected to have factor of 1.000. + */ +static void +fill_matricies( SymbolTable *st, double gamma, DOUBLEMASK *K, DOUBLEMASK *M ) +{ + MatrixBundle bun; + + bun.st = st; + bun.leaf = st->leaf; + bun.K = K; + bun.M = M; + bun.row = 0; + + /* Build matricies. + */ + im__map_table( st, add_row, &bun, &gamma ); +} + +/* Used to select the leaf whose coefficient we set to 1. + */ +static void * +choose_leaf( JoinNode *node ) +{ + if( node->type == JOIN_LEAF ) + return( node ); + + return( NULL ); +} + +/* Make an image from a node. + */ +static IMAGE * +make_mos_image( SymbolTable *st, JoinNode *node, transform_fn tfn, void *a ) +{ + IMAGE *im1, *im2, *out; + + switch( node->type ) { + case JOIN_LR: + case JOIN_TB: + if( !(im1 = make_mos_image( st, node->arg1, tfn, a )) || + !(im2 = make_mos_image( st, node->arg2, tfn, a )) || + !(out = im_open_local( st->im, node->name, "p" )) ) + return( NULL ); + + if( node->type == JOIN_LR ) { + if( im_lrmerge( im1, im2, out, + -node->dx, -node->dy, node->mwidth ) ) + return( NULL ); + } + else { + if( im_tbmerge( im1, im2, out, + -node->dx, -node->dy, node->mwidth ) ) + return( NULL ); + } + + break; + + case JOIN_LRROTSCALE: + case JOIN_TBROTSCALE: + if( !(im1 = make_mos_image( st, node->arg1, tfn, a )) || + !(im2 = make_mos_image( st, node->arg2, tfn, a )) || + !(out = im_open_local( st->im, node->name, "p" )) ) + return( NULL ); + + if( node->type == JOIN_LRROTSCALE ) { + if( im__lrmerge1( im1, im2, out, + node->a, node->b, node->dx, node->dy, + node->mwidth ) ) + return( NULL ); + } + else { + if( im__tbmerge1( im1, im2, out, + node->a, node->b, node->dx, node->dy, + node->mwidth ) ) + return( NULL ); + } + + break; + + case JOIN_LEAF: + /* Trivial case! + */ + if( !(out = tfn( node, a )) ) + return( NULL ); + + break; + + case JOIN_CP: + /* Very trivial case. + */ + out = make_mos_image( st, node->arg1, tfn, a ); + + break; + + default: + error_exit( "internal error #982369824375987" ); + /*NOTEACHED*/ + return( NULL ); + } + + return( out ); +} + +/* Re-build mosaic. + */ +int +im__build_mosaic( SymbolTable *st, IMAGE *out, transform_fn tfn, void *a ) +{ + JoinNode *root = st->root; + IMAGE *im1, *im2; + + switch( root->type ) { + case JOIN_LR: + case JOIN_TB: + if( !(im1 = make_mos_image( st, root->arg1, tfn, a )) || + !(im2 = make_mos_image( st, root->arg2, tfn, a )) ) + return( -1 ); + + if( root->type == JOIN_LR ) { + if( im_lrmerge( im1, im2, out, + -root->dx, -root->dy, root->mwidth ) ) + return( -1 ); + } + else { + if( im_tbmerge( im1, im2, out, + -root->dx, -root->dy, root->mwidth ) ) + return( -1 ); + } + + break; + + case JOIN_LRROTSCALE: + case JOIN_TBROTSCALE: + if( !(im1 = make_mos_image( st, root->arg1, tfn, a )) || + !(im2 = make_mos_image( st, root->arg2, tfn, a )) ) + return( -1 ); + + if( root->type == JOIN_LRROTSCALE ) { + if( im__lrmerge1( im1, im2, out, + root->a, root->b, root->dx, root->dy, + root->mwidth ) ) + return( -1 ); + } + else { + if( im__tbmerge1( im1, im2, out, + root->a, root->b, root->dx, root->dy, + root->mwidth ) ) + return( -1 ); + } + + break; + + case JOIN_LEAF: + /* Trivial case! Just one file in our mosaic. + */ + if( !(im1 = tfn( root, a )) || im_copy( im1, out ) ) + return( -1 ); + + break; + + case JOIN_CP: + /* Very trivial case. + */ + if( !(im1 = make_mos_image( st, root->arg1, tfn, a )) ) + return( -1 ); + if( im_copy( im1, out ) ) + return( -1 ); + + break; + + default: + error_exit( "internal error #982369824375987" ); + /*NOTEACHED*/ + } + + return( 0 ); +} + +/* Find correction factors. + */ +static int +find_factors( SymbolTable *st, double gamma ) +{ + DOUBLEMASK *K; + DOUBLEMASK *M; + DOUBLEMASK *m1, *m2, *m3, *m4, *m5; + double total; + double avg; + int i; + + /* Make output matricies. + */ + if( !(K = local_mask( st->im, im_create_dmask( "K", 1, st->novl ) )) || + !(M = local_mask( st->im, + im_create_dmask( "M", st->nim-1, st->novl ) )) ) + return( -1 ); + fill_matricies( st, gamma, K, M ); +#ifdef DEBUG + im_write_dmask( K ); + im_write_dmask( M ); +#endif /*DEBUG*/ + + /* Calculate LMS. + */ + if( !(m1 = local_mask( st->im, im_mattrn( M, "lms:1" ) )) ) + return( -1 ); + if( !(m2 = local_mask( st->im, im_matmul( m1, M, "lms:2" ) )) ) + return( -1 ); + if( !(m3 = local_mask( st->im, im_matinv( m2, "lms:3" ) )) ) + return( -1 ); + if( !(m4 = local_mask( st->im, im_matmul( m3, m1, "lms:4" ) )) ) + return( -1 ); + if( !(m5 = local_mask( st->im, im_matmul( m4, K, "lms:5" ) )) ) + return( -1 ); + + /* Make array of correction factors. + */ + if( !(st->fac = IM_ARRAY( st->im, st->nim, double )) ) + return( -1 ); + for( i = 0; i < m5->ysize; i++ ) + st->fac[i + 1] = m5->coeff[i]; + st->fac[0] = 1.0; + + /* Find average balance factor, normalise to that average. + */ + total = 0.0; + for( i = 0; i < st->nim; i++ ) + total += st->fac[i]; + avg = total / st->nim; + for( i = 0; i < st->nim; i++ ) + st->fac[i] /= avg; + +#ifdef DEBUG + /* Diagnostics! + */ + printf( "debugging output for im_global_balance():\n" ); + for( i = 0; i < st->nim; i++ ) + printf( "balance factor %d = %g\n", i, st->fac[i] ); + total = 0.0; + printf( "Overlap errors:\n" ); + im__map_table( st, print_overlap_errors, NULL, &total ); + printf( "RMS error = %g\n", sqrt( total / st->novl ) ); + + total = 0.0; + printf( "Overlap errors after adjustment:\n" ); + im__map_table( st, print_overlap_errors, st->fac, &total ); + printf( "RMS error = %g\n", sqrt( total / st->novl ) ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Look for all leaves, make sure we have a transformed version of each. + */ +static void * +generate_trn_leaves( JoinNode *node, SymbolTable *st ) +{ + if( node->type == JOIN_LEAF ) { + /* Check for image. + */ + if( !node->im ) { + im_error( "im_global_balance", + _( "unable to open \"%s\"" ), node->name ); + return( node ); + } + if( node->trnim ) + error_exit( "global_balance: sanity failure #765" ); + + /* Special case: if this is an untransformed leaf (there will + * always be at least one), then skip the affine. + */ + if( im__transform_isidentity( &node->cumtrn ) ) + node->trnim = node->im; + else + if( !(node->trnim = + im_open_local( node->im, "trnleaf:1", "p" )) || + im__affine( node->im, node->trnim, + &node->cumtrn ) ) + return( node ); + } + + return( NULL ); +} + +/* Analyse mosaic. + */ +static int +analyse_mosaic( SymbolTable *st, IMAGE *in ) +{ + /* Parse Hist on in. + */ + if( im__parse_desc( st, in ) ) + return( -1 ); + + /* Print parsed data. + */ +#ifdef DEBUG + printf( "Input files:\n" ); + im__map_table( st, print_leaf, NULL, NULL ); + printf( "\nOutput file:\n" ); + print_node( st->root ); + printf( "\nJoin commands:\n" ); + print_joins( st->root, 0 ); +#endif /*DEBUG*/ + + /* Generate transformed leaves. + */ + if( im__map_table( st, generate_trn_leaves, st, NULL ) ) + return( -1 ); + + /* Find overlaps. + */ + if( im__map_table( st, find_overlaps, st, NULL ) ) + return( -1 ); + + /* Scan table, counting and indexing input images and joins. + */ + im__map_table( st, count_leaves, NULL, NULL ); + im__map_table( st, count_joins, NULL, NULL ); + + /* Select leaf to be 1.000. + * This must be index == 0, unless you change stuff above! + */ + st->leaf = im__map_table( st, choose_leaf, NULL, NULL ); + + /* And print overlaps. + */ +#ifdef DEBUG + printf( "\nLeaf to be 1.000:\n" ); + print_node( st->leaf ); + printf( "\nOverlaps:\n" ); + im__map_table( st, print_overlaps, NULL, NULL ); + printf( "\n%d input files, %d unique overlaps, %d joins\n", + st->nim, st->novl, st->njoin ); +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Scale im by fac --- if it's uchar/ushort, use a lut. If we can use a lut, + * transform in linear space. If we can't, don't bother for efficiency. + */ +static IMAGE * +transform( JoinNode *node, double *gamma ) +{ + SymbolTable *st = node->st; + IMAGE *in = node->im; + double fac = st->fac[node->index]; + + IMAGE *out = im_open_local( st->im, node->name, "p" ); + IMAGE *t1 = im_open_local( out, "transform:1", "p" ); + IMAGE *t2 = im_open_local( out, "transform:2", "p" ); + IMAGE *t3 = im_open_local( out, "transform:3", "p" ); + IMAGE *t4 = im_open_local( out, "transform:4", "p" ); + IMAGE *t5 = im_open_local( out, "transform:5", "p" ); + + + if( !out || !t1 || !t2 || !t3 || !t4 || !t5 ) + return( NULL ); + + if( fac == 1.0 ) { + /* Easy! + */ + out = in; + } + else if( in->BandFmt == IM_BANDFMT_UCHAR ) { + if( im_identity( t1, 1 ) || + im_powtra( t1, t2, 1/(*gamma) ) || + im_lintra( fac, t2, 0.0, t3 ) || + im_powtra( t3, t4, *gamma ) || + im_clip( t4, t5 ) || + im_maplut( in, out, t5 ) ) + return( NULL ); + } + else if( in->BandFmt == IM_BANDFMT_USHORT ) { + if( im_identity_ushort( t1, 1, 65535 ) || + im_powtra( t1, t2, 1/(*gamma) ) || + im_lintra( fac, t2, 0.0, t3 ) || + im_powtra( t3, t4, *gamma ) || + im_clip2us( t4, t5 ) || + im_maplut( in, out, t5 ) ) + return( NULL ); + } + else { + /* Just im_lintra it. + */ + if( im_lintra( fac, in, 0.0, t1 ) || + im_clip2fmt( t1, out, in->BandFmt ) ) + return( NULL ); + } + + return( out ); +} + +/* As above, but output as float, not matched to input. + */ +static IMAGE * +transformf( JoinNode *node, double *gamma ) +{ + SymbolTable *st = node->st; + IMAGE *in = node->im; + double fac = node->st->fac[node->index]; + + IMAGE *out = im_open_local( st->im, node->name, "p" ); + IMAGE *t1 = im_open_local( out, "transform:1", "p" ); + IMAGE *t2 = im_open_local( out, "transform:2", "p" ); + IMAGE *t3 = im_open_local( out, "transform:3", "p" ); + IMAGE *t4 = im_open_local( out, "transform:4", "p" ); + + if( !out || !t1 || !t2 || !t3 || !t4 ) + return( NULL ); + + if( fac == 1.0 ) { + /* Easy! + */ + out = in; + } + else if( in->BandFmt == IM_BANDFMT_UCHAR ) { + if( im_identity( t1, 1 ) || + im_powtra( t1, t2, 1/(*gamma) ) || + im_lintra( fac, t2, 0.0, t3 ) || + im_powtra( t3, t4, *gamma ) || + im_maplut( in, out, t4 ) ) + return( NULL ); + } + else if( in->BandFmt == IM_BANDFMT_USHORT ) { + if( im_identity_ushort( t1, 1, 65535 ) || + im_powtra( t1, t2, 1/(*gamma) ) || + im_lintra( fac, t2, 0.0, t3 ) || + im_powtra( t3, t4, *gamma ) || + im_maplut( in, out, t4 ) ) + return( NULL ); + } + else { + /* Just im_lintra it. + */ + if( im_lintra( fac, in, 0.0, out ) ) + return( NULL ); + } + + return( out ); +} + +/* Balance mosaic, outputting in the original format. + */ +int +im_global_balance( IMAGE *in, IMAGE *out, double gamma ) +{ + SymbolTable *st; + + if( !(st = im__build_symtab( out, SYM_TAB_SIZE )) || + analyse_mosaic( st, in ) || + find_factors( st, gamma ) || + im__build_mosaic( st, out, (transform_fn) transform, &gamma ) ) + return( -1 ); + + return( 0 ); +} + +/* Balance mosaic, outputting as float. This is useful if the automatic + * selection of balance range fails - our caller can search the output for the + * min and max, and rescale to prevent burn-out. + */ +int +im_global_balancef( IMAGE *in, IMAGE *out, double gamma ) +{ + SymbolTable *st; + + if( !(st = im__build_symtab( out, SYM_TAB_SIZE )) || + analyse_mosaic( st, in ) || + find_factors( st, gamma ) || + im__build_mosaic( st, out, (transform_fn) transformf, &gamma ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/global_balance.h b/libsrc/mosaicing/global_balance.h new file mode 100644 index 00000000..2d33d809 --- /dev/null +++ b/libsrc/mosaicing/global_balance.h @@ -0,0 +1,126 @@ +/* Header for the .desc file parser in im_global_balance() + * + * 1/11/01 JC + * - cut from global_balance.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Number of entries in spine of file name hash table. + */ +#define SYM_TAB_SIZE (113) + +typedef enum _JoinType JoinType; +typedef struct _OverlapInfo OverlapInfo; +typedef struct _JoinNode JoinNode; +typedef struct _SymbolTable SymbolTable; + +/* Type of a transform function. + */ +typedef IMAGE *(*transform_fn)( JoinNode *, void * ); + +/* Join type. + */ +enum _JoinType { + JOIN_LR, /* im_lrmerge join */ + JOIN_TB, /* im_tbmerge join */ + JOIN_LRROTSCALE, /* 1st oder lrmerge */ + JOIN_TBROTSCALE, /* 1st oder tbmerge */ + JOIN_CP, /* im_copy operation */ + JOIN_LEAF /* Base file */ +}; + +/* An overlap struct. Attach a list of these to each leaf, one for each of + * the other leaves we touch. + */ +struct _OverlapInfo { + JoinNode *node; /* The base node - we are on this list */ + JoinNode *other; /* Node we overlap with */ + Rect overlap; /* The overlap area */ + DOUBLEMASK *nstats; /* Node's stats for overlap area */ + DOUBLEMASK *ostats; /* Other's stats for overlap area */ +}; + +/* Struct for a join node. + */ +struct _JoinNode { + char *name; /* This file name */ + JoinType type; /* What kind of join */ + SymbolTable *st; /* Symbol table we are on */ + int dirty; /* Used for circularity detection */ + + /* Params from join line in .desc file. + */ + double a, b; + double dx, dy; + int mwidth; + + /* Cumulative transform for this node. What our parents do to us. + * cumtrn.area is position and size of us, thistrn.area is pos and + * size of arg2. + */ + Transformation cumtrn; + + /* X-tras for LR/TB. thistrn is what we do to arg2. + */ + JoinNode *arg1; /* Left or up thing to join */ + JoinNode *arg2; /* Right or down thing to join */ + Transformation thistrn; /* Transformation for arg2 */ + + /* Special for leaves: all the join_nodes we overlap with, the + * IMAGE for that file, and the index. + */ + GSList *overlaps; + IMAGE *im; + IMAGE *trnim; /* Transformed image .. used in 2nd pass */ + int index; +}; + +/* We need to keep a table of JoinNode, indexed by file name. Hash into one + * of these from the name to get a pointer to the base of a list of JoinNode + * which hash to that offset. + */ +struct _SymbolTable { + GSList **table; /* Ptr to base of hash table */ + int sz; /* Size of hash table */ + IMAGE *im; /* Malloc relative to this */ + + int novl; /* Number of unique overlaps */ + int nim; /* Number of leaf images */ + int njoin; /* Number of join nodes */ + + JoinNode *root; /* Root of join tree */ + JoinNode *leaf; /* Leaf nominated to be 1.000 */ + double *fac; /* Correction factors */ +}; + +IMAGE *im__global_open_image( SymbolTable *st, char *name ); +SymbolTable *im__build_symtab( IMAGE *out, int sz ); +int im__parse_desc( SymbolTable *st, IMAGE *in ); +void *im__map_table( SymbolTable *st, void *(*fn)(), void *a, void *b ); +int im__build_mosaic( SymbolTable *st, + IMAGE *out, transform_fn tfn, void * ); diff --git a/libsrc/mosaicing/im_affine.c b/libsrc/mosaicing/im_affine.c new file mode 100644 index 00000000..73e53374 --- /dev/null +++ b/libsrc/mosaicing/im_affine.c @@ -0,0 +1,761 @@ +/* @(#) im_affine() ... affine transform, bi-linear interpolation. + * @(#) + * @(#) int im_affine(in, out, a, b, c, d, dx, dy, w, h, x, y) + * @(#) + * @(#) IMAGE *in, *out; + * @(#) double a, b, c, d, dx, dy; + * @(#) int w, h, x, y; + * @(#) + * @(#) Forward transform + * @(#) X = a * x + b * y + dx + * @(#) Y = c * x + d * y + dy + * @(#) + * @(#) x and y are the coordinates in input image. + * @(#) X and Y are the coordinates in output image. + * @(#) (0,0) is the upper left corner. + * + * Copyright N. Dessipris + * Written on: 01/11/1991 + * Modified on: 12/3/92 JC + * - rounding error in interpolation routine fixed + * - test for scale=1, angle=0 case fixed + * - clipping of output removed: redundant + * - various little tidies + * - problems remain with scale>20, size<10 + * + * Re-written on: 20/08/92, J.Ph Laurent + * + * 21/02/93, JC + * - speed-ups + * - simplifications + * - im_similarity now calculates a window and calls this routine + * 6/7/93 JC + * - rewritten for partials + * - ANSIfied + * - now rotates any non-complex type + * 3/6/94 JC + * - C revised in bug search + * 9/6/94 JC + * - im_prepare() was preparing too small an area! oops + * 22/5/95 JC + * - added code to detect all-black output area case - helps lazy ip + * 3/7/95 JC + * - IM_CODING_LABQ handling moved to here + * 31/7/97 JC + * - dx/dy sign reversed to be less confusing ... now follows comment at + * top ... ax - by + dx etc. + * - tiny speed up, replaced the *++ on interpolation with [z] + * - im_similarity() moved in here + * - args swapped: was whxy, now xywh + * - didn't agree with dispatch fns before :( + * 3/3/98 JC + * - im_demand_hint() added + * 20/12/99 JC + * - im_affine() made from im_similarity_area() + * - transform stuff cleaned up a bit + * 14/4/01 JC + * - oops, invert_point() had a rounding problem + * 23/2/02 JC + * - pre-calculate interpolation matricies + * - integer interpolation for int8/16 types, double for + * int32/float/double + * - faster transformation + * 15/8/02 JC + * - records Xoffset/Yoffset + * 14/4/04 + * - rounding, clipping and transforming revised, now pixel-perfect (or + * better than gimp, anyway) + * 22/6/05 + * - all revised again, simpler and more reliable now + * 30/3/06 + * - gah, still an occasional clipping problem + * 12/7/06 + * - still more tweaking, gah again + * 7/10/06 + * - set THINSTRIP for no-rotate affines + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG +#define DEBUG_GEOMETRY + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* "fast" floor() ... on my laptop, anyway. + */ +#define FLOOR( V ) ((V) >= 0 ? (int)(V) : (int)((V) - 1)) + +/* Precalculate a whole bunch of interpolation matricies. int (used for pel + * sizes up to short), and double (for all others). We go to scale + 1, so + * we can round-to-nearest safely. + + FIXME ... should use seperable tables really + + */ +static int im_affine_linear_int + [TRANSFORM_SCALE + 1][TRANSFORM_SCALE + 1][4]; +static double im_affine_linear_double + [TRANSFORM_SCALE + 1][TRANSFORM_SCALE + 1][4]; + +/* Make sure the interpolation tables are built. + */ +static void +affine_interpol_calc( void ) +{ + static int calced = 0; + int x, y; + + if( calced ) + return; + + for( x = 0; x < TRANSFORM_SCALE + 1; x++ ) + for( y = 0; y < TRANSFORM_SCALE + 1; y++ ) { + double X, Y, Xd, Yd; + double c1, c2, c3, c4; + + /* Interpolation errors. + */ + X = (double) x / TRANSFORM_SCALE; + Y = (double) y / TRANSFORM_SCALE; + Xd = 1.0 - X; + Yd = 1.0 - Y; + + /* Weights. + */ + c1 = Xd*Yd; + c2 = X*Yd; + c3 = X*Y; + c4 = Xd*Y; + + im_affine_linear_double[x][y][0] = c1; + im_affine_linear_double[x][y][1] = c2; + im_affine_linear_double[x][y][2] = c3; + im_affine_linear_double[x][y][3] = c4; + + im_affine_linear_int[x][y][0] = c1 * INTERPOL_SCALE; + im_affine_linear_int[x][y][1] = c2 * INTERPOL_SCALE; + im_affine_linear_int[x][y][2] = c3 * INTERPOL_SCALE; + im_affine_linear_int[x][y][3] = c4 * INTERPOL_SCALE; + } + + calced = 1; +} + +/* Calculate the inverse transformation. + */ +int +im__transform_calc_inverse( Transformation *trn ) +{ + DOUBLEMASK *msk, *msk2; + + if( !(msk = im_create_dmaskv( "boink", 2, 2, + trn->a, trn->b, trn->c, trn->d )) ) + return( -1 ); + if( !(msk2 = im_matinv( msk, "boink2" )) ) { + (void) im_free_dmask( msk ); + return( -1 ); + } + trn->ia = msk2->coeff[0]; + trn->ib = msk2->coeff[1]; + trn->ic = msk2->coeff[2]; + trn->id = msk2->coeff[3]; + (void) im_free_dmask( msk ); + (void) im_free_dmask( msk2 ); + + return( 0 ); +} + +/* Init a Transform. + */ +void +im__transform_init( Transformation *trn ) +{ + trn->oarea.left = 0; + trn->oarea.top = 0; + trn->oarea.width = -1; + trn->oarea.height = -1; + trn->iarea.left = 0; + trn->iarea.top = 0; + trn->iarea.width = -1; + trn->iarea.height = -1; + trn->a = 1.0; /* Identity transform */ + trn->b = 0.0; + trn->c = 0.0; + trn->d = 1.0; + trn->dx = 0.0; + trn->dy = 0.0; + + (void) im__transform_calc_inverse( trn ); +} + +/* Test for transform is identity function. + */ +int +im__transform_isidentity( Transformation *trn ) +{ + if( trn->a == 1.0 && trn->b == 0.0 && trn->c == 0.0 && + trn->d == 1.0 && trn->dx == 0.0 && trn->dy == 0.0 ) + return( 1 ); + else + return( 0 ); +} + +/* Map a pixel coordinate through the transform. + */ +void +im__transform_forward( Transformation *trn, + double x, double y, /* In input space */ + double *ox, double *oy ) /* In output space */ +{ + *ox = trn->a * x + trn->b * y + trn->dx; + *oy = trn->c * x + trn->d * y + trn->dy; +} + +/* Map a pixel coordinate through the inverse transform. + */ +void +im__transform_inverse( Transformation *trn, + double x, double y, /* In output space */ + double *ox, double *oy ) /* In input space */ +{ + double mx = x - trn->dx; + double my = y - trn->dy; + + *ox = trn->ia * mx + trn->ib * my; + *oy = trn->ic * mx + trn->id * my; +} + +/* Combine two transformations. out can be one of the ins. + */ +int +im__transform_add( Transformation *in1, Transformation *in2, + Transformation *out ) +{ + out->a = in1->a * in2->a + in1->c * in2->b; + out->b = in1->b * in2->a + in1->d * in2->b; + out->c = in1->a * in2->c + in1->c * in2->d; + out->d = in1->b * in2->c + in1->d * in2->d; + + out->dx = in1->dx * in2->a + in1->dy * in2->b + in2->dx; + out->dy = in1->dx * in2->c + in1->dy * in2->d + in2->dy; + + if( im__transform_calc_inverse( out ) ) + return( -1 ); + + return( 0 ); +} + +void +im__transform_print( Transformation *trn ) +{ + printf( "im__transform_print:\n" ); + printf( " iarea: left=%d, top=%d, width=%d, height=%d\n", + trn->iarea.left, + trn->iarea.top, + trn->iarea.width, + trn->iarea.height ); + printf( " oarea: left=%d, top=%d, width=%d, height=%d\n", + trn->oarea.left, + trn->oarea.top, + trn->oarea.width, + trn->oarea.height ); + printf( " mat: a=%g, b=%g, c=%g, d=%g\n", + trn->a, trn->b, trn->c, trn->d ); + printf( " off: dx=%g, dy=%g\n", + trn->dx, trn->dy ); +} + +/* Map a point through the inverse transform. Used for clipping calculations, + * so it takes account of iarea and oarea. + */ +static void +invert_point( Transformation *trn, + double x, double y, /* In output space */ + double *ox, double *oy ) /* In input space */ +{ + double xin = x - trn->oarea.left - trn->dx; + double yin = y - trn->oarea.top - trn->dy; + + /* Find the inverse transform of current (x, y) + */ + *ox = trn->ia * xin + trn->ib * yin; + *oy = trn->ic * xin + trn->id * yin; +} + +/* Given a bounding box for an area in the output image, set the bounding box + * for the corresponding pixels in the input image. + */ +static void +invert_rect( Transformation *trn, + Rect *in, /* In output space */ + Rect *out ) /* In input space */ +{ + double x1, y1; /* Map corners */ + double x2, y2; + double x3, y3; + double x4, y4; + double left, right, top, bottom; + + /* Map input Rect. + */ + invert_point( trn, in->left, in->top, &x1, &y1 ); + invert_point( trn, in->left, IM_RECT_BOTTOM(in), &x2, &y2 ); + invert_point( trn, IM_RECT_RIGHT(in), in->top, &x3, &y3 ); + invert_point( trn, IM_RECT_RIGHT(in), IM_RECT_BOTTOM(in), &x4, &y4 ); + + /* Find bounding box for these four corners. + */ + left = IM_MIN( x1, IM_MIN( x2, IM_MIN( x3, x4 ) ) ); + right = IM_MAX( x1, IM_MAX( x2, IM_MAX( x3, x4 ) ) ); + top = IM_MIN( y1, IM_MIN( y2, IM_MIN( y3, y4 ) ) ); + bottom = IM_MAX( y1, IM_MAX( y2, IM_MAX( y3, y4 ) ) ); + + /* Set output Rect. + */ + out->left = left; + out->top = top; + out->width = right - left + 1; + out->height = bottom - top + 1; + + /* Add a border for interpolation. You'd think +1 would do it, but + * we need to allow for rounding clipping as well. + + FIXME ... will need adjusting when we add bicubic + + */ + im_rect_marginadjust( out, 2 ); +} + +/* Interpolate a section ... int8/16 types. + */ +#define DO_IPEL(TYPE) { \ + TYPE *tq = (TYPE *) q; \ + \ + int c1 = im_affine_linear_int[xi][yi][0]; \ + int c2 = im_affine_linear_int[xi][yi][1]; \ + int c3 = im_affine_linear_int[xi][yi][2]; \ + int c4 = im_affine_linear_int[xi][yi][3]; \ + \ + /* p1 points to location (x_int, y_int) \ + * p2 " " " (x_int+1, y_int) \ + * p4 " " " (x_int+1, y_int+1) \ + * p3 " " " (x_int, y_int+1) \ + */ \ + PEL *p1 = (PEL *) IM_REGION_ADDR( ir, x_int, y_int ); \ + PEL *p2 = p1 + ofs2; \ + PEL *p3 = p1 + ofs3; \ + PEL *p4 = p1 + ofs4; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + TYPE *tp3 = (TYPE *) p3; \ + TYPE *tp4 = (TYPE *) p4; \ + \ + /* Interpolate each band. \ + */ \ + for( z = 0; z < in->Bands; z++ ) \ + tq[z] = (c1*tp1[z] + c2*tp2[z] + \ + c3*tp3[z] + c4*tp4[z]) >> INTERPOL_SHIFT; \ +} + +/* Interpolate a pel ... int32 and float types. + */ +#define DO_FPEL(TYPE) { \ + TYPE *tq = (TYPE *) q; \ + \ + double c1 = im_affine_linear_double[xi][yi][0]; \ + double c2 = im_affine_linear_double[xi][yi][1]; \ + double c3 = im_affine_linear_double[xi][yi][2]; \ + double c4 = im_affine_linear_double[xi][yi][3]; \ + \ + /* p1 points to location (x_int, y_int) \ + * p2 " " " (x_int+1, y_int) \ + * p4 " " " (x_int+1, y_int+1) \ + * p3 " " " (x_int, y_int+1) \ + */ \ + PEL *p1 = (PEL *) IM_REGION_ADDR( ir, x_int, y_int ); \ + PEL *p2 = p1 + ofs2; \ + PEL *p3 = p1 + ofs3; \ + PEL *p4 = p1 + ofs4; \ + TYPE *tp1 = (TYPE *) p1; \ + TYPE *tp2 = (TYPE *) p2; \ + TYPE *tp3 = (TYPE *) p3; \ + TYPE *tp4 = (TYPE *) p4; \ + \ + /* Interpolate each band. \ + */ \ + for( z = 0; z < in->Bands; z++ ) \ + tq[z] = c1*tp1[z] + c2*tp2[z] + \ + c3*tp3[z] + c4*tp4[z]; \ +} + +static int +affine_gen( REGION *or, REGION *ir, IMAGE *in, Transformation *trn ) +{ + /* Output area for this call. + */ + Rect *r = &or->valid; + int le = r->left; + int ri = IM_RECT_RIGHT(r); + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + Rect *iarea = &trn->iarea; + Rect *oarea = &trn->oarea; + int ps = IM_IMAGE_SIZEOF_PEL( in ); + int x, y, z; + + /* Interpolation variables. + */ + int ofs2, ofs3, ofs4; + + /* Clipping Rects. + */ + Rect image, need, clipped; + + /* Find the area of the input image we need. + */ + image.left = 0; + image.top = 0; + image.width = in->Xsize; + image.height = in->Ysize; + invert_rect( trn, r, &need ); + im_rect_intersectrect( &need, &image, &clipped ); + + /* Outside input image? All black. + */ + if( im_rect_isempty( &clipped ) ) { + im__black_region( or ); + return( 0 ); + } + + /* We do need some pixels from the input image to make our output - + * ask for them. + */ + if( im_prepare( ir, &clipped ) ) + return( -1 ); + +#ifdef DEBUG + printf( "affine: preparing left=%d, top=%d, width=%d, height=%d\n", + clipped.left, + clipped.top, + clipped.width, + clipped.height ); +#endif /*DEBUG*/ + + /* Calculate pel offsets. + */ + ofs2 = IM_IMAGE_SIZEOF_PEL( in ); + ofs3 = ofs2 + IM_REGION_LSKIP( ir ); + ofs4 = IM_REGION_LSKIP( ir ); + + /* Resample! + */ + for( y = to; y < bo; y++ ) { + /* Continuous cods in output space. + */ + double oy = y - oarea->top - trn->dy; + double ox; + + /* Input clipping rectangle. + */ + int ile = iarea->left; + int ito = iarea->top; + int iri = iarea->left + iarea->width; + int ibo = iarea->top + iarea->height; + + /* Derivative of matrix. + */ + double dx = trn->ia; + double dy = trn->ic; + + /* Continuous cods in input space. + */ + double ix, iy; + + PEL *q; + + q = (PEL *) IM_REGION_ADDR( or, le, y ); + ox = le - oarea->left - trn->dx; + + ix = trn->ia * ox + trn->ib * oy; + iy = trn->ic * ox + trn->id * oy; + + /* Offset ix/iy input by iarea.left/top ... so we skip the + * image edges we added for interpolation. + */ + ix += iarea->left; + iy += iarea->top; + + for( x = le; x < ri; x++ ) { + int fx, fy; + + fx = FLOOR( ix ); + fy = FLOOR( iy ); + + /* Clipping! Use >= for right/bottom, since IPOL needs + * to see one pixel more each way. + */ + if( fx < ile || fx >= iri || fy < ito || fy >= ibo ) { + for( z = 0; z < ps; z++ ) + q[z] = 0; + } + else { + double sx, sy; + int x_int, y_int; + int xi, yi; + + /* Subtract 0.5 to centre the bilinear. + + FIXME ... need to adjust for bicubic. + + */ + sx = ix - 0.5; + sy = iy - 0.5; + + /* Now go to scaled int. + */ + sx *= TRANSFORM_SCALE; + sy *= TRANSFORM_SCALE; + x_int = FLOOR( sx ); + y_int = FLOOR( sy ); + + /* Get index into interpolation table and + * unscaled integer position. + */ + xi = x_int & (TRANSFORM_SCALE - 1); + yi = y_int & (TRANSFORM_SCALE - 1); + x_int = x_int >> TRANSFORM_SHIFT; + y_int = y_int >> TRANSFORM_SHIFT; + + /* Interpolate for each input type. + */ + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + DO_IPEL( unsigned char ); + break; + case IM_BANDFMT_CHAR: + DO_IPEL( char ); + break; + case IM_BANDFMT_USHORT: + DO_IPEL( unsigned short ); + break; + case IM_BANDFMT_SHORT: + DO_IPEL( short ); + break; + case IM_BANDFMT_UINT: + DO_FPEL( unsigned int ); + break; + case IM_BANDFMT_INT: + DO_FPEL( int ); + break; + case IM_BANDFMT_FLOAT: + DO_FPEL( float ); + break; + case IM_BANDFMT_DOUBLE: + DO_FPEL( double ); + break; + + default: + error_exit( "im_affine: panic!"); + /*NOTREACHED*/ + } + } + + ix += dx; + iy += dy; + q += ps; + } + } + + return( 0 ); +} + +static int +affine( IMAGE *in, IMAGE *out, Transformation *trn ) +{ + Transformation *trn2; + double edge; + + if( im_iscomplex( in ) ) { + im_errormsg( "im_affine: complex input not supported" ); + return( -1 ); + } + + /* We output at (0,0), so displace output by that amount -ve to get + * output at (ox,oy). Alter our copy of trn. + */ + if( !(trn2 = IM_NEW( out, Transformation )) ) + return( -1 ); + *trn2 = *trn; + trn2->oarea.left = -trn->oarea.left; + trn2->oarea.top = -trn->oarea.top; + + if( im__transform_calc_inverse( trn2 ) ) + return( -1 ); + + /* Make output image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = trn2->oarea.width; + out->Ysize = trn2->oarea.height; + + /* Normally SMALLTILE ... except if this is a size up/down affine. + */ + if( trn->b == 0.0 && trn->c == 0.0 ) { + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + } + else { + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + } + + /* Check for coordinate overflow ... we want to be able to hold the + * output space inside INT_MAX / TRANSFORM_SCALE. + */ + edge = INT_MAX / TRANSFORM_SCALE; + if( trn2->oarea.left < -edge || trn2->oarea.top < -edge || + IM_RECT_RIGHT( &trn2->oarea ) > edge || + IM_RECT_BOTTOM( &trn2->oarea ) > edge ) { + im_errormsg( "im_affine: output coordinates out of range" ); + return( -1 ); + } + + /* Generate! + */ + if( im_generate( out, + im_start_one, affine_gen, im_stop_one, in, trn2 ) ) + return( -1 ); + + return( 0 ); +} + +/* As above, but do IM_CODING_LABQ too. And embed the input. + */ +int +im__affine( IMAGE *in, IMAGE *out, Transformation *trn ) +{ + IMAGE *t3 = im_open_local( out, "im_affine:3", "p" ); + Transformation trn2; + +#ifdef DEBUG_GEOMETRY + printf( "im__affine: %s\n", in->filename ); + im__transform_print( trn ); +#endif /*DEBUG_GEOMETRY*/ + + /* Add new pixels around the input so we can interpolate at the edges. + * Bilinear needs 0.5 pixels on all edges. + + FIXME ... will need to fiddle with this when we add bicubic + + */ + if( !t3 || + im_embed( in, t3, 1, + 1, 1, in->Xsize + 2, in->Ysize + 2 ) ) + return( -1 ); + + /* Set iarea so we know what part of the input we can take. + */ + trn2 = *trn; + trn2.iarea.left += 1; + trn2.iarea.top += 1; + + affine_interpol_calc(); + + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "im_affine:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_affine:2", "p" ); + + if( !t1 || !t2 || + im_LabQ2LabS( t3, t1 ) || + affine( t1, t2, &trn2 ) || + im_LabS2LabQ( t2, out ) ) + return( -1 ); + } + else if( in->Coding == IM_CODING_NONE ) { + if( affine( t3, out, &trn2 ) ) + return( -1 ); + } + else { + im_errormsg( "im_affine: unknown coding type" ); + return( -1 ); + } + + /* Finally: can now set Xoffset/Yoffset. + */ + out->Xoffset = trn->dx - trn->oarea.left; + out->Yoffset = trn->dy - trn->oarea.top; + + return( 0 ); +} + +int +im_affine( IMAGE *in, IMAGE *out, + double a, double b, double c, double d, double dx, double dy, + int ox, int oy, int ow, int oh ) +{ + Transformation trn; + + trn.oarea.left = ox; + trn.oarea.top = oy; + trn.oarea.width = ow; + trn.oarea.height = oh; + trn.iarea.left = 0; + trn.iarea.top = 0; + trn.iarea.width = in->Xsize; + trn.iarea.height = in->Ysize; + trn.a = a; + trn.b = b; + trn.c = c; + trn.d = d; + trn.dx = dx; + trn.dy = dy; + + return( im__affine( in, out, &trn ) ); +} diff --git a/libsrc/mosaicing/im_avgdxdy.c b/libsrc/mosaicing/im_avgdxdy.c new file mode 100644 index 00000000..e152d553 --- /dev/null +++ b/libsrc/mosaicing/im_avgdxdy.c @@ -0,0 +1,85 @@ +/* @(#) Function which averages the difference x_secondary[] - x_reference[] + * @(#) and y_secondary[] - y_reference[] of the structure points; + * @(#) The rounded integer result is returnd into dx, dy + * @(#) No IMAGES are involved in this function. + * @(#) + * @(#) int im_avgdxdy( points, dx, dy ) + * @(#) TIE_POINTS *points; + * @(#) int *dx, *dy; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__avgdxdy( TIE_POINTS *points, int *dx, int *dy ) +{ + int sumdx, sumdy; + int i; + + if( points->nopoints == 0 ) { + im_errormsg( "im_avgdxdy: no points to average" ); + return( -1 ); + } + + /* Lots of points. + */ + sumdx = 0; + sumdy = 0; + for( i = 0; i < points->nopoints; i++ ) { + sumdx += points->x_secondary[i] - points->x_reference[i]; + sumdy += points->y_secondary[i] - points->y_reference[i]; + } + + *dx = IM_RINT( (double) sumdx / (double) points->nopoints ); + *dy = IM_RINT( (double) sumdy / (double) points->nopoints ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_chkpair.c b/libsrc/mosaicing/im_chkpair.c new file mode 100644 index 00000000..13547a87 --- /dev/null +++ b/libsrc/mosaicing/im_chkpair.c @@ -0,0 +1,241 @@ +/* @(#) Functions which improve the selection of two ttie points pairs in two + * @(#) images, by estimating the correlation coefficient given in page 426 + * @(#) 2nd edn of the book Digital Image processing Gonzalez and Wintz + * @(#) The function works as follows: + * @(#) It expects to receive nopoints pairs (coordinates) of points + * @(#) corresponding to ref and sec. + * @(#) The coordinates of the pairs are in arrays x1,y1 and x2,y2 + * @(#) After that the program reads a region of 2*halfcorsize +1 pels centered + * @(#) at point (x1, y1) and looks around + * @(#) an area 2*halfareasize+1 centered at point (x2, y2). + * @(#) For each point in this 2*halfareasize+1, + * @(#) the program reads the corresponding + * @(#) image2 values in a region of 2*halfcorsize+1 pels centered at this point + * @(#) and calculates the corresponding correlation coefficients. + * @(#) The result is stored in a the array + * @(#) corcoef[(2*halfareasize+1)(2*halfareasize+1)]. Within this window, the + * @(#) max correlation coefficient is estimated and its corresponding + * @(#) (x, y) coordinates are returned in (x2, y2). + * @(#) The purpose of this function is to improve the selection of + * @(#) control points entered in (x1, y1) + * @(#) Both input images should are either memory mapped or in a buffer. + * @(#) The variable bandno should be between 1 and ref->Bands + * @(#) The program fills the dx[] and dy[] arrays before returning. + * @(#) + * @(#) int im__chkpair( ref, sec, bandno, points ) + * @(#) IMAGE *ref, *sec; + * @(#) int bandno; + * @(#) TIE_POINTS *points; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on : 18/04/1991 + * 8/7/93 JC + * - allows IM_CODING_LABQ coding + * - now calls im_incheck() + * 13/7/95 JC + * - rewritten + * - now uses im_spcor() + * 13/8/96 JC + * - order of args changed to help C++ API + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Find position of sec within ref. Search around point xsec, ysec for the + * best match for the area around xref, yref. Search an area of size + * hsearchsize for an of size hwindowsize. + * + * Return a new value for xsec, ysec and the correlation at that point. + * + * Also used by im_match_linear(), im_match_linear_search(), etc. + */ +int +im_correl( IMAGE *ref, IMAGE *sec, + int xref, int yref, int xsec, int ysec, + int hwindowsize, int hsearchsize, + double *correlation, int *x, int *y ) +{ + IMAGE *surface = im_open( "surface", "t" ); + IMAGE *t1, *t2, *t3, *t4; + + Rect refr, secr; + Rect winr, srhr; + Rect wincr, srhcr; + + if( !surface || + !(t1 = im_open_local( surface, "correlate:1", "p" )) || + !(t2 = im_open_local( surface, "correlate:1", "p" )) || + !(t3 = im_open_local( surface, "correlate:1", "p" )) || + !(t4 = im_open_local( surface, "correlate:1", "p" )) ) + return( -1 ); + + /* Find position of window and search area, and clip against image + * size. + */ + refr.left = 0; + refr.top = 0; + refr.width = ref->Xsize; + refr.height = ref->Ysize; + winr.left = xref - hwindowsize; + winr.top = yref - hwindowsize; + winr.width = hwindowsize*2 + 1; + winr.height = hwindowsize*2 + 1; + im_rect_intersectrect( &refr, &winr, &wincr ); + + secr.left = 0; + secr.top = 0; + secr.width = sec->Xsize; + secr.height = sec->Ysize; + srhr.left = xsec - hsearchsize; + srhr.top = ysec - hsearchsize; + srhr.width = hsearchsize*2 + 1; + srhr.height = hsearchsize*2 + 1; + im_rect_intersectrect( &secr, &srhr, &srhcr ); + + /* Extract window and search area. + */ + if( im_extract_area( ref, t1, + wincr.left, wincr.top, wincr.width, wincr.height ) || + im_extract_area( sec, t2, + srhcr.left, srhcr.top, srhcr.width, srhcr.height ) ) { + im_close( surface ); + return( -1 ); + } + + /* Make sure we have just one band. From im_*mosaic() we will, but + * from im_match_linear_search() etc. we may not. + */ + if( t1->Bands != 1 ) { + if( im_extract_band( t1, t3, 0 ) ) { + im_close( surface ); + return( -1 ); + } + t1 = t3; + } + if( t2->Bands != 1 ) { + if( im_extract_band( t2, t4, 0 ) ) { + im_close( surface ); + return( -1 ); + } + t2 = t4; + } + + /* Search! + */ + if( im_spcor( t2, t1, surface ) ) { + im_close( surface ); + return( -1 ); + } + + /* Find maximum of correlation surface. + */ + if( im_maxpos( surface, x, y, correlation ) ) { + im_close( surface ); + return( -1 ); + } + im_close( surface ); + + /* Translate back to position within sec. + */ + *x += srhcr.left; + *y += srhcr.top; + + return( 0 ); +} + +int +im__chkpair( IMAGE *ref, IMAGE *sec, TIE_POINTS *points ) +{ + int i; + int x, y; + double correlation; + + const int hcor = points->halfcorsize; + const int harea = points->halfareasize; + + /* Check images. + */ + if( im_incheck( ref ) || im_incheck( sec ) ) + return( -1 ); + if( ref->Bands != sec->Bands || ref->BandFmt != sec->BandFmt || + ref->Coding != sec->Coding ) { + im_errormsg( "im_chkpair: inputs incompatible"); + return( -1 ); + } + if( ref->Bands != 1 || ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_chkpair: help!" ); + return( -1 ); + } + + for( i = 0; i < points->nopoints; i++ ) { + /* Find correlation point. + */ + if( im_correl( ref, sec, + points->x_reference[i], points->y_reference[i], + points->x_reference[i], points->y_reference[i], + hcor, harea, + &correlation, &x, &y ) ) + return( -1 ); + + /* And note in x_secondary. + */ + points->x_secondary[i] = x; + points->y_secondary[i] = y; + points->correlation[i] = correlation; + + /* Note each dx, dy too. + */ + points->dx[i] = + points->x_secondary[i] - points->x_reference[i]; + points->dy[i] = + points->y_secondary[i] - points->y_reference[i]; + } + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_clinear.c b/libsrc/mosaicing/im_clinear.c new file mode 100644 index 00000000..0f180da7 --- /dev/null +++ b/libsrc/mosaicing/im_clinear.c @@ -0,0 +1,183 @@ +/* @(#) Function which calculates the coefficients between corresponding + * @(#) points from reference and secondary images (probably from the scanner), + * @(#) previously calculated using the functions im_calcon() and im_chpair() + * @(#) It is assummed that a selection of the best(?) possible points has + * @(#) been already carried out and that those nopoints points are in arrays + * @(#) x1, y1 and x2, y2 + * @(#) No IMAGES are involved in this function and the calculated parameters + * @(#) are returned in scale angle deltax and deltay of the TIE_POINTS struct. + * @(#) + * @(#) int im_clinear( points ) + * @(#) TIE_POINTS *points; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + * 24/1/97 JC + * - tiny mem leak fixed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__clinear( TIE_POINTS *points ) +{ + double **mat; /* matrix mar[4][4] */ + double *g; /* vector g[1][4] */ + double value; + double sx1=0.0, sx1x1=0.0, sy1=0.0, sy1y1=0.0, sx1y1 = 0.0; + double sx2x1=0.0, sx2y1=0.0, sx2=0.0, sy2=0.0, sy2y1=0.0, sy2x1=0.0; + + int i, j; + int elms; + double scale, angle, xdelta, ydelta; + int *xref, *yref, *xsec, *ysec; + double *dx, *dy, *dev; + double resx, resy; + + xref = &points->x_reference[0]; + yref = &points->y_reference[0]; + xsec = &points->x_secondary[0]; + ysec = &points->y_secondary[0]; + dx = &points->dx[0]; + dy = &points->dy[0]; + dev = &points->deviation[0]; + elms = points->nopoints; + + if( !(mat = im_dmat_alloc( 0, 3, 0, 3 )) ) + return( -1 ); + if( !(g = im_dvector( 0, 3 )) ) { + im_free_dmat( mat, 0, 3, 0, 3 ); + return( -1 ); + } + + resx = 0.0; + resy = 0.0; + for( i = 0; i < points->nopoints; i++ ) { + sx1 += xref[i]; + sx1x1 += xref[i] * xref[i]; + sy1 += yref[i]; + sy1y1 += yref[i] * yref[i]; + sx1y1 += xref[i] * yref[i]; + sx2x1 += xsec[i] * xref[i]; + sx2y1 += xsec[i] * yref[i]; + sy2y1 += ysec[i] * yref[i]; + sy2x1 += ysec[i] * xref[i]; + sx2 += xsec[i]; + sy2 += ysec[i]; + } + + resx = fabs( sx1-sx2 )/points->nopoints; + resy = fabs( sy1-sy2 )/points->nopoints; + + mat[0][0] = sx1x1 + sy1y1; + mat[0][1] = 0; + mat[0][2] = sx1; + mat[0][3] = sy1; + + mat[1][0] = 0; + mat[1][1] = sx1x1 + sy1y1; + mat[1][2] = -sy1; + mat[1][3] = sx1; + + mat[2][0] = sx1; + mat[2][1] = -sy1; + mat[2][2] = (double)elms; + mat[2][3] = 0.0; + + mat[3][0] = sy1; + mat[3][1] = sx1; + mat[3][2] = 0.0; + mat[3][3] = (double)elms; + + g[0] = sx2x1 + sy2y1; + g[1] = -sx2y1 + sy2x1; + g[2] = sx2; + g[3] = sy2; + + if( im_invmat( mat, 4 ) ) { + im_free_dmat( mat, 0, 3, 0, 3 ); + im_free_dvector( g, 0, 3 ); + im_errormsg( "im_clinear: im_invmat failed" ); + return( -1 ); + } + + scale = 0.0; angle = 0.0; + xdelta = 0.0; ydelta = 0.0; + + for( j = 0; j < 4; j++ ) { + scale += mat[0][j] * g[j]; + angle += mat[1][j] * g[j]; + xdelta += mat[2][j] * g[j]; + ydelta += mat[3][j] * g[j]; + } + + /* find the deviation of each point for the estimated variables + * if it greater than 1 then the solution is not good enough + * but this is handled by the main program + */ + for( i = 0; i < points->nopoints; i++ ) { + dx[i] = xsec[i] - + ((scale * xref[i]) - (angle * yref[i]) + xdelta); + + dy[i] = ysec[i] - + ((angle * xref[i]) + (scale * yref[i]) + ydelta); + + value = sqrt( dx[i]*dx[i] + dy[i]*dy[i] ); + dev[i] = value; + } + + points->l_scale = scale; + points->l_angle = angle; + points->l_deltax = xdelta; + points->l_deltay = ydelta; + + im_free_dmat( mat, 0, 3, 0, 3 ); + im_free_dvector( g, 0, 3 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_improve.c b/libsrc/mosaicing/im_improve.c new file mode 100644 index 00000000..3b1ca9a7 --- /dev/null +++ b/libsrc/mosaicing/im_improve.c @@ -0,0 +1,193 @@ +/* @(#) Function which improves the selection of tiepoints carried out by + * @(#) im_clinear() until no points have deviation greater than 1 pixel + * @(#) No reference or secondary images are involved + * @(#) Function im_improve assumes that im_clinear has been applied on points + * @(#) No IMAGES are involved in this function and the result is + * @(#) returned in outpoints which is declared as a pointer in the + * @(#) calling routine. Space for outpoints should be allocated in the calling + * @(#) routine + * @(#) + * @(#) int im_improve( inpoints, outpoints ) + * @(#) TIE_POINTS *inpoints, *outpoints; + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static void +copypoints( TIE_POINTS *pnew, TIE_POINTS *pold ) +{ + int i; + + pnew->reference = pold->reference; + pnew->secondary = pold->secondary; + + pnew->deltax = pold->deltax; + pnew->deltay = pold->deltay; + pnew->nopoints = pold->nopoints; + pnew->halfcorsize = pold->halfcorsize; + pnew->halfareasize = pold->halfareasize; + + for( i = 0; i < pold->nopoints; i++ ) { + pnew->x_reference[i] = pold->x_reference[i]; + pnew->y_reference[i] = pold->y_reference[i]; + pnew->x_secondary[i] = pold->x_secondary[i]; + pnew->y_secondary[i] = pold->y_secondary[i]; + pnew->contrast[i] = pold->contrast[i]; + pnew->correlation[i] = pold->correlation[i]; + pnew->deviation[i] = pold->deviation[i]; + pnew->dx[i] = pold->dx[i]; + pnew->dy[i] = pold->dy[i]; + } + + pnew->l_scale = pold->l_scale; + pnew->l_angle = pold->l_angle; + pnew->l_deltax = pold->l_deltax; + pnew->l_deltay = pold->l_deltay; +} + +/* exclude all points with deviation greater or equal to 1.0 pixel + */ +static int +copydevpoints( TIE_POINTS *pnew, TIE_POINTS *pold ) +{ + int i; + int j; + double thresh_dev,max_dev, min_dev; + double *corr; + + min_dev = 9999.0; + max_dev = 0.0; + corr = &pold->correlation[0]; + + for( i = 0; i < pold->nopoints; i++ ) + if( corr[i] > 0.01 ) { + if( pold->deviation[i]/corr[i] < min_dev ) + min_dev = pold->deviation[i]/corr[i] ; + if( pold->deviation[i]/corr[i] > max_dev ) + max_dev = pold->deviation[i]/corr[i]; + } + + thresh_dev = min_dev + (max_dev - min_dev)*0.3; + if( thresh_dev <= 1.0 ) + thresh_dev = 1.0; + + for( i = 0, j = 0; i < pold->nopoints; i++ ) + if( pold->correlation[i] > 0.01 ) + if( pold->deviation[i]/corr[i] <= thresh_dev ) { + pnew->x_reference[j] = pold->x_reference[i]; + pnew->y_reference[j] = pold->y_reference[i]; + pnew->x_secondary[j] = pold->x_secondary[i]; + pnew->y_secondary[j] = pold->y_secondary[i]; + pnew->contrast[j] = pold->contrast[i]; + pnew->correlation[j] = pold->correlation[i]; + pnew->deviation[j] = pold->deviation[i]; + pnew->dx[j] = pold->dx[i]; + pnew->dy[j] = pold->dy[i]; + j++; + } + pnew->nopoints = j; + + for( i = j; i < IM_MAXPOINTS; i++ ) { + pnew->x_reference[i] = 0; + pnew->y_reference[i] = 0; + pnew->x_secondary[i] = 0; + pnew->y_secondary[i] = 0; + pnew->contrast[i] = 0; + pnew->correlation[i] = 0.0; + pnew->deviation[i] = 0.0; + pnew->dx[i] = 0.0; + pnew->dy[i] = 0.0; + } + + /* Return non-zero if we changed something. + */ + if( j != pold->nopoints ) + return( -1 ); + + return( 0 ); +} + +#define SWAP( A, B ) { void *t = (A); A = B; B = t; } + +int +im__improve( TIE_POINTS *inpoints, TIE_POINTS *outpoints ) +{ + TIE_POINTS points1, points2; + TIE_POINTS *p = &points1; + TIE_POINTS *q = &points2; + + /* p has the current state - make a new state, q, with only those + * points which have a small deviation. + */ + for( copypoints( p, inpoints ); + copypoints( q, p ), copydevpoints( q, p ); ) { + /* If there are only a few left, jump out. + */ + if( q->nopoints < 2 ) + break; + + /* Fit the model to the new set of points. + */ + if( im__clinear( q ) ) + return( -1 ); + + /* And loop. + */ + SWAP( p, q ); + } + + /* q has the output - copy to outpoints. + */ + copypoints( outpoints, q ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_initialize.c b/libsrc/mosaicing/im_initialize.c new file mode 100644 index 00000000..8ab66af0 --- /dev/null +++ b/libsrc/mosaicing/im_initialize.c @@ -0,0 +1,100 @@ +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__initialize( TIE_POINTS *points ) +{ + if( im__clinear( points ) ) { + /* im_clinear failed! Set some sensible fallback values. + */ + int i, j; + double xdelta, ydelta, max_cor; + double a1, a2; + + int *xref = &points->x_reference[0]; + int *yref = &points->y_reference[0]; + int *xsec = &points->x_secondary[0]; + int *ysec = &points->y_secondary[0]; + + double *corr = &points->correlation[0]; + double *dx = &points->dx[0]; + double *dy = &points->dy[0]; + + int npt = points->nopoints; + + max_cor = 0.0; + for( i = 0; i < npt; i++ ) + if( corr[i] > max_cor ) + max_cor = corr[i]; + + max_cor = max_cor - 0.04; + xdelta = 0.0; + ydelta = 0.0; + j = 0; + for( i = 0; i < npt; i++ ) + if( corr[i] >= max_cor ) { + xdelta += xsec[i] - xref[i]; + ydelta += ysec[i] - yref[i]; + ++j; + } + + xdelta = xdelta/j; + ydelta = ydelta/j; + for(i = 0; i < npt; i++ ) { + dx[i] = (xsec[i] - xref[i]) - xdelta; + dy[i] = (ysec[i] - yref[i]) - ydelta; + } + + for( i = 0; i < npt; i++ ) { + a1 = dx[i]; + a2 = dy[i]; + points->deviation[i] = sqrt( a1*a1 + a2*a2 ); + } + + points->l_scale = 1.0; + points->l_angle = 0.0; + points->l_deltax = xdelta; + points->l_deltay = ydelta; + } + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_lrcalcon.c b/libsrc/mosaicing/im_lrcalcon.c new file mode 100644 index 00000000..368e0aeb --- /dev/null +++ b/libsrc/mosaicing/im_lrcalcon.c @@ -0,0 +1,318 @@ +/* @(#) Functions which takes an initial estimate of deltax, deltay + * @(#) between reference and secondary images (probably from the scanner), + * @(#) and looks in three areas of the overlapping part of the reference image + * @(#) corresponding to reference and secondary. For every other halfreasize + * @(#) point of the three areas of the reference image + * @(#) the contrast is calculated + * @(#) an area 2*halfcorsize+1 centered at this point + * @(#) Results are saved in the structure points + * @(#) The function expects the following valid data in points: + * @(#) deltax, deltay, nopoints, halfcorsize, halfareasize + * @(#) and fills in the memebers: + * @(#) x, y_reference[], contrast and x,y_secondary[], + * @(#) based on deltax and deltay + * @(#) Input image should are either memory mapped or in a buffer. + * @(#) The initial setting checks all points of reference + * @(#) in the overlapping area of the images to be mosaiced + * @(#) To speed up the procedure the ysize of the box can be reduced + * @(#) during the calculation of the ysize + * @(#) An easy way is to change FACTOR to 1 2 or 3. + * @(#) The calculation of the contrast is carried out based on bandno only. + * @(#) The variable bandno should be between 1 and ref->Bands + * @(#) + * @(#) int im_lrcalcon( ref, sec, bandno, points ) + * @(#) IMAGE *ref, *sec; + * @(#) int bandno; + * @(#) TIE_POINTS *points; see mosaic.h + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + * 8/7/93 JC + * - now calls im_incheck() + * 12/7/95 JC + * - reworked + * - what a lot of horrible old code there was too + * 24/1/97 JC + * - now ignores black stuff (all bands zero) when selecting possible tie + * points, part of new mosaic policy + * 26/9/97 JC + * - now skips all-black windows, instead of any-black windows + * 11/4/01 JC + * - ooops, < 0 should have been <= 0 + * 10/3/03 JC + * - better error message for overlap too small + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* A position and contrast. + */ +typedef struct { + int x, y; + int cont; +} PosCont; + +/* Search a window for black pelss ... true if window is all black. + * One-band uchar only. + */ +static int +all_black( IMAGE *im, int xpos, int ypos, int winsize ) +{ + const int hwinsize = (winsize - 1)/2; + const int left = xpos - hwinsize; + const int top = ypos - hwinsize; + const int ls = im->Xsize; + + int x, y; + PEL *line; + + /* Loop over image. + */ + line = (PEL *) im->data + top*ls + left; + for( y = 0; y < winsize; y++ ) { + for( x = 0; x < winsize; x++ ) + if( line[x] ) + /* Not all black. + */ + return( 0 ); + + line += ls; + } + + return( -1 ); +} + +/* Calculate a value for 'contrast' within a window + * of (odd) size winsize*winsize centered at location (xpos, ypos). + * One band uchar only, + */ +static int +calculate_contrast( IMAGE *im, int xpos, int ypos, int winsize ) +{ + const int hwinsize = (winsize - 1)/2; + const int left = xpos - hwinsize; + const int top = ypos - hwinsize; + const int ls = im->Xsize; + + int x, y; + PEL *line, *p; + int total; + + line = (PEL *) im->data + top*ls + left; + for( total = 0, y = 0; y < winsize-1; y++ ) { + p = line; + + for( x = 0; x < winsize-1; x++ ) { + const int lrd = (int) p[0] - p[1]; + const int tbd = (int) p[0] - p[ls]; + + total += abs( lrd ) + abs( tbd ); + p += 1; + } + + line += ls; + } + + return( total ); +} + +/* Compare two PosConts for qsort. + */ +static int +pos_compare( const void *vl, const void *vr ) +{ + PosCont *l = (PosCont *) vl; + PosCont *r = (PosCont *) vr; + + return( r->cont - l->cont ); +} + +/* Search an area for the n best contrast areas. + */ +int +im__find_best_contrast( IMAGE *im, + int xpos, int ypos, int xsize, int ysize, + int xarray[], int yarray[], int cont[], + int nbest, int hcorsize ) +{ + /* Geometry: we test squares of size windowsize, overlapping by + * hcorsize. + */ + const int windowsize = 2 * hcorsize + 1; + + /* Number of squares we can fit in area. + */ + const int nacross = (xsize - windowsize + hcorsize) / hcorsize; + const int ndown = (ysize - windowsize + hcorsize) / hcorsize; + + /* Number of squares we search. + */ + int elms; + + /* All points in this area. + */ + PosCont *pc; + + int x, y, i; + + if( nacross <= 0 || ndown <= 0 ) { + im_errormsg( "im__lrcalcon: mosaicing overlap too small for " + "your search size" ); + return( -1 ); + } + + /* Malloc space for 3 int arrays, to keep the int coordinates and + * the contrast. + */ + if( !(pc = IM_ARRAY( NULL, nacross * ndown, PosCont )) ) + return( -1 ); + + /* Find contrast for each area. + */ + for( i = 0, y = 0; y < ndown; y++ ) + for( x = 0; x < nacross; x++ ) { + const int left = xpos + x * hcorsize; + const int top = ypos + y * hcorsize; + + /* Skip this position if it is all black. + */ + if( all_black( im, left, top, windowsize ) ) + continue; + + /* Find contrast and note. + */ + pc[i].x = left; + pc[i].y = top; + pc[i].cont = calculate_contrast( im, + left, top, windowsize ); + i++; + } + + /* Note number found. + */ + elms = i; + + /* Found enough tie-points? + */ + if( elms < nbest ) { + im_errormsg( "im_mosaic: found %d tie-points, need at least %d", + elms, nbest ); + im_free( pc ); + return( -1 ); + } + + /* Sort areas by contrast. + */ + qsort( pc, elms, sizeof( PosCont ), pos_compare ); + + /* Copy the n best into our parent. + */ + for( i = 0; i < nbest; i++ ) { + xarray[i] = pc[i].x; + yarray[i] = pc[i].y; + cont[i] = pc[i].cont; + } + im_free( pc ); + + return( 0 ); +} + +int +im__lrcalcon( IMAGE *ref, TIE_POINTS *points ) +{ + /* Geometry: border we must leave around each area. + */ + const int border = points->halfareasize; + + /* Height of an area. + */ + const int aheight = ref->Ysize / AREAS; + + /* Number of points we find in each area. + */ + const int len = points->nopoints / AREAS; + + int i; + Rect area; + + /* Make sure we can read image. + */ + if( im_incheck( ref ) ) + return( -1 ); + if( ref->Bands != 1 || ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im__lrcalcon: not 1-band uchar image" ); + return( -1 ); + } + + /* Define bits to search for high-contrast areas. Need to be able to + * fit at least 1 window in. + */ + area.height = aheight; + area.width = ref->Xsize; + area.left = 0; + area.top = 0; + im_rect_marginadjust( &area, -border ); + area.width--; + area.height--; + + /* Loop over areas, finding points. + */ + for( i = 0; area.top < ref->Ysize; area.top += aheight, i++ ) + if( im__find_best_contrast( ref, + area.left, area.top, area.width, area.height, + points->x_reference + i*len, + points->y_reference + i*len, + points->contrast + i*len, + len, + points->halfcorsize ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_lrmerge.c b/libsrc/mosaicing/im_lrmerge.c new file mode 100644 index 00000000..a9c6901b --- /dev/null +++ b/libsrc/mosaicing/im_lrmerge.c @@ -0,0 +1,1128 @@ +/* Merge two images left-right. dx, dy is the offset needed to get from sec + * (secondary image) to ref (reference image). + * + * Usage: + * + * int + * im_lrmerge( ref, sec, out, dx, dy ) + * IMAGE *ref, *sec, *out; + * int dx, dy; + * + * Returns 0 on success and -1 on error + * + * Copyright: 1990, 1991 N. Dessipris + * Author: N. Dessipris + * Written on: 20/09/1990 + * Updated on: 17/04/1991 + * 1/6/92: JC + * - check for difference bug fixed + * - geometry calculations improved and simplified + * - small speedups + Kirk Martinez for Sys5 29/4/93 + * 7/8/93 JC + * - ANSIfied + * - memory leaks fixed, ready for partial v2 + * - now does IM_CODING_LABQ too + * 8/11/93 JC + * - now propogates both input histories + * - adds magic lines for global mosaic optimisation + * + * + * + May/1994 Ahmed Abbood + * + * - Modified to use partials on all IO + * + June/1995 Ahmed Abbood + * + * - Modified to work with different types of images. + * + * 16/6/95 JC + * - tidied up a little + * - added to VIPS! + * 7/9/95 JC + * - split into two parts: im_lrmerge() and im__lrmerge() + * - latter called by im_lrmosaic() + * - just the same as public im_lrmerge(), but adds no history + * - necessary for im_global_balance() + * - small bugs fixed + * 10/10/95 JC + * - better checks that parameters are sensible + * 11/10/95 JC + * - Kirk spotted what a load of rubbish Ahmed's code is + * - rewritten - many, many bugs fixed + * 24/1/97 JC + * - now outputs bounding area of input images, rather than clipping + * - ignores 0 pixels in blend + * - small tidies + * 7/2/97 JC + * - new blend, caching + * 25/2/97 JC + * - old blend back, much simpler + * - speed this up at some point if you think of an easy way to do it + * 29/7/97 JC + * - IM_CODING_LABQ blend now works, was bug in im_wrapone() + * - small tidies + * 10/1/98 JC + * - merge LUTs now shared between all running mergers + * - frees memory explicitly in im__stop_merge, for much better memory + * use in large mosaics, huge improvement! + * 18/2/98 JC + * - im_demand_hint() call added + * 19/2/98 JC + * - now works for any dx/dy by calling im_insert() for bizarre cases + * 26/9/99 JC + * - ooops, blend lut was wrong! wonder how long that's been broken, + * since feb97 I guess + * 2/2/01 JC + * - added tunable max blend width + * 8/3/01 JC + * - switched to integer arithmetic for integer blends + * 7/11/01 JC + * - more sophisticated transparency handling + * - tiny blend speed up + * 19/3/02 JC + * - move fl cache to main state for better sharing + * 15/8/02 JC + * - records Xoffset/Yoffset + * 20/6/05 + * - now requires all bands == 0 for transparency (used to just check + * band 0) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +/* +#define DEBUG + */ + +#include +#include + +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Blend luts. Shared between all lr and tb blends. + */ +double *im__coef1 = NULL; +double *im__coef2 = NULL; +int *im__icoef1 = NULL; +int *im__icoef2 = NULL; + +/* Create a lut for the merging area. Always BLEND_SIZE entries, we + * scale later when we index it. + */ +int +im__make_blend_luts() +{ + int x; + + /* Already done? + */ + if( im__coef1 && im__coef2 ) + return( 0 ); + + /* Allocate and fill. + */ + im__coef1 = IM_ARRAY( NULL, BLEND_SIZE, double ); + im__coef2 = IM_ARRAY( NULL, BLEND_SIZE, double ); + im__icoef1 = IM_ARRAY( NULL, BLEND_SIZE, int ); + im__icoef2 = IM_ARRAY( NULL, BLEND_SIZE, int ); + if( !im__coef1 || !im__coef2 || !im__icoef1 || !im__icoef2 ) + return( -1 ); + + for( x = 0; x < BLEND_SIZE; x++ ) { + double a = IM_PI * x / (BLEND_SIZE - 1.0); + + im__coef1[x] = (cos( a ) + 1.0) / 2.0; + im__coef2[x] = 1.0 - im__coef1[x]; + im__icoef1[x] = im__coef1[x] * BLEND_SCALE; + im__icoef2[x] = im__coef2[x] * BLEND_SCALE; + } + + return( 0 ); +} + +/* Return the position of the first non-zero pel from the left. + */ +static int +find_first( REGION *ir, int *pos, int x, int y, int w ) +{ + PEL *pr = (PEL *) IM_REGION_ADDR( ir, x, y ); + IMAGE *im = ir->im; + int ne = w * im->Bands; + int i; + + /* Double the number of bands in a complex. + */ + if( im_iscomplex( im ) ) + ne *= 2; + +/* Search for the first non-zero band element from the left edge of the image. + */ +#define lsearch( TYPE ) { \ + TYPE *p = (TYPE *) pr; \ + \ + for( i = 0; i < ne; i++ ) \ + if( p[i] )\ + break;\ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: lsearch( unsigned char ); break; + case IM_BANDFMT_CHAR: lsearch( signed char ); break; + case IM_BANDFMT_USHORT: lsearch( unsigned short ); break; + case IM_BANDFMT_SHORT: lsearch( signed short ); break; + case IM_BANDFMT_UINT: lsearch( unsigned int ); break; + case IM_BANDFMT_INT: lsearch( signed int ); break; + case IM_BANDFMT_FLOAT: lsearch( float ); break; + case IM_BANDFMT_DOUBLE: lsearch( double ); break; + case IM_BANDFMT_COMPLEX:lsearch( float ); break; + case IM_BANDFMT_DPCOMPLEX:lsearch( double ); break; + + default: + im_errormsg( "im_lrmerge: internal error" ); + return( -1 ); + } + + /* i is first non-zero band element, we want first non-zero pixel. + */ + *pos = x + i / im->Bands; + + return( 0 ); +} + +/* Return the position of the first non-zero pel from the right. + */ +static int +find_last( REGION *ir, int *pos, int x, int y, int w ) +{ + PEL *pr = (PEL *) IM_REGION_ADDR( ir, x, y ); + IMAGE *im = ir->im; + int ne = w * im->Bands; + int i; + + /* Double the number of bands in a complex. + */ + if( im_iscomplex( im ) ) + ne *= 2; + +/* Search for the first non-zero band element from the right. + */ +#define rsearch( TYPE ) { \ + TYPE *p = (TYPE *) pr; \ + \ + for( i = ne - 1; i >= 0; i-- )\ + if( p[i] )\ + break;\ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: rsearch( unsigned char ); break; + case IM_BANDFMT_CHAR: rsearch( signed char ); break; + case IM_BANDFMT_USHORT: rsearch( unsigned short ); break; + case IM_BANDFMT_SHORT: rsearch( signed short ); break; + case IM_BANDFMT_UINT: rsearch( unsigned int ); break; + case IM_BANDFMT_INT: rsearch( signed int ); break; + case IM_BANDFMT_FLOAT: rsearch( float ); break; + case IM_BANDFMT_DOUBLE: rsearch( double ); break; + case IM_BANDFMT_COMPLEX:rsearch( float ); break; + case IM_BANDFMT_DPCOMPLEX:rsearch( double ); break; + + default: + im_errormsg( "im_lrmerge: internal error" ); + return( -1 ); + } + + /* i is first non-zero band element, we want first non-zero pixel. + */ + *pos = x + i / im->Bands; + + return( 0 ); +} + +/* Make sure we have first/last for this area. + */ +static int +make_firstlast( MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + Rect rr, sr; + int y, yr, ys; + int missing; + + /* We're going to build first/last ... lock it from other generate + * threads. In fact it's harmless if we do get two writers, but we may + * avoid duplicating work. + */ + g_mutex_lock( ovlap->fl_lock ); + + /* Do we already have first/last for this area? Bail out if we do. + */ + missing = 0; + for( y = oreg->top; y < IM_RECT_BOTTOM( oreg ); y++ ) { + const int j = y - ovlap->overlap.top; + const int first = ovlap->first[j]; + + if( first < 0 ) { + missing = 1; + break; + } + } + if( !missing ) { + /* No work to do! + */ + g_mutex_unlock( ovlap->fl_lock ); + return( 0 ); + } + + /* Entire width of overlap in ref for scan-lines we want. + */ + rr.left = ovlap->overlap.left; + rr.top = oreg->top; + rr.width = ovlap->overlap.width; + rr.height = oreg->height; + rr.left -= ovlap->rarea.left; + rr.top -= ovlap->rarea.top; + + /* Entire width of overlap in sec for scan-lines we want. + */ + sr.left = ovlap->overlap.left; + sr.top = oreg->top; + sr.width = ovlap->overlap.width; + sr.height = oreg->height; + sr.left -= ovlap->sarea.left; + sr.top -= ovlap->sarea.top; + +#ifdef DEBUG + printf( "im__lrmerge: making first/last for areas:\n" ); + printf( "ref: left = %d, top = %d, width = %d, height = %d\n", + rr.left, rr.top, rr.width, rr.height ); + printf( "sec: left = %d, top = %d, width = %d, height = %d\n", + sr.left, sr.top, sr.width, sr.height ); +#endif + + /* Make pixels. + */ + if( im_prepare( rir, &rr ) || im_prepare( sir, &sr ) ) { + g_mutex_unlock( ovlap->fl_lock ); + return( -1 ); + } + + /* Make first/last cache. + */ + for( y = oreg->top, yr = rr.top, ys = sr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + const int j = y - ovlap->overlap.top; + int *first = &ovlap->first[j]; + int *last = &ovlap->last[j]; + + /* Done this line already? + */ + if( *first < 0 ) { + /* Search for start/end of overlap on this scan-line. + */ + if( find_first( sir, first, + sr.left, ys, sr.width ) || + find_last( rir, last, + rr.left, yr, rr.width ) ) { + g_mutex_unlock( ovlap->fl_lock ); + return( -1 ); + } + + /* Translate to output space. + */ + *first += ovlap->sarea.left; + *last += ovlap->rarea.left; + + /* Clip to maximum blend width, if necessary. + */ + if( ovlap->mwidth >= 0 && + *last - *first > ovlap->mwidth ) { + int shrinkby = (*last - *first) - ovlap->mwidth; + + *first += shrinkby / 2; + *last -= shrinkby / 2; + } + } + } + + g_mutex_unlock( ovlap->fl_lock ); + + return( 0 ); +} + +/* Test pixel == 0. + */ +#define TEST_ZERO( TYPE, T, RESULT ) { \ + TYPE *tt = (T); \ + int ii; \ + \ + for( ii = 0; ii < cb; ii++ ) \ + if( tt[i] ) \ + break; \ + if( ii == cb ) \ + (RESULT) = 1; \ +} + +/* Blend two integer images. + */ +#define iblend( TYPE, B, IN1, IN2, OUT ) { \ + TYPE *tr = (TYPE *) (IN1); \ + TYPE *ts = (TYPE *) (IN2); \ + TYPE *tq = (TYPE *) (OUT); \ + const int cb = (B); \ + const int left = IM_CLIP( 0, first - oreg->left, oreg->width ); \ + const int right = IM_CLIP( left, last - oreg->left, oreg->width ); \ + int ref_zero; \ + int sec_zero; \ + int x, b; \ + int i; \ + \ + /* Left of the blend area. \ + */ \ + for( i = 0, x = 0; x < left; x++ ) { \ + ref_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + \ + /* In blend area. \ + */ \ + for( x = left; x < right; x++ ) { \ + ref_zero = 0; \ + sec_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + \ + if( !ref_zero && !sec_zero ) { \ + int inx = ((x + oreg->left - first) << \ + BLEND_SHIFT) / bwidth; \ + int c1 = im__icoef1[inx]; \ + int c2 = im__icoef2[inx]; \ + \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = c1 * tr[i] / BLEND_SCALE + \ + c2 * ts[i] / BLEND_SCALE; \ + } \ + else if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + \ + /* Right of blend. + */ \ + for( x = right; x < oreg->width; x++ ) { \ + sec_zero = 0; \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + if( !sec_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + } \ +} + +/* Blend two float images. + */ +#define fblend( TYPE, B, IN1, IN2, OUT ) { \ + TYPE *tr = (TYPE *) (IN1); \ + TYPE *ts = (TYPE *) (IN2); \ + TYPE *tq = (TYPE *) (OUT); \ + const int cb = (B); \ + const int left = IM_CLIP( 0, first - oreg->left, oreg->width ); \ + const int right = IM_CLIP( left, last - oreg->left, oreg->width ); \ + int ref_zero; \ + int sec_zero; \ + int x, b; \ + int i; \ + \ + /* Left of the blend area. \ + */ \ + for( i = 0, x = 0; x < left; x++ ) { \ + ref_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + \ + /* In blend area. \ + */ \ + for( x = left; x < right; x++ ) { \ + ref_zero = 0; \ + sec_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + \ + if( !ref_zero && !sec_zero ) { \ + int inx = ((x + oreg->left - first) << \ + BLEND_SHIFT) / bwidth; \ + double c1 = im__coef1[inx]; \ + double c2 = im__coef2[inx]; \ + \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = c1 * tr[i] + c2 * ts[i]; \ + } \ + else if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + \ + /* Right of blend. + */ \ + for( x = right; x < oreg->width; x++ ) { \ + sec_zero = 0; \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + if( !sec_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + } \ +} + +/* Left-right blend function for non-labpack images. + */ +static int +lr_blend( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + IMAGE *im = or->im; + + Rect prr, psr; + int y, yr, ys; + + /* Make sure we have a complete first/last set for this area. + */ + if( make_firstlast( inf, ovlap, oreg ) ) + return( -1 ); + + /* Part of rr which we will output. + */ + prr = *oreg; + prr.left -= ovlap->rarea.left; + prr.top -= ovlap->rarea.top; + + /* Part of sr which we will output. + */ + psr = *oreg; + psr.left -= ovlap->sarea.left; + psr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &prr ) ) + return( -1 ); + if( im_prepare( sir, &psr ) ) + return( -1 ); + + /* Loop down overlap area. + */ + for( y = oreg->top, yr = prr.top, ys = psr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + PEL *pr = (PEL *) IM_REGION_ADDR( rir, prr.left, yr ); + PEL *ps = (PEL *) IM_REGION_ADDR( sir, psr.left, ys ); + PEL *q = (PEL *) IM_REGION_ADDR( or, oreg->left, y ); + + const int j = y - ovlap->overlap.top; + const int first = ovlap->first[j]; + const int last = ovlap->last[j]; + const int bwidth = last - first; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + iblend( unsigned char, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_CHAR: + iblend( signed char, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_USHORT: + iblend( unsigned short, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_SHORT: + iblend( signed short, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_UINT: + iblend( unsigned int, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_INT: + iblend( signed int, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_FLOAT: + fblend( float, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_DOUBLE: + fblend( double, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_COMPLEX: + fblend( float, im->Bands*2, pr, ps, q ); break; + case IM_BANDFMT_DPCOMPLEX: + fblend( double, im->Bands*2, pr, ps, q ); break; + + default: + im_errormsg( "im_lrmerge: internal error" ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Left-right blend function for IM_CODING_LABQ images. + */ +static int +lr_blend_labpack( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + Rect prr, psr; + int y, yr, ys; + + /* Make sure we have a complete first/last set for this area. This + * will just look at the top 8 bits of L, not all 10, but should be OK. + */ + if( make_firstlast( inf, ovlap, oreg ) ) + return( -1 ); + + /* Part of rr which we will output. + */ + prr = *oreg; + prr.left -= ovlap->rarea.left; + prr.top -= ovlap->rarea.top; + + /* Part of sr which we will output. + */ + psr = *oreg; + psr.left -= ovlap->sarea.left; + psr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &prr ) ) + return( -1 ); + if( im_prepare( sir, &psr ) ) + return( -1 ); + + /* Loop down overlap area. + */ + for( y = oreg->top, yr = prr.top, ys = psr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + PEL *pr = (PEL *) IM_REGION_ADDR( rir, prr.left, yr ); + PEL *ps = (PEL *) IM_REGION_ADDR( sir, psr.left, ys ); + PEL *q = (PEL *) IM_REGION_ADDR( or, oreg->left, y ); + + const int j = y - ovlap->overlap.top; + const int first = ovlap->first[j]; + const int last = ovlap->last[j]; + const int bwidth = last - first; + + float *fq = inf->merge; + float *r = inf->from1; + float *s = inf->from2; + + /* Unpack two bits we want. + */ + imb_LabQ2Lab( pr, r, oreg->width ); + imb_LabQ2Lab( ps, s, oreg->width ); + + /* Blend as floats. + */ + fblend( float, 3, r, s, fq ); + + /* Re-pack to output buffer. + */ + imb_Lab2LabQ( inf->merge, q, oreg->width ); + } + + return( 0 ); +} + +static void * +lock_free( GMutex *lock ) +{ + g_mutex_free( lock ); + + return( NULL ); +} + +/* Build basic per-call state and do some geometry calculations. Shared with + * im_tbmerge, so not static. + */ +Overlapping * +im__build_mergestate( IMAGE *ref, IMAGE *sec, IMAGE *out, + int dx, int dy, int mwidth ) +{ + Overlapping *ovlap = IM_NEW( out, Overlapping ); + int x; + + if( !ovlap ) + return( NULL ); + if( mwidth < -1 ) { + im_errormsg( "im_lr/tbmerge: mwidth must be -1 or >= 0" ); + return( NULL ); + } + + ovlap->ref = ref; + ovlap->sec = sec; + ovlap->out = out; + ovlap->dx = dx; + ovlap->dy = dy; + ovlap->mwidth = mwidth; + + /* Area occupied by ref image. Place at (0,0) to start with. + */ + ovlap->rarea.left = 0; + ovlap->rarea.top = 0; + ovlap->rarea.width = ref->Xsize; + ovlap->rarea.height = ref->Ysize; + + /* Area occupied by sec image. + */ + ovlap->sarea.left = -dx; + ovlap->sarea.top = -dy; + ovlap->sarea.width = sec->Xsize; + ovlap->sarea.height = sec->Ysize; + + /* Compute overlap. + */ + im_rect_intersectrect( &ovlap->rarea, &ovlap->sarea, &ovlap->overlap ); + if( im_rect_isempty( &ovlap->overlap ) ) { + im_errormsg( "im_lr/tbmerge: no overlap" ); + return( NULL ); + } + + /* Find position and size of output image. + */ + im_rect_unionrect( &ovlap->rarea, &ovlap->sarea, &ovlap->oarea ); + + /* Now: translate everything, so that the output image, not the left + * image, is at (0,0). + */ + ovlap->rarea.left -= ovlap->oarea.left; + ovlap->rarea.top -= ovlap->oarea.top; + ovlap->sarea.left -= ovlap->oarea.left; + ovlap->sarea.top -= ovlap->oarea.top; + ovlap->overlap.left -= ovlap->oarea.left; + ovlap->overlap.top -= ovlap->oarea.top; + ovlap->oarea.left = 0; + ovlap->oarea.top = 0; + + /* Make sure blend luts are built. + */ + im__make_blend_luts(); + + /* Size of first/last cache. Could be either of these ... just pick + * the larger. + */ + ovlap->flsize = IM_MAX( ovlap->overlap.width, ovlap->overlap.height ); + + /* Build first/last cache. + */ + ovlap->first = IM_ARRAY( out, ovlap->flsize, int ); + ovlap->last = IM_ARRAY( out, ovlap->flsize, int ); + if( !ovlap->first || !ovlap->last ) + return( NULL ); + for( x = 0; x < ovlap->flsize; x++ ) + ovlap->first[x] = -1; + + ovlap->fl_lock = g_mutex_new(); + if( im_add_close_callback( out, + (im_callback_fn) lock_free, ovlap->fl_lock, NULL ) ) { + g_mutex_free( ovlap->fl_lock ); + return( NULL ); + } + + return( ovlap ); +} + +/* Build per-call state. + */ +static Overlapping * +build_lrstate( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + Overlapping *ovlap; + + if( !(ovlap = im__build_mergestate( ref, sec, out, dx, dy, mwidth )) ) + return( NULL ); + + /* Select blender. + */ + switch( ref->Coding ) { + case IM_CODING_LABQ: + ovlap->blend = lr_blend_labpack; + break; + + case IM_CODING_NONE: + ovlap->blend = lr_blend; + break; + + default: + im_errormsg( "im_lrmerge: unknown coding type" ); + return( NULL ); + } + + /* Find the parts of output which come just from ref and just from sec. + */ + ovlap->rpart = ovlap->rarea; + ovlap->spart = ovlap->sarea; + ovlap->rpart.width -= ovlap->overlap.width; + ovlap->spart.left += ovlap->overlap.width; + ovlap->spart.width -= ovlap->overlap.width; + + /* Is there too much overlap? ie. right edge of ref image is greater + * than right edge of sec image, or left > left. + */ + if( IM_RECT_RIGHT( &ovlap->rarea ) > IM_RECT_RIGHT( &ovlap->sarea ) || + ovlap->rarea.left > ovlap->sarea.left ) { + im_errormsg( "im_lrmerge: too much overlap" ); + return( NULL ); + } + + /* Max number of pixels we may have to blend over. + */ + ovlap->blsize = ovlap->overlap.width; + + return( ovlap ); +} + +/* The area being demanded can be filled using only pels from either the ref + * or the sec images. Attach output to the appropriate part of the input image. + * area is the position that ir->im occupies in the output image. + * + * Shared with im_tbmerge(), so not static. + */ +int +im__attach_input( REGION *or, REGION *ir, Rect *area ) +{ + Rect r = or->valid; + + /* Translate to source coordinate space. + */ + r.left -= area->left; + r.top -= area->top; + + /* Demand input. + */ + if( im_prepare( ir, &r ) ) + return( -1 ); + + /* Attach or to ir. + */ + if( im_region_region( or, ir, &or->valid, r.left, r.top ) ) + return( -1 ); + + return( 0 ); +} + +/* The area being demanded requires pixels from the ref and sec images. As + * above, but just do a sub-area of the output, and make sure we copy rather + * than just pointer-fiddling. reg is the sub-area of or->valid we should do. + * + * Shared with im_tbmerge(), so not static. + */ +int +im__copy_input( REGION *or, REGION *ir, Rect *area, Rect *reg ) +{ + Rect r = *reg; + + /* Translate to source coordinate space. + */ + r.left -= area->left; + r.top -= area->top; + + /* Paint this area of ir into or. + */ + if( im_prepare_to( ir, or, &r, reg->left, reg->top ) ) + return( -1 ); + + return( 0 ); +} + +/* Black out a region. + */ +void +im__black_region( REGION *reg ) +{ + PEL *q = (PEL *) IM_REGION_ADDR( reg, reg->valid.left, reg->valid.top ); + int wd = IM_REGION_SIZEOF_LINE( reg ); + int ls = IM_REGION_LSKIP( reg ); + int y; + + for( y = 0; y < reg->valid.height; y++, q += ls ) + memset( (char *) q, 0, wd ); +} + +/* Generate function for merge. This is shared between im_lrmerge() and + * im_tbmerge(). + */ +int +im__merge_gen( REGION *or, MergeInfo *inf, Overlapping *ovlap ) +{ + Rect *r = &or->valid; + Rect rreg, sreg, oreg; + + /* Find intersection with overlap, ref and sec parts. + */ + im_rect_intersectrect( r, &ovlap->rpart, &rreg ); + im_rect_intersectrect( r, &ovlap->spart, &sreg ); + + /* Do easy cases first: can we satisfy this demand with pixels just + * from ref, or just from sec. + */ + if( im_rect_equalsrect( r, &rreg ) ) { + if( im__attach_input( or, inf->rir, &ovlap->rarea ) ) + return( -1 ); + } + else if( im_rect_equalsrect( r, &sreg ) ) { + if( im__attach_input( or, inf->sir, &ovlap->sarea ) ) + return( -1 ); + } + else { + /* Difficult case - do in three stages: black out whole area, + * copy in parts of ref and sec we touch, write blend area. + * This could be sped up somewhat ... we will usually black + * out far too much, and write to the blend area three times. + * Upgrade in the future! + */ + + /* Need intersections with whole of left & right, and overlap + * too. + */ + im_rect_intersectrect( r, &ovlap->rarea, &rreg ); + im_rect_intersectrect( r, &ovlap->sarea, &sreg ); + im_rect_intersectrect( r, &ovlap->overlap, &oreg ); + + im__black_region( or ); + if( !im_rect_isempty( &rreg ) ) + if( im__copy_input( or, + inf->rir, &ovlap->rarea, &rreg ) ) + return( -1 ); + if( !im_rect_isempty( &sreg ) ) + if( im__copy_input( or, + inf->sir, &ovlap->sarea, &sreg ) ) + return( -1 ); + + /* Nasty: inf->rir and inf->sir now point to the same bit of + * memory (part of or), and we've written twice. We need to + * make sure we get fresh pixels for the blend, so we must + * invalidate them both. Should maybe add a call to the API + * for this. + */ + inf->rir->valid.width = inf->sir->valid.width = 0; + + /* Now blat in the blended area. + */ + if( !im_rect_isempty( &oreg ) ) + if( ovlap->blend( or, inf, ovlap, &oreg ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Stop function. Shared with im_tbmerge(). Free explicitly to reduce mem + * requirements quickly for large mosaics. + */ +int +im__stop_merge( MergeInfo *inf ) +{ + if( inf->rir ) { + im_region_free( inf->rir ); + inf->rir = NULL; + } + if( inf->sir ) { + im_region_free( inf->sir ); + inf->sir = NULL; + } + if( inf->from1 ) { + im_free( inf->from1 ); + inf->from1 = NULL; + } + if( inf->from2 ) { + im_free( inf->from2 ); + inf->from2 = NULL; + } + if( inf->merge ) { + im_free( inf->merge ); + inf->merge = NULL; + } + im_free( inf ); + + return( 0 ); +} + +/* Start function. Shared with im_tbmerge(). + */ +void * +im__start_merge( IMAGE *out, Overlapping *ovlap ) +{ + MergeInfo *inf = IM_NEW( NULL, MergeInfo ); + + if( !inf ) + return( NULL ); + + /* Clear all ptrs. + */ + inf->rir = NULL; + inf->sir = NULL; + inf->from1 = NULL; + inf->from2 = NULL; + inf->merge = NULL; + + /* If this is going to be a IM_CODING_LABQ, we need IM_CODING_LABQ blend buffers. + */ + if( out->Coding == IM_CODING_LABQ ) { + inf->from1 = IM_ARRAY( NULL, ovlap->blsize * 3, float ); + inf->from2 = IM_ARRAY( NULL, ovlap->blsize * 3, float ); + inf->merge = IM_ARRAY( NULL, ovlap->blsize * 3, float ); + if( !inf->from1 || !inf->from2 || !inf->merge ) { + im__stop_merge( inf ); + return( NULL ); + } + } + + /* Make input regions. + */ + inf->rir = im_region_create( ovlap->ref ); + inf->sir = im_region_create( ovlap->sec ); + + if( !inf->rir || !inf->sir ) { + im__stop_merge( inf ); + return( NULL ); + } + + return( inf ); +} + +int +im__lrmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + Overlapping *ovlap; + +#ifdef DEBUG + printf( "im__lrmerge %s %s %s %d %d %d\n", + ref->filename, sec->filename, out->filename, + dx, dy, mwidth ); + printf( "ref is %d x %d pixels\n", ref->Xsize, ref->Ysize ); + printf( "sec is %d x %d pixels\n", sec->Xsize, sec->Ysize ); +#endif + + /* Check IMAGEs parameters + */ + if( ref->Bands != sec->Bands || ref->Bbits != sec->Bbits || + ref->BandFmt != sec->BandFmt || + ref->Coding != sec->Coding ) { + im_errormsg( "im_lrmerge: input images incompatible" ); + return( -1 ); + } + if( ref->Coding != IM_CODING_NONE && ref->Coding != IM_CODING_LABQ ) { + im_errormsg( "im_lrmerge: inputs not uncoded or IM_CODING_LABQ" ); + return( -1 ); + } + if( dx > 0 || dx < 1 - ref->Xsize ) { +#ifdef DEBUG + printf( "im__lrmerge: no overlap, using insert\n" ); +#endif + + /* No overlap, use insert instead. + */ + if( im_insert( ref, sec, out, -dx, -dy ) ) + return( -1 ); + out->Xoffset = -dx; + out->Yoffset = -dy; + + return( 0 ); + } + if( im_piocheck( ref, out ) || im_piocheck( sec, out ) ) + return( -1 ); + + /* Build state for this join. + */ + if( !(ovlap = build_lrstate( ref, sec, out, dx, dy, mwidth )) ) + return( -1 ); + + /* Prepare the output IMAGE. + */ + if( im_cp_descv( out, ref, sec, NULL ) ) + return( -1 ); + out->Xsize = ovlap->oarea.width; + out->Ysize = ovlap->oarea.height; + out->Xoffset = ovlap->sarea.left; + out->Yoffset = ovlap->sarea.top; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, ref, sec, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im__start_merge, im__merge_gen, im__stop_merge, ovlap, NULL ) ) + return( -1 ); + + return ( 0 ); +} + +int +im_lrmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + if( im__lrmerge( ref, sec, out, dx, dy, mwidth ) ) + return( -1 ); + + if( im_histlin( out, "#LRJOIN <%s> <%s> <%s> <%d> <%d> <%d>", + ref->filename, sec->filename, out->filename, + -dx, -dy, mwidth ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_lrmosaic.c b/libsrc/mosaicing/im_lrmosaic.c new file mode 100644 index 00000000..59cdde03 --- /dev/null +++ b/libsrc/mosaicing/im_lrmosaic.c @@ -0,0 +1,475 @@ +/* @(#) Program to calculate the best possible tie points + * @(#) in the overlapping part between the primary and the secondary picture + * @(#) + * @(#) Right call: + * @(#) int im_lrmosaic( reference, secondary, out, bandno, + * @(#) xref, yref, xsec, ysec, halfcorrelation, halfarea ) + * @(#) IMAGE *reference, *secondary, *out; + * @(#) int bandno; + * @(#) int xref, yref, xsec, ysec; + * @(#) int halfcorrelation, halfarea; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 07/11/1989 + * Modified on : 29/11/1989, 18/04/1991 + * + * + * Modified and debugged by Ahmed Abbood . 1995 + * 14/6/95 JC + * - rewritten for new balance ideas + * - more bug-fixes + * 1/11/95 JC + * - frees memory used by analysis phase as soon as possible + * - means large mosaics use significantly less peak memory + * 26/3/96 JC + * - now calls im_lrmerge() rather than im__lrmerge() + * 2/2/01 JC + * - added tunable max blend width + * 24/2/05 + * - im_scale() makes it work for any image type + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#ifdef DEBUG +static void +im__print_mdebug( TIE_POINTS *points ) +{ + int i; + double adx = 0.0; + double ady = 0.0; + double acor = 0.0; + + for( i = 0; i < points->nopoints; i++ ) { + adx += points->dx[i]; + ady += points->dy[i]; + acor += points->correlation[i]; + } + adx = adx / (double) points->nopoints; + ady = ady / (double) points->nopoints; + acor = acor / (double) points->nopoints; + + printf( "points: %d\n", points->nopoints ); + printf( "average dx, dy: %g %g\n", adx, ady ); + printf( "average correlation: %g\n", acor ); + printf( "deltax, deltay: %g %g\n", points->l_deltax, points->l_deltay ); +} +#endif /*DEBUG*/ + +int +im__find_lroverlap( IMAGE *ref_in, IMAGE *sec_in, IMAGE *out, + int bandno_in, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int *dx0, int *dy0, + double *scale1, double *angle1, double *dx1, double *dy1 ) +{ + IMAGE *ref, *sec; + TIE_POINTS points, *p_points; + TIE_POINTS newpoints, *p_newpoints; + int dx, dy; + int i; + + Rect left, right, overlap; + + /* Check ref and sec are compatible. + */ + if( ref_in->Bands != sec_in->Bands || + ref_in->BandFmt != sec_in->BandFmt || + ref_in->Coding != sec_in->Coding ) { + im_errormsg( "im_lrmosaic: input images incompatible" ); + return( -1 ); + } + + /* Test cor and area. + */ + if( halfcorrelation < 0 || halfarea < 0 || + halfarea < halfcorrelation ) { + im_errormsg( "im_lrmosaic: bad area parameters" ); + return( -1 ); + } + + /* Set positions of left and right. + */ + left.left = 0; + left.top = 0; + left.width = ref_in->Xsize; + left.height = ref_in->Ysize; + right.left = xref - xsec; + right.top = yref - ysec; + right.width = sec_in->Xsize; + right.height = sec_in->Ysize; + + /* Find overlap. + */ + im_rect_intersectrect( &left, &right, &overlap ); + if( overlap.width < 2*halfarea + 1 || + overlap.height < 2*halfarea + 1 ) { + im_errormsg( "im_lrmosaic: overlap too small for search" ); + return( -1 ); + } + + /* Extract overlaps. + */ + ref = im_open_local( out, "temp_one", "t" ); + sec = im_open_local( out, "temp_two", "t" ); + if( !ref || !sec ) + return( -1 ); + if( ref_in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "temp:3", "p" ); + IMAGE *t2 = im_open_local( out, "temp:4", "p" ); + IMAGE *t3 = im_open_local( out, "temp:5", "p" ); + IMAGE *t4 = im_open_local( out, "temp:6", "p" ); + IMAGE *t5 = im_open_local( out, "temp:7", "p" ); + IMAGE *t6 = im_open_local( out, "temp:8", "p" ); + + if( !t1 || !t2 || !t3 || !t4 || !t5 || !t6 ) + return( -1 ); + if( im_extract_area( ref_in, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec_in, t2, + overlap.left - right.left, overlap.top - right.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_LabQ2Lab( t1, t3 ) || im_LabQ2Lab( t2, t4 ) || + im_Lab2disp( t3, t5, im_col_displays( 1 ) ) || + im_Lab2disp( t4, t6, im_col_displays( 1 ) ) ) + return( -1 ); + + /* Extract the green. + */ + if( im_extract_band( t5, ref, 1 ) || + im_extract_band( t6, sec, 1 ) ) + return( -1 ); + } + else if( ref_in->Coding == IM_CODING_NONE ) { + IMAGE *t1 = im_open_local( out, "temp:9", "p" ); + IMAGE *t2 = im_open_local( out, "temp:10", "p" ); + IMAGE *t3 = im_open_local( out, "temp:11", "p" ); + IMAGE *t4 = im_open_local( out, "temp:12", "p" ); + + if( !t1 || !t2 || !t3 || !t4 ) + return( -1 ); + if( im_extract_area( ref_in, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec_in, t2, + overlap.left - right.left, overlap.top - right.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_band( t1, t3, bandno_in ) || + im_extract_band( t2, t4, bandno_in ) ) + return( -1 ); + if( im_scale( t3, ref ) || + im_scale( t4, sec ) ) + return( -1 ); + } + else { + im_errormsg( "im_lrmosaic: unknown Coding type" ); + return( -1 ); + } + + /* Initialise and fill TIE_POINTS + */ + p_points = &points; + p_newpoints = &newpoints; + p_points->reference = ref_in->filename; + p_points->secondary = sec_in->filename; + p_points->nopoints = IM_MAXPOINTS; + p_points->deltax = 0; + p_points->deltay = 0; + p_points->halfcorsize = halfcorrelation; + p_points->halfareasize = halfarea; + + /* Initialise the structure + */ + for( i = 0; i < IM_MAXPOINTS; i++ ) { + p_points->x_reference[i] = 0; + p_points->y_reference[i] = 0; + p_points->x_secondary[i] = 0; + p_points->y_secondary[i] = 0; + p_points->contrast[i] = 0; + p_points->correlation[i] = 0.0; + p_points->dx[i] = 0.0; + p_points->dy[i] = 0.0; + p_points->deviation[i] = 0.0; + } + + /* Search ref for possible tie-points. Sets: p_points->contrast, + * p_points->x,y_reference. + */ + if( im__lrcalcon( ref, p_points ) ) + return( -1 ); + + /* For each candidate point, correlate against corresponding part of + * sec. Sets x,y_secondary and fills correlation and dx, dy. + */ + if( im__chkpair( ref, sec, p_points ) ) + return( -1 ); + + /* First call to im_clinear(). + */ + if( im__initialize( p_points ) ) + return( -1 ); + + /* Improve the selection of tiepoints until all abs(deviations) are + * < 1.0 by deleting all wrong points. + */ + if( im__improve( p_points, p_newpoints ) ) + return( -1 ); + + /* Average remaining offsets. + */ + if( im__avgdxdy( p_newpoints, &dx, &dy ) ) + return( -1 ); + + /* Offset with overlap position. + */ + *dx0 = -right.left + dx; + *dy0 = -right.top + dy; + + /* Write 1st order parameters too. + */ + *scale1 = newpoints.l_scale; + *angle1 = newpoints.l_angle; + *dx1 = newpoints.l_deltax; + *dy1 = newpoints.l_deltay; + + return( 0 ); +} + +/* Scale im by fac with a lut. + */ +static IMAGE * +transform( IMAGE *out, IMAGE *im, double fac ) +{ + IMAGE *t1 = im_open_local( out, "transform:1", "p" ); + IMAGE *t2 = im_open_local( out, "transform:2", "p" ); + IMAGE *t3 = im_open_local( out, "transform:3", "p" ); + IMAGE *t4 = im_open_local( out, "transform:4", "p" ); + + if( !t1 || !t2 || !t3 || !t4 ) + return( NULL ); + + if( fac == 1.0 ) + /* Easy! + */ + return( im ); + + if( im_identity( t1, 1 ) || + im_lintra( fac, t1, 0.0, t2 ) || + im_clip( t2, t3 ) || + im_maplut( im, t4, t3 ) ) + return( NULL ); + + return( t4 ); +} + +/* Balance two images. dx, dy parameters as for im_??merge, etc. + */ +int +im__balance( IMAGE *ref, IMAGE *sec, IMAGE *out, + IMAGE **ref_out, IMAGE **sec_out, int dx, int dy, int balancetype ) +{ + double lavg, ravg; + double lfac, rfac; + Rect left, right, overlap; + IMAGE *t1, *t2; + + /* Test balancetype. + */ + if( balancetype < 0 || balancetype > 3 ) { + im_errormsg( "im_mosaic: bad balancetype parameter" ); + return( -1 ); + } + + /* No balance - easy! + */ + if( balancetype == 0 ) { + *ref_out = ref; + *sec_out = sec; + + return( 0 ); + } + + /* Must be uchar uncoded. + */ + if( ref->Coding != IM_CODING_NONE || + ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im_mosaic: uncoded uchar only for balancing" ); + return( -1 ); + } + + /* Set positions of left and right. + */ + left.left = 0; + left.top = 0; + left.width = ref->Xsize; + left.height = ref->Ysize; + right.left = -dx; + right.top = -dy; + right.width = sec->Xsize; + right.height = sec->Ysize; + + /* Find overlap. + */ + im_rect_intersectrect( &left, &right, &overlap ); + + /* Extract overlaps. + */ + t1 = im_open_local( out, "temp_one", "p" ); + t2 = im_open_local( out, "temp_two", "p" ); + if( !t1 || !t2 ) + return( -1 ); + + if( im_extract_area( ref, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec, t2, + overlap.left - right.left, overlap.top - right.top, + overlap.width, overlap.height ) ) + return( -1 ); + + /* And find the average. + */ + if( im_avg( t1, &lavg ) || im_avg( t2, &ravg ) ) + return( -1 ); + + /* Compute scale factors. + */ + switch( balancetype ) { + case 1: + /* Ajust left. + */ + rfac = 1.0; + lfac = ravg / lavg; + break; + + case 2: + /* Adjust right. + */ + lfac = 1.0; + rfac = lavg / ravg; + break; + + case 3: + { + /* Adjust both to weighted average. + */ + double ltot = (double) ref->Xsize * ref->Ysize; + double rtot = (double) sec->Xsize * sec->Ysize; + double rat = ltot / (ltot + rtot); + double navg = rat * (lavg - ravg) + ravg; + + lfac = navg / lavg; + rfac = navg / ravg; + } + break; + + default: + error_exit( "internal error #897624395" ); + return( -1 ); + } + + /* Transform the left and right images. + */ + if( !(*ref_out = transform( out, ref, lfac )) ) + return( -1 ); + if( !(*sec_out = transform( out, sec, rfac )) ) + return( -1 ); + + return( 0 ); +} + +int +im_lrmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + int dx0, dy0; + double scale1, angle1, dx1, dy1; + IMAGE *ref2, *sec2; + IMAGE *dummy; + + /* Correct overlap. dummy is just a placeholder used to ensure that + * memory used by the analysis phase is freed as soon as possible. + */ + if( !(dummy = im_open( "placeholder:1", "p" )) ) + return( -1 ); + if( im__find_lroverlap( ref, sec, dummy, + bandno, + xref, yref, xsec, ysec, + halfcorrelation, halfarea, + &dx0, &dy0, + &scale1, &angle1, &dx1, &dy1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + /* Balance. + */ + if( im__balance( ref, sec, out, + &ref2, &sec2, + dx0, dy0, balancetype ) ) + return( -1 ); + + /* Merge left right. + */ + if( im_lrmerge( ref2, sec2, out, dx0, dy0, mwidth ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_remosaic.c b/libsrc/mosaicing/im_remosaic.c new file mode 100644 index 00000000..7f225e92 --- /dev/null +++ b/libsrc/mosaicing/im_remosaic.c @@ -0,0 +1,136 @@ +/* Use one mosiaced file to mosaic another set of images. + * + * 1/11/01 JC + * - from global_balance + * 25/02/02 JC + * - detect size change + * 10/4/06 + * - spot file-not-found + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Define for debug output. +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include + +#include "merge.h" +#include "global_balance.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct _RemosaicData { + const char *old_str; + const char *new_str; + int new_len; + int old_len; +} RemosaicData; + +static IMAGE * +remosaic( JoinNode *node, RemosaicData *rd ) +{ + SymbolTable *st = node->st; + IMAGE *im = node->im; + + IMAGE *out; + char filename[FILENAME_MAX]; + char *p; + + if( !im ) { + im_error( "im_remosaic", _( "file \"%s\" not found" ), + node->name ); + return( NULL ); + } + + /* Remove substring rd->old_str from in->filename, replace with + * rd->new_str. + */ + im_strncpy( filename, im->filename, FILENAME_MAX ); + if( (p = im_strrstr( filename, rd->old_str )) ) { + int offset = p - &filename[0]; + + im_strncpy( p, rd->new_str, FILENAME_MAX - offset ); + im_strncpy( p + rd->new_len, + im->filename + offset + rd->old_len, + FILENAME_MAX - offset - rd->new_len ); + } + +#ifdef DEBUG + printf( "im_remosaic: filename \"%s\" -> \"%s\"\n", + im->filename, filename ); +#endif /*DEBUG*/ + + if( !(out = im__global_open_image( st, filename )) ) + return( NULL ); + + if( out->Xsize != im->Xsize || out->Ysize != im->Ysize ) { + im_error( "im_remosaic", + _( "substitute image \"%s\" is not " + "the same size as \"%s\"" ), + filename, im->filename ); + return( NULL ); + } + + return( out ); +} + +int +im_remosaic( IMAGE *in, IMAGE *out, const char *old_str, const char *new_str ) +{ + SymbolTable *st; + RemosaicData rd; + + if( !(st = im__build_symtab( out, SYM_TAB_SIZE )) || + im__parse_desc( st, in ) ) + return( -1 ); + + /* Re-make mosaic. + */ + rd.old_str = old_str; + rd.new_str = new_str; + rd.new_len = strlen( new_str ); + rd.old_len = strlen( old_str ); + if( im__build_mosaic( st, out, (transform_fn) remosaic, &rd ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_tbcalcon.c b/libsrc/mosaicing/im_tbcalcon.c new file mode 100644 index 00000000..b444fa43 --- /dev/null +++ b/libsrc/mosaicing/im_tbcalcon.c @@ -0,0 +1,137 @@ +/* @(#) Functions which takes an initial estimate of deltax, deltay + * @(#) between reference and secondary images (probably from the scanner), + * @(#) and looks in three areas of the overlapping part of the reference image + * @(#) corresponding to reference and secondary. For every other halfreasize + * @(#) point of the three areas of the reference image + * @(#) the contrast is calculated + * @(#) an area 2*halfcorsize+1 centered at this point + * @(#) Results are saved in the structure points + * @(#) The function expects the following valid data in points: + * @(#) deltax, deltay, nopoints, halfcorsize, halfareasize + * @(#) and fills in the memebers: + * @(#) x, y_reference[], contrast and x,y_secondary[], + * @(#) based on deltax and deltay + * @(#) Input image should are either memory mapped or in a buffer. + * @(#) To make the calculation faster set FACTOR to 1, 2 or 3 + * @(#) Calculations are based on bandno only. + * @(#) The function uses functions im_calculate_contrast() + * @(#) which is in im_lrcalcon() + * @(#) + * @(#) int im_tbcalcon( ref, sec, bandno, points ) + * @(#) IMAGE *ref, *sec; + * @(#) int bandno; + * @(#) TIE_POINTS *points; see mosaic.h + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 20/12/1990 + * Modified on : 18/04/1991 + * 8/7/93 JC + * - allow IM_CODING_LABQ coding + * - now calls im_incheck() + * 12/7/95 JC + * - reworked + * - what a lot of horrible old code there was too + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__tbcalcon( IMAGE *ref, TIE_POINTS *points ) +{ + /* Geometry: border we must leave around each area. + */ + const int border = points->halfareasize; + + /* Width of an area. + */ + const int awidth = ref->Xsize / AREAS; + + /* Number of points we find in each area. + */ + const int len = points->nopoints / AREAS; + + int i; + Rect area; + + /* Make sure we can read image. + */ + if( im_incheck( ref ) ) + return( -1 ); + if( ref->Bands != 1 || ref->BandFmt != IM_BANDFMT_UCHAR ) { + im_errormsg( "im__tbcalcon: help!" ); + return( -1 ); + } + + /* Define bits to search for high-contrast areas. + */ + area.width = awidth; + area.height = ref->Ysize; + area.left = 0; + area.top = 0; + im_rect_marginadjust( &area, -border ); + area.width--; + area.height--; + if( area.width < 0 || area.height < 0 ) { + im_errormsg( "im__tbcalcon: overlap too small" ); + return( -1 ); + } + + /* Loop over areas, finding points. + */ + for( i = 0; area.left < ref->Xsize; area.left += awidth, i++ ) + if( im__find_best_contrast( ref, + area.left, area.top, area.width, area.height, + points->x_reference + i*len, + points->y_reference + i*len, + points->contrast + i*len, + len, + points->halfcorsize ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_tbmerge.c b/libsrc/mosaicing/im_tbmerge.c new file mode 100644 index 00000000..b9432e79 --- /dev/null +++ b/libsrc/mosaicing/im_tbmerge.c @@ -0,0 +1,732 @@ +/* Merge two images top-bottom. dx, dy is the offset needed to get from sec + * (secondary image) to ref (reference image). + * + * Usage: + * + * int + * im_tbmerge( ref, sec, out, dx, dy ) + * IMAGE *ref, *sec, *out; + * int dx, dy; + * + * Returns 0 on success and -1 on error + * + * Copyright: 1990, 1991 N. Dessipris + * Author: N. Dessipris + * Written on: 20/09/1990 + * Updated on: 17/04/1991 + * 1/6/92: J. Cupitt + * - check for difference bug fixed + * - geometry calculations improved and simplified + * - small speedups + * 30/6/93 K.Martinez : coped with IM_CODING_LABQ images + * 7/7/93 JC + * - ANSIfied + * - proper freeing on errors, ready for partial + * 8/11/93 JC + * - now propogates both input histories + * - adds magic lines for global mosaic optimisation + * + * + * 16/May/1994 Ahmed. Abbood + * - Modified to use partials on all IO + * + * June/1995 Ahmed Abbood + * + * - Modified to work with different types of images. + * + * + * 16/6/95 JC + * - added to VIPS! + * 7/9/95 JC + * - split into two parts: im_tbmerge() and im__tbmerge() + * - latter called by im_tbmosaic() + * - just the same as public im_tbmerge(), but adds no history + * - necessary for im_global_balance() + * - small bugs fixed + * 10/10/95 JC + * - better checks that parameters are sensible + * 11/10/95 JC + * - Kirk spotted what a load of rubbish Ahmed's code is + * - rewritten - many, many bugs fixed + * 28/7/97 JC + * - new non-rectangular im_lrmerge adapted to make this + * - small tidies + * 18/2/98 JC + * - im_demand_hint() call added + * 19/2/98 JC + * - now works for any dx/dy by calling im_insert() for bizarre cases + * 2/2/01 JC + * - added tunable max blend width + * 8/3/01 JC + * - switched to integer arithmetic for integer blends + * 23/3/01 JC + * - oops, iblend was broken + * 7/11/01 JC + * - more sophisticated transparency handling + * 15/8/02 JC + * - records Xoffset/Yoffset + * 20/6/05 + * - now requires all bands == 0 for transparency (used to just check + * band 0) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Return the position of the first non-zero pel from the top. + */ +static int +find_top( REGION *ir, int *pos, int x, int y, int h ) +{ + PEL *pr = (PEL *) IM_REGION_ADDR( ir, x, y ); + IMAGE *im = ir->im; + int ls = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( im ); + int b = im->Bands; + int i, j; + + /* Double the number of bands in a complex. + */ + if( im_iscomplex( im ) ) + b *= 2; + +/* Search for the first non-zero band element from the top edge of the image. + */ +#define tsearch( TYPE ) { \ + TYPE *p = (TYPE *) pr; \ + \ + for( i = 0; i < h; i++ ) { \ + for( j = 0; j < b; j++ ) \ + if( p[j] ) \ + break; \ + if( j < b ) \ + break; \ + \ + p += ls; \ + } \ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: tsearch( unsigned char ); break; + case IM_BANDFMT_CHAR: tsearch( signed char ); break; + case IM_BANDFMT_USHORT: tsearch( unsigned short ); break; + case IM_BANDFMT_SHORT: tsearch( signed short ); break; + case IM_BANDFMT_UINT: tsearch( unsigned int ); break; + case IM_BANDFMT_INT: tsearch( signed int ); break; + case IM_BANDFMT_FLOAT: tsearch( float ); break; + case IM_BANDFMT_DOUBLE: tsearch( double ); break; + case IM_BANDFMT_COMPLEX:tsearch( float ); break; + case IM_BANDFMT_DPCOMPLEX:tsearch( double ); break; + + default: + im_error( "im_tbmerge", _( "internal error" ) ); + return( -1 ); + } + + *pos = y + i; + + return( 0 ); +} + +/* Return the position of the first non-zero pel from the bottom. + */ +static int +find_bot( REGION *ir, int *pos, int x, int y, int h ) +{ + PEL *pr = (PEL *) IM_REGION_ADDR( ir, x, y ); + IMAGE *im = ir->im; + int ls = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( ir->im ); + int b = im->Bands; + int i, j; + + /* Double the number of bands in a complex. + */ + if( im_iscomplex( im ) ) + b *= 2; + +/* Search for the first non-zero band element from the top edge of the image. + */ +#define rsearch( TYPE ) { \ + TYPE *p = (TYPE *) pr + (h - 1) * ls; \ + \ + for( i = h - 1; i >= 0; i-- ) { \ + for( j = 0; j < b; j++ ) \ + if( p[j] ) \ + break; \ + if( j < b ) \ + break; \ + \ + p -= ls; \ + } \ +} + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: rsearch( unsigned char ); break; + case IM_BANDFMT_CHAR: rsearch( signed char ); break; + case IM_BANDFMT_USHORT: rsearch( unsigned short ); break; + case IM_BANDFMT_SHORT: rsearch( signed short ); break; + case IM_BANDFMT_UINT: rsearch( unsigned int ); break; + case IM_BANDFMT_INT: rsearch( signed int ); break; + case IM_BANDFMT_FLOAT: rsearch( float ); break; + case IM_BANDFMT_DOUBLE: rsearch( double ); break; + case IM_BANDFMT_COMPLEX:rsearch( float ); break; + case IM_BANDFMT_DPCOMPLEX:rsearch( double ); break; + + default: + im_error( "im_tbmerge", _( "internal error" ) ); + return( -1 ); + } + + *pos = y + i; + + return( 0 ); +} + +/* Make first/last for oreg. + */ +static int +make_firstlast( MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + Rect rr, sr; + int x; + int missing; + + /* We're going to build first/last ... lock it from other generate + * threads. In fact it's harmless if we do get two writers, but we may + * avoid duplicating work. + */ + g_mutex_lock( ovlap->fl_lock ); + + /* Do we already have first/last for this area? Bail out if we do. + */ + missing = 0; + for( x = oreg->left; x < IM_RECT_RIGHT( oreg ); x++ ) { + const int j = x - ovlap->overlap.left; + const int first = ovlap->first[j]; + + if( first < 0 ) { + missing = 1; + break; + } + } + if( !missing ) { + /* No work to do! + */ + g_mutex_unlock( ovlap->fl_lock ); + return( 0 ); + } + + /* Entire height of overlap in ref for oreg ... we know oreg is inside + * overlap. + */ + rr.left = oreg->left; + rr.top = ovlap->overlap.top; + rr.width = oreg->width; + rr.height = ovlap->overlap.height; + rr.left -= ovlap->rarea.left; + rr.top -= ovlap->rarea.top; + + /* Same in sec. + */ + sr.left = oreg->left; + sr.top = ovlap->overlap.top; + sr.width = oreg->width; + sr.height = ovlap->overlap.height; + sr.left -= ovlap->sarea.left; + sr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &rr ) || im_prepare( sir, &sr ) ) { + g_mutex_unlock( ovlap->fl_lock ); + return( -1 ); + } + + /* Make first/last cache. + */ + for( x = 0; x < oreg->width; x++ ) { + const int j = (x + oreg->left) - ovlap->overlap.left; + int *first = &ovlap->first[j]; + int *last = &ovlap->last[j]; + + /* Done this line already? + */ + if( *first < 0 ) { + /* Search for top/bottom of overlap on this scan-line. + */ + if( find_top( sir, first, + x + sr.left, sr.top, sr.height ) || + find_bot( rir, last, + x + rr.left, rr.top, rr.height ) ) { + g_mutex_unlock( ovlap->fl_lock ); + return( -1 ); + } + + /* Translate to output space. + */ + *first += ovlap->sarea.top; + *last += ovlap->rarea.top; + + /* Clip to maximum blend width, if necessary. + */ + if( ovlap->mwidth >= 0 && + *last - *first > ovlap->mwidth ) { + int shrinkby = (*last - *first) - ovlap->mwidth; + + *first += shrinkby / 2; + *last -= shrinkby / 2; + } + } + } + + g_mutex_unlock( ovlap->fl_lock ); + + return( 0 ); +} + +/* Test pixel == 0. + */ +#define TEST_ZERO( TYPE, T, RESULT ) { \ + TYPE *tt = (T); \ + int ii; \ + \ + for( ii = 0; ii < cb; ii++ ) \ + if( tt[i] ) \ + break; \ + if( ii == cb ) \ + (RESULT) = 1; \ +} + +/* Blend two integer images ... one scan-line. + */ +#define iblend( TYPE, B, IN1, IN2, OUT ) { \ + TYPE *tr = (TYPE *) (IN1); \ + TYPE *ts = (TYPE *) (IN2); \ + TYPE *tq = (TYPE *) (OUT); \ + const int cb = (B); \ + int ref_zero; \ + int sec_zero; \ + int x, b; \ + int i; \ + \ + for( i = 0, x = 0; x < oreg->width; x++ ) { \ + ref_zero = 0; \ + sec_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + \ + /* Above the bottom image? \ + */ \ + if( y < first[x] ) { \ + if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + /* To the right? \ + */ \ + else if( y >= last[x] ) { \ + if( !sec_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + } \ + /* In blend area. \ + */ \ + else { \ + if( !ref_zero && !sec_zero ) { \ + const int bheight = last[x] - first[x]; \ + const int inx = ((y - first[x]) << \ + BLEND_SHIFT) / bheight; \ + int c1 = im__icoef1[inx]; \ + int c2 = im__icoef2[inx]; \ + \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = c1*tr[i] / BLEND_SCALE + \ + c2*ts[i] / BLEND_SCALE; \ + } \ + else if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + } \ +} + +/* Blend two float images. + */ +#define fblend( TYPE, B, IN1, IN2, OUT ) { \ + TYPE *tr = (TYPE *) (IN1); \ + TYPE *ts = (TYPE *) (IN2); \ + TYPE *tq = (TYPE *) (OUT); \ + int ref_zero; \ + int sec_zero; \ + const int cb = (B); \ + int x, b; \ + int i; \ + \ + for( i = 0, x = 0; x < oreg->width; x++ ) { \ + ref_zero = 0; \ + sec_zero = 0; \ + TEST_ZERO( TYPE, tr, ref_zero ); \ + TEST_ZERO( TYPE, ts, sec_zero ); \ + \ + /* Above the bottom image? \ + */ \ + if( y < first[x] ) \ + if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + /* To the right? \ + */ \ + else if( y >= last[x] ) \ + if( !sec_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + /* In blend area. \ + */ \ + else { \ + if( !ref_zero && !sec_zero ) { \ + const int bheight = last[x] - first[x]; \ + const int inx = ((y - first[x]) << \ + BLEND_SHIFT) / bheight; \ + double c1 = im__coef1[inx]; \ + double c2 = im__coef2[inx]; \ + \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = c1 * tr[i] + c2 * ts[i]; \ + } \ + else if( !ref_zero ) \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = tr[i]; \ + else \ + for( b = 0; b < cb; b++, i++ ) \ + tq[i] = ts[i]; \ + } \ + } \ +} + +/* Top-bottom blend function for non-labpack images. + */ +static int +tb_blend( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + IMAGE *im = or->im; + + Rect prr, psr; + int y, yr, ys; + + /* Make sure we have a complete first/last set for this area. + */ + if( make_firstlast( inf, ovlap, oreg ) ) + return( -1 ); + + /* Part of rr which we will output. + */ + prr = *oreg; + prr.left -= ovlap->rarea.left; + prr.top -= ovlap->rarea.top; + + /* Part of sr which we will output. + */ + psr = *oreg; + psr.left -= ovlap->sarea.left; + psr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &prr ) ) + return( -1 ); + if( im_prepare( sir, &psr ) ) + return( -1 ); + + /* Loop down overlap area. + */ + for( y = oreg->top, yr = prr.top, ys = psr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + PEL *pr = (PEL *) IM_REGION_ADDR( rir, prr.left, yr ); + PEL *ps = (PEL *) IM_REGION_ADDR( sir, psr.left, ys ); + PEL *q = (PEL *) IM_REGION_ADDR( or, oreg->left, y ); + + const int j = oreg->left - ovlap->overlap.left; + const int *first = ovlap->first + j; + const int *last = ovlap->last + j; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + iblend( unsigned char, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_CHAR: + iblend( signed char, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_USHORT: + iblend( unsigned short, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_SHORT: + iblend( signed short, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_UINT: + iblend( unsigned int, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_INT: + iblend( signed int, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_FLOAT: + fblend( float, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_DOUBLE: + fblend( double, im->Bands, pr, ps, q ); break; + case IM_BANDFMT_COMPLEX: + fblend( float, im->Bands*2, pr, ps, q ); break; + case IM_BANDFMT_DPCOMPLEX: + fblend( double, im->Bands*2, pr, ps, q ); break; + + default: + im_error( "im_tbmerge", _( "internal error" ) ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Top-bottom blend function for IM_CODING_LABQ images. + */ +static int +tb_blend_labpack( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg ) +{ + REGION *rir = inf->rir; + REGION *sir = inf->sir; + Rect prr, psr; + int y, yr, ys; + + /* Make sure we have a complete first/last set for this area. This + * will just look at the top 8 bits of L, not all 10, but should be OK. + */ + if( make_firstlast( inf, ovlap, oreg ) ) + return( -1 ); + + /* Part of rr which we will output. + */ + prr = *oreg; + prr.left -= ovlap->rarea.left; + prr.top -= ovlap->rarea.top; + + /* Part of sr which we will output. + */ + psr = *oreg; + psr.left -= ovlap->sarea.left; + psr.top -= ovlap->sarea.top; + + /* Make pixels. + */ + if( im_prepare( rir, &prr ) ) + return( -1 ); + if( im_prepare( sir, &psr ) ) + return( -1 ); + + /* Loop down overlap area. + */ + for( y = oreg->top, yr = prr.top, ys = psr.top; + y < IM_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) { + PEL *pr = (PEL *) IM_REGION_ADDR( rir, prr.left, yr ); + PEL *ps = (PEL *) IM_REGION_ADDR( sir, psr.left, ys ); + PEL *q = (PEL *) IM_REGION_ADDR( or, oreg->left, y ); + + const int j = oreg->left - ovlap->overlap.left; + const int *first = ovlap->first + j; + const int *last = ovlap->last + j; + + float *fq = inf->merge; + float *r = inf->from1; + float *s = inf->from2; + + /* Unpack two bits we want. + */ + imb_LabQ2Lab( pr, r, oreg->width ); + imb_LabQ2Lab( ps, s, oreg->width ); + + /* Blend as floats. + */ + fblend( float, 3, r, s, fq ); + + /* Re-pack to output buffer. + */ + imb_Lab2LabQ( inf->merge, q, oreg->width ); + } + + return( 0 ); +} + +/* Build per-call state. + */ +static Overlapping * +build_tbstate( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + Overlapping *ovlap; + + if( !(ovlap = im__build_mergestate( ref, sec, out, dx, dy, mwidth )) ) + return( NULL ); + + /* Select blender. + */ + switch( ref->Coding ) { + case IM_CODING_LABQ: + ovlap->blend = tb_blend_labpack; + break; + + case IM_CODING_NONE: + ovlap->blend = tb_blend; + break; + + default: + im_error( "im_tbmerge", _( "unknown coding type" ) ); + return( NULL ); + } + + /* Find the parts of output which come just from ref and just from sec. + */ + ovlap->rpart = ovlap->rarea; + ovlap->spart = ovlap->sarea; + ovlap->rpart.height -= ovlap->overlap.height; + ovlap->spart.top += ovlap->overlap.height; + ovlap->spart.height -= ovlap->overlap.height; + + /* Is there too much overlap? ie. bottom edge of ref image is greater + * than bottom edge of sec image, or top edge of ref > top edge of + * sec. + */ + if( IM_RECT_BOTTOM( &ovlap->rarea ) > IM_RECT_BOTTOM( &ovlap->sarea ) || + ovlap->rarea.top > ovlap->sarea.top ) { + im_error( "im_tbmerge", _( "too much overlap" ) ); + return( NULL ); + } + + /* Max number of pixels we may have to blend together. + */ + ovlap->blsize = ovlap->overlap.width; + + return( ovlap ); +} + +int +im__tbmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + Overlapping *ovlap; + + /* Check IMAGEs parameters + */ + if( ref->Bands != sec->Bands || ref->Bbits != sec->Bbits || + ref->BandFmt != sec->BandFmt || + ref->Coding != sec->Coding ) { + im_error( "im_tbmerge", _( "input images incompatible" ) ); + return( -1 ); + } + if( ref->Coding != IM_CODING_NONE && ref->Coding != IM_CODING_LABQ ) { + im_error( "im_tbmerge", + _( "inputs not uncoded or IM_CODING_LABQ" ) ); + return( -1 ); + } + if( dy > 0 || dy < 1 - ref->Ysize ) { + /* No overlap, use insert instead. + */ + if( im_insert( ref, sec, out, -dx, -dy ) ) + return( -1 ); + out->Xoffset = -dx; + out->Yoffset = -dy; + + return( 0 ); + } + if( im_piocheck( ref, out ) || im_piocheck( sec, out ) ) + return( -1 ); + + /* Build state for this join. + */ + if( !(ovlap = build_tbstate( ref, sec, out, dx, dy, mwidth )) ) + return( -1 ); + + /* Prepare the output IMAGE. + */ + if( im_cp_descv( out, ref, sec, NULL ) ) + return( -1 ); + out->Xsize = ovlap->oarea.width; + out->Ysize = ovlap->oarea.height; + out->Xoffset = ovlap->sarea.left; + out->Yoffset = ovlap->sarea.top; + + /* Set demand hints. + */ + if( im_demand_hint( out, IM_THINSTRIP, ref, sec, NULL ) ) + return( -1 ); + + /* Generate! + */ + if( im_generate( out, + im__start_merge, im__merge_gen, im__stop_merge, ovlap, NULL ) ) + return( -1 ); + + return ( 0 ); +} + +int +im_tbmerge( IMAGE *ref, IMAGE *sec, IMAGE *out, int dx, int dy, int mwidth ) +{ + if( im__tbmerge( ref, sec, out, dx, dy, mwidth ) ) + return( -1 ); + + if( im_histlin( out, "#TBJOIN <%s> <%s> <%s> <%d> <%d> <%d>", + ref->filename, sec->filename, out->filename, + -dx, -dy, mwidth ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/im_tbmosaic.c b/libsrc/mosaicing/im_tbmosaic.c new file mode 100644 index 00000000..75a423d6 --- /dev/null +++ b/libsrc/mosaicing/im_tbmosaic.c @@ -0,0 +1,309 @@ +/* @(#) Program to calculate the best possible tie points + * @(#) in the overlapping part between the primary and the secondary picture + * @(#) + * @(#) Right call: + * @(#) int im_tbmosaic( reference, secondary, out, bandno, + * @(#) xref, yref, xsec, ysec, halfcorrelation, halfarea, balancetype ) + * @(#) IMAGE *reference, *secondary, *out; + * @(#) int bandno; + * @(#) int xref, yref, xsec, ysec; + * @(#) int halfcorrelation, halfarea; + * @(#) int balancetype; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 07/11/1989 + * Modified on : 29/11/1989, 18/04/1991 + * Modified and debugged by Ahmed Abbood . 1995 + * 14/6/95 JC + * - adapted for new balance ideas + * - more bug-fixes + * 1/11/95 JC + * - frees memory used by analysis phase as soon as possible + * - means large mosaics use significantly less peak memory + * 26/3/96 JC + * - now calls im_tbmerge() rather than im__tbmerge() + * 30/7/97 JC + * - im__find_tboverlap() returns 1st order params too + * 2/2/01 JC + * - added tunable max blend width + * 24/2/05 + * - im_scale() makes it work for any image type + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__find_tboverlap( IMAGE *ref_in, IMAGE *sec_in, IMAGE *out, + int bandno_in, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int *dx0, int *dy0, + double *scale1, double *angle1, double *dx1, double *dy1 ) +{ + IMAGE *ref, *sec; + TIE_POINTS points, *p_points; /* defined in mosaic.h */ + TIE_POINTS newpoints, *p_newpoints; + int i; + int dx, dy; + + Rect top, bottom, overlap; + + /* Check ref and sec are compatible. + */ + if( ref_in->Bands != sec_in->Bands || + ref_in->BandFmt != sec_in->BandFmt || + ref_in->Coding != sec_in->Coding ) { + im_errormsg( "im_tbmosaic: input images incompatible" ); + return( -1 ); + } + + /* Test cor and area. + */ + if( halfcorrelation < 0 || halfarea < 0 || + halfarea < halfcorrelation ) { + im_errormsg( "im_tbmosaic: bad area parameters" ); + return( -1 ); + } + + /* Set positions of top and bottom. + */ + top.left = 0; + top.top = 0; + top.width = ref_in->Xsize; + top.height = ref_in->Ysize; + bottom.left = xref - xsec; + bottom.top = yref - ysec; + bottom.width = sec_in->Xsize; + bottom.height = sec_in->Ysize; + + /* Find overlap. + */ + im_rect_intersectrect( &top, &bottom, &overlap ); + if( overlap.width < 2*halfarea + 1 || + overlap.height < 2*halfarea + 1 ) { + im_errormsg( "im_tbmosaic: overlap too small for search" ); + return( -1 ); + } + + /* Extract overlaps. + */ + ref = im_open_local( out, "temp_one", "t" ); + sec = im_open_local( out, "temp_two", "t" ); + if( !ref || !sec ) + return( -1 ); + if( ref_in->Coding == IM_CODING_LABQ ) { + IMAGE *t1 = im_open_local( out, "temp:3", "p" ); + IMAGE *t2 = im_open_local( out, "temp:4", "p" ); + IMAGE *t3 = im_open_local( out, "temp:5", "p" ); + IMAGE *t4 = im_open_local( out, "temp:6", "p" ); + IMAGE *t5 = im_open_local( out, "temp:7", "p" ); + IMAGE *t6 = im_open_local( out, "temp:8", "p" ); + + if( !t1 || !t2 || !t3 || !t4 || !t5 || !t6 ) + return( -1 ); + if( im_extract_area( ref_in, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec_in, t2, + overlap.left - bottom.left, overlap.top - bottom.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_LabQ2Lab( t1, t3 ) || im_LabQ2Lab( t2, t4 ) || + im_Lab2disp( t3, t5, im_col_displays( 1 ) ) || + im_Lab2disp( t4, t6, im_col_displays( 1 ) ) ) + return( -1 ); + + /* Extract the green. + */ + if( im_extract_band( t5, ref, 1 ) || + im_extract_band( t6, sec, 1 ) ) + return( -1 ); + } + else if( ref_in->Coding == IM_CODING_NONE ) { + IMAGE *t1 = im_open_local( out, "temp:9", "p" ); + IMAGE *t2 = im_open_local( out, "temp:10", "p" ); + IMAGE *t3 = im_open_local( out, "temp:11", "p" ); + IMAGE *t4 = im_open_local( out, "temp:12", "p" ); + + if( !t1 || !t2 || !t3 || !t4 ) + return( -1 ); + if( im_extract_area( ref_in, t1, + overlap.left, overlap.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_area( sec_in, t2, + overlap.left - bottom.left, overlap.top - bottom.top, + overlap.width, overlap.height ) ) + return( -1 ); + if( im_extract_band( t1, t3, bandno_in ) || + im_extract_band( t2, t4, bandno_in ) ) + return( -1 ); + if( im_scale( t3, ref ) || + im_scale( t4, sec ) ) + return( -1 ); + } + else { + im_errormsg( "im_tbmosaic: unknown Coding type" ); + return( -1 ); + } + + /* Initialise and fill TIE_POINTS + */ + p_points = &points; + p_newpoints = &newpoints; + p_points->reference = ref_in->filename; + p_points->secondary = sec_in->filename; + p_points->nopoints = IM_MAXPOINTS; + p_points->deltax = 0; + p_points->deltay = 0; + p_points->halfcorsize = halfcorrelation; + p_points->halfareasize = halfarea; + + /* Initialise the structure + */ + for( i = 0; i < IM_MAXPOINTS; i++ ) { + p_points->x_reference[i] = 0; + p_points->y_reference[i] = 0; + p_points->x_secondary[i] = 0; + p_points->y_secondary[i] = 0; + p_points->contrast[i] = 0; + p_points->correlation[i] = 0.0; + p_points->dx[i] = 0.0; + p_points->dy[i] = 0.0; + p_points->deviation[i] = 0.0; + } + + /* Search ref for possible tie-points. Sets: p_points->contrast, + * p_points->x,y_reference. + */ + if( im__tbcalcon( ref, p_points ) ) + return( -1 ); + + /* For each candidate point, correlate against corresponding part of + * sec. Sets x,y_secondary and fills correlation and dx, dy. + */ + if( im__chkpair( ref, sec, p_points ) ) + return( -1 ); + + /* First call to im_clinear(). + */ + if( im__initialize( p_points ) ) + return( -1 ); + + /* Improve the selection of tiepoints until all abs(deviations) are + * < 1.0 by deleting all wrong points. + */ + if( im__improve( p_points, p_newpoints ) ) + return( -1 ); + + /* Average remaining offsets. + */ + if( im__avgdxdy( p_newpoints, &dx, &dy ) ) + return( -1 ); + + /* Offset with overlap position. + */ + *dx0 = -bottom.left + dx; + *dy0 = -bottom.top + dy; + + /* Write 1st order parameters too. + */ + *scale1 = newpoints.l_scale; + *angle1 = newpoints.l_angle; + *dx1 = newpoints.l_deltax; + *dy1 = newpoints.l_deltay; + + return( 0 ); +} + +int +im_tbmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xref, int yref, int xsec, int ysec, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + int dx0, dy0; + double scale1, angle1, dx1, dy1; + IMAGE *ref2, *sec2; + IMAGE *dummy; + + /* Correct overlap. dummy is just a placeholder used to ensure that + * memory used by the analysis phase is freed as soon as possible. + */ + if( !(dummy = im_open( "placeholder:1", "p" )) ) + return( -1 ); + if( im__find_tboverlap( ref, sec, dummy, + bandno, + xref, yref, xsec, ysec, + halfcorrelation, halfarea, + &dx0, &dy0, + &scale1, &angle1, &dx1, &dy1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + /* Balance. + */ + if( im__balance( ref, sec, out, + &ref2, &sec2, + dx0, dy0, balancetype ) ) + return( -1 ); + + /* Merge top-bottom. + */ + if( im_tbmerge( ref2, sec2, out, dx0, dy0, mwidth ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/mosaicing/man3/Makefile.am b/libsrc/mosaicing/man3/Makefile.am new file mode 100644 index 00000000..3b3034f7 --- /dev/null +++ b/libsrc/mosaicing/man3/Makefile.am @@ -0,0 +1,17 @@ +man_MANS = \ + im_affine.3 \ + im_correl.3 \ + im_global_balance.3 \ + im_global_balance_float.3 \ + im_lrmerge.3 \ + im_lrmosaic.3 \ + im_match_linear.3 \ + im_match_linear_search.3 \ + im_similarity.3 \ + im_similarity_area.3 \ + im_tbmerge.3 \ + im_remosaic.3 \ + im_tbmosaic.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/mosaicing/man3/im_affine.3 b/libsrc/mosaicing/man3/im_affine.3 new file mode 100644 index 00000000..a7663852 --- /dev/null +++ b/libsrc/mosaicing/man3/im_affine.3 @@ -0,0 +1,40 @@ +.TH IM_AFFINE 3 "21 December 1999" +.SH NAME +im_affine \- apply an affine transform to an image +.SH SYNOPSIS +.B #include + +int im_affine(in, out, a, b, c, d, dx, dy, x, y, w, h) +.br +.B IMAGE *in, *out; +.br +.B double a, b, c, d, dx, dy; +.br +.B int x, y; +.br +.B int w, h; + +.SH DESCRIPTION +.B im_affine() +applies an affine transformation on the image held by the IMAGE descriptor +in and puts the result at the location pointed by the IMAGE descriptor out. in +many have any number of bands, be any size, and have any non-complex type. + +The transformation is described by a, b, c, d, dx, dy. The point (x,y) in +the input is mapped onto point (X,Y) in the output by + + X = a * x + b * y + dx + Y = c * x + d * y + dy + +The area of the output image given by w, h, x, y is generated. (0,0) is +the position of the transformed top-left-hand corner of the input image. +Function im_affine resamples the transformed image using bilinear +interpolation. + +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH BUGS +As with most resamplers, im_affine(3) performs poorly at the edges of +images. +.SH SEE\ ALSO +im_similarity(3) diff --git a/libsrc/mosaicing/man3/im_correl.3 b/libsrc/mosaicing/man3/im_correl.3 new file mode 100644 index 00000000..89730696 --- /dev/null +++ b/libsrc/mosaicing/man3/im_correl.3 @@ -0,0 +1,49 @@ +.TH IM_CORREL 3 "13 May 1991" +.SH NAME +im_correl \- search for image match +.SH SYNOPSIS +#include + +int +.br +im_correl( IMAGE *ref, IMAGE *sec, +.br + int xref, int yref, int xsec, int ysec, +.br + int hwindowsize, int hsearchsize, +.br + double *correlation, int *x, int *y ) + +.SH DESCRIPTION +im_correl() is the base image-searching function used by the mosaicing +functions im_lrmosaic() and im_tbmosaic(), and by the matching funtions +im_match_linear() and im_match_linear_search(). + +It finds the position of the secondary image +.B sec +within +.B ref. +It searches the area around +.B xsec +, +.B ysec +for the best match for the area around +.B xref +, +.B yref. +It searches an area of size +.B hsearchsize +for a match of size +.B hwindowsize. +The position of the best match is returned, together with the correlation at +that point. + +Only the first band of each image is correlated. ref and sec may be very large +--- the function extracts and generated just the parts needed. Correlation is +done with im_spcor(); the position of the maximum is found with im_maxpos(). +.SH SEE ALSO +im_fastcor(3), im_spcor(3), im_maxpos(3), im_match_linear(3), im_lrmosaic(3). +.SH AUTHOR +J.Cupitt \- 22/02/93 +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1989-1996. diff --git a/libsrc/mosaicing/man3/im_global_balance.3 b/libsrc/mosaicing/man3/im_global_balance.3 new file mode 100644 index 00000000..adb5edb4 --- /dev/null +++ b/libsrc/mosaicing/man3/im_global_balance.3 @@ -0,0 +1,53 @@ +.TH IM_GLOBAL_BALANCE 3 "13 May 1991" +.SH NAME +im_global_balance, im_global_balancef \- perform global mosaic balancing on +an image +.SH SYNOPSIS +#include + +int +.br +im_global_balance( IMAGE *in, IMAGE *out, double gamma ) + +int +.br +im_global_balance_float( IMAGE *in, IMAGE *out, double gamma ) + +.SH DESCRIPTION +These functions takes an image assembled with the mosaicing functions +(im_*merge*(), im_*mosaic*()), take it apart, and reassemble it, globally +optimising the image balance. This is useful for assembling image mosaics +from sources where the exposure is uncontrolled and may vary from tile to tile +--- such as video, or photographic sources. + +The function finds a set of factors, one for each of the input images, and +scales each image by its factor before reassembling. The factors are chosen so +as to minimise the average grey-level difference between neighboring images at +their overlaps. Trivial overlaps (where the width and height of the overlap +are both less than 20 pixels) are ignored. + +The gamma parameter is the gamma of the image input system. It is used during +brightness adjustment. Set to 1.0 to disable gamma, to 1.6 for a typical IR +vidicon camera, or 2.3 for a typical video camera. + +It relies on information left by the mosaicing functions in ".desc" files. If +the ".desc" file of the input image has been corrupted, or is strangely +complicated, or if any of the original input images have been moved or +deleted, the function can fail. + +The function will fail for mosaics larger than about 7 by 7 frames, since it +will run out of file descriptors (UNIX sets a limit of 256 per process). To +balance larger mosaics, just assemble them in 7x7 sections, balancing and +saving each part in turn, before loading, assembling and balancing the final +image. The function can also fail if there are significant mosaicing errors. + +im_global_balancef() works as im_global_balance(), but outputs a float +rather than a uchar image. This lets you adjust the range of the image +manually, if the automatically-found scales are causing burn-out. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_lrmerge(3), im_lrmosaic(3). +.SH COPYRIGHT +Birkbeck College and the National Gallery, 1991 - 1995. diff --git a/libsrc/mosaicing/man3/im_global_balance_float.3 b/libsrc/mosaicing/man3/im_global_balance_float.3 new file mode 100644 index 00000000..a0cd72cc --- /dev/null +++ b/libsrc/mosaicing/man3/im_global_balance_float.3 @@ -0,0 +1 @@ +.so man3/im_global_balance.3 diff --git a/libsrc/mosaicing/man3/im_lrmerge.3 b/libsrc/mosaicing/man3/im_lrmerge.3 new file mode 100644 index 00000000..0e295312 --- /dev/null +++ b/libsrc/mosaicing/man3/im_lrmerge.3 @@ -0,0 +1,40 @@ +.TH IM_LRMERGE 3 "13 May 1991" +.SH NAME +im_lrmerge, im_tbmerge, im_lrsmerge, im_tbsmerge, im_lrmergeb, im_tbmergeb \- merges two images with a given dx and dy +.SH SYNOPSIS +#include + +int im_lrmerge( ref, sec, out, dx, dy, mwidth ) +.br +IMAGE *ref, *sec, *out; +.br +int dx, dy; +.br +int mwidth; + +int im_tbmerge( ref, sec, out, dx, dy, mwidth ) +.br +IMAGE *ref, *sec, *out; +.br +int dx, dy; +.br +int mwidth; + +.SH DESCRIPTION +im_lrmerge() and im_tbmerge() merge the images held by the image descriptors +reference and secondary (ref and sec) according to the values dx and dy. dx +and dy give the displacement of sec relative to ref. The result is written on +the image descriptor out. The program carries out a smooth merge using a +raised cosine function. Both work for any image type, including LABPACK. + +The functions treat pixels with the value zero as "transparent", that is, +zero pixels in the overlap area do not contribute to the merge. This makes it +possible to join non-rectangular images. + +The "mwidth" parameter limits the maximum width (or height) of the blend area. +A value of "-1" means "unlimited". All other negative values are errors. + +.SH RETURN VALUE +Both functions return 0 on success and -1 on error. +.SH SEE ALSO +im_lrmosaic(3), im_tbmosaic(3), im_match_linear(3). diff --git a/libsrc/mosaicing/man3/im_lrmosaic.3 b/libsrc/mosaicing/man3/im_lrmosaic.3 new file mode 100644 index 00000000..8192075b --- /dev/null +++ b/libsrc/mosaicing/man3/im_lrmosaic.3 @@ -0,0 +1,104 @@ +.TH IM_LRMOSAIC 3 "13 May 1991" +.SH NAME +im_lrmosaic, im_tbmosaic \- mosaic two images using a zero order procedure +.SH SYNOPSIS +#include + +int +.br +im_lrmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, +.br + int bandno, +.br + int xref, int yref, int xsec, int ysec, +.br + int halfcorrelation, int halfarea, +.br + int balancetype, +.br + int mwidth ) + +int +.br +im_tbmosaic( IMAGE *ref, IMAGE *sec, IMAGE *out, +.br + int bandno, +.br + int xref, int yref, int xsec, int ysec, +.br + int halfcorrelation, int halfarea, +.br + int balancetype, +.br + int mwidth ) + +.SH DESCRIPTION +im_lrmosaic() and im_tbmosaic() are used to mosaic two images (left-right and +top-bottom respectively). Both input images are held by the IMAGE descriptors +reference and secondary whereas the output is written on the IMAGE descriptor +out. + +In order to carry out mosaicing, the coordinates of one tie point are +required. The tie point is expected to be in the overlapping area and has +coordinates (xref, yref) on the reference image and (xsec, ysec) on the +secondary image. The tie-point is not used as a start point for the search, +but is used to specify the overlap of the two images. + +The functions split the overlap area into three parts (top, middle and bottom; +or left, middle and right) and search the reference image in each part for the +20 best high contrast points. These 60 points are then searched for in the +secondary image, giving a set of 60 possible corrected vectors. + +A straight line is fitted through the 60 vectors, and points discarded which +lie a significant distance from the line. The line is then refitted to the +remaining points, and the process repeated until either all remaining points +lie on a straight line, or too many points have been discarded. + +If a good straight line fit is found, ref and sec are joined. If no fit was +found, the functions fail with an error message. Note that these functions +detect rotation: if the straight line found requires sec to be rotated, they +also fail with an error message. + +Each function requires three more parameters: + +halfcorrelationsize - sets the size of the fragments of ref + for which the function searches sec. The actual window + will be of size 2*halfcorrelationsize+1. We recommend a + value of 5. + +halfareasize - sets the size of the area of sec that is + searched. The actual area searched will be of size + 2*halfareasize+1. We recommend a value of 14. + +balancetype - sets the style of the balancing the functions + perform. Balancing finds the average value of pixels in + the overlap area, and scales the left and right images + (or top and bottom images) so as to make the images + match in average overlap. Possible values are: + + 0 - means do no balancing. + + 1 - means keep the left image unadjusted and adjust + the contrast of the right image to match the left. + + 2 - means keep the right image unadjusted and scale + the left image to match it. + + 3 - means adjust the contrast of both the left and + right images to bring both averages to a middle + value. The middle value chosen is weighted by the + number of pixels in each image: large images will be + adjusted less than small images. + +Balancing is useful for mosaicing frames from photographic or video sources +where exact colour control is impossible and exposure varies from frame to +frame. Balancing is only allowed for uncoded uchar images. + +The final "mwidth" parameter sets the maximum blend width, see im_lrmerge(3). + +See also im_global_balance() for a better way of balancing large mosaics. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_lrmerge(3), im_tbmerge(3), im_global_balance(3). diff --git a/libsrc/mosaicing/man3/im_match_linear.3 b/libsrc/mosaicing/man3/im_match_linear.3 new file mode 100644 index 00000000..d397a7a6 --- /dev/null +++ b/libsrc/mosaicing/man3/im_match_linear.3 @@ -0,0 +1 @@ +.so man3/im_match_linear_search.3 diff --git a/libsrc/mosaicing/man3/im_match_linear_search.3 b/libsrc/mosaicing/man3/im_match_linear_search.3 new file mode 100644 index 00000000..a5b52574 --- /dev/null +++ b/libsrc/mosaicing/man3/im_match_linear_search.3 @@ -0,0 +1,55 @@ +.TH MATCH_LINEAR 3 "13 May 1991" +.SH NAME +im_match_linear_search, im_match_linear \- resample to make a match +.SH SYNOPSIS + +#include + +int +.br +im_match_linear( IMAGE *ref, IMAGE *sec, IMAGE *out, +.br + int xr1, int yr1, int xs1, int ys1, +.br + int xr2, int yr2, int xs2, int ys2 ) + +int +.br +im_match_linear_search( IMAGE *ref, IMAGE *sec, IMAGE *out, +.br + int xr1, int yr1, int xs1, int ys1, +.br + int xr2, int yr2, int xs2, int ys2, +.br + int hwindowsize, int hsearchsize ) + +.SH DESCRIPTION +im_match_linear_search() attempts to transform sec to make it match +ref. The transformation is linear, that is, it only involves scale, +rotate and translate. + +im_match_linear_search() requires a pair of tie points to fix the parameters of +its transformation. You should pick points as far apart as possible to +increase accuracy. im_match_linear_search() will search the area in the image +around each tie point for a good fit, so your selection of points need not +be exact. WARNING! This searching process will fail for rotations of more +than about 10 degrees or for scales of more than about 10 percent. +The best you can hope for is < 1 pixel error, since the command does not +attempt sub-pixel correlation. + +hwindowsize and hsearchsize set the size of the area to be searched: we +recommend values of 5 and 14. + +The output image is positioned and clipped so that you can immediately +subtract it from orig to obtain pixel difference images. + +im_match_linear() works exactly as im_match_linear_search(), but does not +attempt to correlate to correct your tie points. It can thus be used for any +angle and any scale, but you must be far more careful in your selection. + +.SH SEE ALSO +im_lrmosaic(). +.SH AUTHORS +J.Ph.Laurent \- 12/12/92 +.br +J.Cupitt \- 22/02/93 diff --git a/libsrc/mosaicing/man3/im_remosaic.3 b/libsrc/mosaicing/man3/im_remosaic.3 new file mode 100644 index 00000000..406ecbc6 --- /dev/null +++ b/libsrc/mosaicing/man3/im_remosaic.3 @@ -0,0 +1,28 @@ +.TH IM_REMOSAIC 3 "7 Nov 2001" +.SH NAME +im_remosaic \- rebuild a mosaic, substituting filenames +.SH SYNOPSIS +#include + +int im_remosaic( IMAGE *in, IMAGE *out, +.br + const char *old_str, const char *new_str ) + +.SH DESCRIPTION +.B im_remosaic(3) +works rather as im_global_balance(). It takes apart the mosaiced image in and +rebuilds it, substituting images. + +Unlike +.B im_global_balance(3) +images are substituted based on their filenames. The rightmost occurence of +the string old_str is swapped for new_str, that file is opened, and that image +substituted for the old image. + +It's convenient for multispectral images. You can mosaic one band, then use +that mosaic as a template for mosaicing the others automatically. + +.SH RETURN VALUE +Functions return 0 on success and -1 on error. +.SH SEE ALSO +im_global_balance(3) diff --git a/libsrc/mosaicing/man3/im_similarity.3 b/libsrc/mosaicing/man3/im_similarity.3 new file mode 100644 index 00000000..e9a1151d --- /dev/null +++ b/libsrc/mosaicing/man3/im_similarity.3 @@ -0,0 +1 @@ +.so man3/im_similarity_area.3 diff --git a/libsrc/mosaicing/man3/im_similarity_area.3 b/libsrc/mosaicing/man3/im_similarity_area.3 new file mode 100644 index 00000000..330a480e --- /dev/null +++ b/libsrc/mosaicing/man3/im_similarity_area.3 @@ -0,0 +1,61 @@ +.TH IM_SIMILARITY_AREA 3 "13 January 1992" +.SH NAME +im_similarity_area, im_similarity \- apply a similarity transform to an image +.SH SYNOPSIS +.B #include + +int im_similarity_area(in, out, s, a, dx, dy, x, y, w, h) +.br +.B IMAGE *in, *out; +.br +.B double s, a, dx, dy; +.br +.B int x, y; +.br +.B int w, h; + +int im_similarity(in, out, s, a, dx, dy) +.br +.B IMAGE *in, *out; +.br +.B double s, a, dx, dy; + +.SH DESCRIPTION +.B im_similarity_area() +applies a similarity transformation on the image held by the IMAGE descriptor +in and puts the result at the location pointed by the IMAGE descriptor out. in +many have any number of bands, be any size, and have any non-complex type. + +The transformation is described by s, a, dx, dy. The point (x,y) in the input +is mapped onto point (X,Y) in the output by + + X = s * x - a * y + dx + Y = a * x + s * y + dy + +s and a do not correspond to scale and angle of the transformation; the actual +scale and angle are given by the equations: + + scale = sqrt(s*s + a*a) + angle = arctan(s/a). + +The area of the output image given by x, y, w, h is generated. (0,0) is +the position of the transformed top-left-hand corner of the input image. +Function im_similarity_area resamples the transformed image using bilinear +interpolation. + +im_similarity works exactly as im_similarity_area, but calculates x, y, w, h +for you such that the rectangle described just encloses all of the transformed +input pixels. +.SH RETURN VALUE +The functions return 0 on success and -1 on error. +.SH BUGS +As with most resamplers, im_similarity performs poorly at the edges of +images. +.SH SEE\ ALSO +similarity(1), similarity_area(1) +.SH AUTHORS +N. Dessipris -\ 13/01/1992 +.br +J.Ph. Laurent -\ 12/12/92 +.br +J. Cupitt -\ 22/02/93 diff --git a/libsrc/mosaicing/man3/im_tbmerge.3 b/libsrc/mosaicing/man3/im_tbmerge.3 new file mode 100644 index 00000000..ee6cab70 --- /dev/null +++ b/libsrc/mosaicing/man3/im_tbmerge.3 @@ -0,0 +1 @@ +.so man3/im_lrmerge.3 diff --git a/libsrc/mosaicing/man3/im_tbmosaic.3 b/libsrc/mosaicing/man3/im_tbmosaic.3 new file mode 100644 index 00000000..385c7d7d --- /dev/null +++ b/libsrc/mosaicing/man3/im_tbmosaic.3 @@ -0,0 +1 @@ +.so man3/im_lrmosaic.3 diff --git a/libsrc/mosaicing/match.c b/libsrc/mosaicing/match.c new file mode 100644 index 00000000..24b38788 --- /dev/null +++ b/libsrc/mosaicing/match.c @@ -0,0 +1,154 @@ +/* Match images. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "mosaic.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Given a pair of points, return scale, angle, dx, dy to resample the 2nd + * image with. + */ +int +im__coeff( int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + double *a, double *b, double *dx, double *dy ) +{ + DOUBLEMASK *in, *out; + + if( !(in = im_create_dmask( "in", 4, 4 )) ) { + im_errormsg( "im__coeff: unable to allocate matrix" ); + return( -1 ); + } + + in->coeff[0] = (double)xs1; + in->coeff[1] = (double)-ys1; + in->coeff[2] = 1.0; + in->coeff[3] = 0.0; + in->coeff[4] = (double)ys1; + in->coeff[5] = (double)xs1; + in->coeff[6] = 0.0; + in->coeff[7] = 1.0; + in->coeff[8] = (double)xs2; + in->coeff[9] = (double)-ys2; + in->coeff[10] = 1.0; + in->coeff[11] = 0.0; + in->coeff[12] = (double)ys2; + in->coeff[13] = (double)xs2; + in->coeff[14] = 0.0; + in->coeff[15] = 1.0; + + if( !(out = im_matinv( in, "out" )) ) { + im_free_dmask( in ); + im_errormsg( "im__coeff: unable to invert matrix" ); + return( -1 ); + } + + *a = out->coeff[0]*xr1 + out->coeff[1]*yr1 + + out->coeff[2]*xr2 + out->coeff[3]*yr2; + *b = out->coeff[4]*xr1 + out->coeff[5]*yr1 + + out->coeff[6]*xr2 + out->coeff[7]*yr2; + *dx= out->coeff[8]*xr1 + out->coeff[9]*yr1 + + out->coeff[10]*xr2 + out->coeff[11]*yr2; + *dy= out->coeff[12]*xr1 + out->coeff[13]*yr1 + + out->coeff[14]*xr2 + out->coeff[15]*yr2; + + im_free_dmask( in ); + im_free_dmask( out ); + + return( 0 ); +} + +int +im_match_linear( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2 ) +{ + double a, b, dx, dy; + int w, h, x, y; + + /* Solve to get scale + rot + disp to obtain match. + */ + if( im__coeff( xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + &a, &b, &dx, &dy ) ) + return( -1 ); + + /* Output area of ref image. + */ + x = 0; + y = 0; + w = ref->Xsize; + h = ref->Ysize; + + /* Transform image! + */ + if( im_similarity_area( sec, out, a, b, dx, dy, x, y, w, h ) ) + return( -1 ); + + return( 0 ); +} + +int +im_match_linear_search( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int hwindowsize, int hsearchsize ) +{ + int xs3, ys3; + int xs4, ys4; + double cor1, cor2; + + /* Search for new tie-points. + */ + if( im_correl( ref, sec, xr1, yr1, xs1, ys1, + hwindowsize, hsearchsize, &cor1, &xs3, &ys3 ) ) + return( -1 ); + if( im_correl( ref, sec, xr2, yr2, xs2, ys2, + hwindowsize, hsearchsize, &cor2, &xs4, &ys4 ) ) + return( -1 ); + + /* ... and match_linear. + */ + if( im_match_linear( ref, sec, out, + xr1, yr1, xs3, ys3, xr2, yr2, xs4, ys4 ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/mosaicing/merge.h b/libsrc/mosaicing/merge.h new file mode 100644 index 00000000..a65fc93a --- /dev/null +++ b/libsrc/mosaicing/merge.h @@ -0,0 +1,151 @@ +/* Declarations for code shared between im_lrmerge() and im_tbmerge(). + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Number of entries in blend table. As a power of two as well, for >>ing. + */ +#define BLEND_SHIFT (10) +#define BLEND_SIZE (1< +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "mosaic.h" +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +/* define this to get old not-really-working joiner. +#define OLD + */ + +/* Like im_similarity(), but return the transform we generated. + */ +static int +apply_similarity( Transformation *trn, IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy ) +{ + trn->iarea.left = 0; + trn->iarea.top = 0; + trn->iarea.width = in->Xsize; + trn->iarea.height = in->Ysize; + trn->a = a; + trn->b = -b; + trn->c = b; + trn->d = a; + trn->dx = dx; + trn->dy = dy; + im__transform_set_area( trn ); + if( im__transform_calc_inverse( trn ) ) + return( -1 ); + + if( im__affine( in, out, trn ) ) + return( -1 ); + + return( 0 ); +} + +/* A join function ... either left-right or top-bottom rotscalemerge. + */ +typedef int (*joinfn)( IMAGE *, IMAGE *, IMAGE *, + double, double, double, double, int ); + +/* similarity+lrmerge. + */ +int +im__lrmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + double a, double b, double dx, double dy, int mwidth ) +{ + Transformation trn; + IMAGE *t1 = im_open_local( out, "im_lrmosaic1:1", "p" ); + VBuf buf; + char text[1024]; + + /* Scale, rotate and displace sec. + */ + if( !t1 || apply_similarity( &trn, sec, t1, a, b, dx, dy ) ) + return( -1 ); + + /* And join to ref. + */ + if( im__lrmerge( ref, t1, out, + -trn.oarea.left, -trn.oarea.top, mwidth ) ) + return( -1 ); + + /* Note parameters in history file ... for global balance to pick up + * later. + */ + im_buf_init_static( &buf, text, 1024 ); + im_buf_appendf( &buf, "#LRROTSCALE <%s> <%s> <%s> <", + ref->filename, sec->filename, out->filename ); + im_buf_appendg( &buf, a ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, b ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, dx ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, dy ); + im_buf_appendf( &buf, "> <%d>", mwidth ); + if( im_histlin( out, im_buf_all( &buf ) ) ) + return( -1 ); + + return( 0 ); +} + +/* similarity+tbmerge. + */ +int +im__tbmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + double a, double b, double dx, double dy, int mwidth ) +{ + Transformation trn; + IMAGE *t1 = im_open_local( out, "im_lrmosaic1:2", "p" ); + VBuf buf; + char text[1024]; + + /* Scale, rotate and displace sec. + */ + if( !t1 || apply_similarity( &trn, sec, t1, a, b, dx, dy ) ) + return( -1 ); + + /* And join to ref. + */ + if( im__tbmerge( ref, t1, out, + -trn.oarea.left, -trn.oarea.top, mwidth ) ) + return( -1 ); + + /* Note parameters in history file ... for global balance to pick up + * later. + */ + im_buf_init_static( &buf, text, 1024 ); + im_buf_appendf( &buf, "#TBROTSCALE <%s> <%s> <%s> <", + ref->filename, sec->filename, out->filename ); + im_buf_appendg( &buf, a ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, b ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, dx ); + im_buf_appendf( &buf, "> <" ); + im_buf_appendg( &buf, dy ); + im_buf_appendf( &buf, "> <%d>", mwidth ); + if( im_histlin( out, im_buf_all( &buf ) ) ) + return( -1 ); + + return( 0 ); +} + +/* Join two images, using a pair of tie-points as parameters. + */ +static int +rotjoin( IMAGE *ref, IMAGE *sec, IMAGE *out, joinfn jfn, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ) +{ + double a, b, dx, dy; + + /* Solve to get scale + rot + disp. + */ + if( im__coeff( xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + &a, &b, &dx, &dy ) ) + return( -1 ); + + /* Scale, rotate and displace sec. + */ + if( jfn( ref, sec, out, a, b, dx, dy, mwidth ) ) + return( -1 ); + + return( 0 ); +} + +/* 1st order left-right merge. + */ +int +im_lrmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ) +{ + return( rotjoin( ref, sec, out, im__lrmerge1, + xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, mwidth ) ); +} + +/* 1st order top-bottom merge. + */ +int +im_tbmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int mwidth ) +{ + return( rotjoin( ref, sec, out, im__tbmerge1, + xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, mwidth ) ); +} + +/* Like rotjoin, but do a search to refine the tie-points. + */ +static int +rotjoin_search( IMAGE *ref, IMAGE *sec, IMAGE *out, joinfn jfn, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + Transformation trn; + double cor1, cor2; + double a, b, dx, dy; + double xs3, ys3; + double xs4, ys4; + int xs5, ys5; + int xs6, ys6; + double xs7, ys7; + double xs8, ys8; + + /* Temps. + */ + IMAGE *t[3]; + + if( im_open_local_array( out, t, 3, "rotjoin_search", "p" ) ) + return( -1 ); + + /* Unpack LABQ to LABS for correlation. + */ + if( ref->Coding == IM_CODING_LABQ ) { + if( im_LabQ2LabS( ref, t[0] ) ) + return( -1 ); + } + else + t[0] = ref; + if( sec->Coding == IM_CODING_LABQ ) { + if( im_LabQ2LabS( sec, t[1] ) ) + return( -1 ); + } + else + t[1] = sec; + + /* Solve to get scale + rot + disp. + */ + if( im__coeff( xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + &a, &b, &dx, &dy ) || + apply_similarity( &trn, t[1], t[2], a, b, dx, dy ) ) + return( -1 ); + + /* Map points on sec to rotated image. + */ + im__transform_forward( &trn, xs1, ys1, &xs3, &ys3 ); + im__transform_forward( &trn, xs2, ys2, &xs4, &ys4 ); + + /* Refine tie-points on rotated image. Remember the clip + * im__transform_set_area() has set, and move the sec tie-points + * accordingly. + */ + if( im_correl( t[0], t[2], xr1, yr1, + xs3 - trn.oarea.left, ys3 - trn.oarea.top, + halfcorrelation, halfarea, &cor1, &xs5, &ys5 ) ) + return( -1 ); + if( im_correl( t[0], t[2], xr2, yr2, + xs4 - trn.oarea.left, ys4 - trn.oarea.top, + halfcorrelation, halfarea, &cor2, &xs6, &ys6 ) ) + return( -1 ); + +#ifdef DEBUG + printf( "rotjoin_search: nudged pair 1 from %d, %d to %d, %d\n", + xs3 - trn.oarea.left, ys3 - trn.oarea.top, + xs5, ys5 ); + printf( "rotjoin_search: nudged pair 2 from %d, %d to %d, %d\n", + xs4 - trn.oarea.left, ys4 - trn.oarea.top, + xs6, ys6 ); +#endif /*DEBUG*/ + + /* Put the sec tie-points back into output space. + */ + xs5 += trn.oarea.left; + ys5 += trn.oarea.top; + xs6 += trn.oarea.left; + ys6 += trn.oarea.top; + + /* ... and now back to input space again. + */ + im__transform_inverse( &trn, xs5, ys5, &xs7, &ys7 ); + im__transform_inverse( &trn, xs6, ys6, &xs8, &ys8 ); + + /* Recalc the transform using the refined points. + */ + if( im__coeff( xr1, yr1, xs7, ys7, xr2, yr2, xs8, ys8, + &a, &b, &dx, &dy ) ) + return( -1 ); + + /* Scale and rotate final. + */ + if( jfn( ref, sec, out, a, b, dx, dy, mwidth ) ) + return( -1 ); + + return( 0 ); +} + +/* 1st order lr mosaic. + */ +int +im_lrmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + return( rotjoin_search( ref, sec, out, im__lrmerge1, + bandno, + xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + halfcorrelation, halfarea, balancetype, + mwidth ) ); +} + +/* 1st order tb mosaic. + */ +int +im_tbmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + return( rotjoin_search( ref, sec, out, im__tbmerge1, + bandno, + xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + halfcorrelation, halfarea, balancetype, mwidth ) ); +} + +#ifdef OLD +/* 1st order mosaic using im__find_lroverlap() ... does not work too well :( + * Look at im__find_lroverlap() for problem? + */ +static int +old_lrmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out, + int bandno, + int xr1, int yr1, int xs1, int ys1, + int xr2, int yr2, int xs2, int ys2, + int halfcorrelation, int halfarea, + int balancetype, + int mwidth ) +{ + Transformation trn1, trn2; + int dx0, dy0; + double a, b, dx, dy; + double a1, b1, dx1, dy1; + double af, bf, dxf, dyf; + int xpos, ypos; + int xpos1, ypos1; + + /* Temps. + */ + IMAGE *t1 = im_open_local( out, "im_lrmosaic1:1", "p" ); + IMAGE *t2 = im_open_local( out, "im_lrmosaic1:2", "p" ); + IMAGE *dummy; + + if( !t1 || !t2 ) + return( -1 ); + + /* Solve to get scale + rot + disp. + */ + if( im__coeff( xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, + &a, &b, &dx, &dy ) || + apply_similarity( &trn1, sec, t1, a, b, dx, dy ) ) + return( -1 ); + + /* Correct tie-points. dummy is just a placeholder used to ensure that + * memory used by the analysis phase is freed as soon as possible. + */ + if( !(dummy = im_open( "placeholder:1", "p" )) ) + return( -1 ); + if( im__find_lroverlap( ref, t1, dummy, + bandno, + -trn1.area.left, -trn1.area.top, 0, 0, + halfcorrelation, halfarea, + &dx0, &dy0, + &a1, &b1, &dx1, &dy1 ) ) { + im_close( dummy ); + return( -1 ); + } + im_close( dummy ); + + /* Now combine the two transformations to get a corrected transform. + */ + af = a1 * a - b1 * b; + bf = a1 * b + b1 * a; + dxf = a1 * dx - b1 * dy + dx1; + dyf = b1 * dx + a1 * dy + dy1; + + printf( "transform was: a = %g, b = %g, dx = %g, dy = %g\n", + a, b, dx, dy ); + printf( "correction: a = %g, b = %g, dx = %g, dy = %g\n", + a1, b1, dx1, dy1 ); + printf( "final: a = %g, b = %g, dx = %g, dy = %g\n", + af, bf, dxf, dyf ); + + /* Scale and rotate final. + */ + if( apply_similarity( &trn2, sec, t2, af, bf, dxf, dyf ) ) + return( -1 ); + + printf( "disp: trn1 left = %d, top = %d\n", + trn1.area.left, trn1.area.top ); + printf( "disp: trn2 left = %d, top = %d\n", + trn2.area.left, trn2.area.top ); + + /* And join to ref. + */ + if( im_lrmerge( ref, t2, out, + -trn2.area.left, -trn2.area.top, mwidth ) ) + return( -1 ); + + return( 0 ); +} +#endif /*OLD*/ diff --git a/libsrc/mosaicing/mosaicing_dispatch.c b/libsrc/mosaicing/mosaicing_dispatch.c new file mode 100644 index 00000000..c12ea146 --- /dev/null +++ b/libsrc/mosaicing/mosaicing_dispatch.c @@ -0,0 +1,847 @@ +/* Function dispatch tables for mosaicing. + * + * J. Cupitt, 23/2/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Merge args. + */ +static im_arg_desc merge_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "dx" ), + IM_INPUT_INT( "dy" ), + IM_INPUT_INT( "mwidth" ) +}; + +/* Merge1 args. + */ +static im_arg_desc merge1_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xr1" ), + IM_INPUT_INT( "yr1" ), + IM_INPUT_INT( "xs1" ), + IM_INPUT_INT( "ys1" ), + IM_INPUT_INT( "xr2" ), + IM_INPUT_INT( "yr2" ), + IM_INPUT_INT( "xs2" ), + IM_INPUT_INT( "ys2" ), + IM_INPUT_INT( "mwidth" ) +}; + +/* Mosaic args. + */ +static im_arg_desc mosaic_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "bandno" ), + IM_INPUT_INT( "xr" ), + IM_INPUT_INT( "yr" ), + IM_INPUT_INT( "xs" ), + IM_INPUT_INT( "ys" ), + IM_INPUT_INT( "halfcorrelation" ), + IM_INPUT_INT( "halfarea" ), + IM_INPUT_INT( "balancetype" ), + IM_INPUT_INT( "mwidth" ) +}; + +/* Mosaic1 args. + */ +static im_arg_desc mosaic1_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "bandno" ), + IM_INPUT_INT( "xr1" ), + IM_INPUT_INT( "yr1" ), + IM_INPUT_INT( "xs1" ), + IM_INPUT_INT( "ys1" ), + IM_INPUT_INT( "xr2" ), + IM_INPUT_INT( "yr2" ), + IM_INPUT_INT( "xs2" ), + IM_INPUT_INT( "ys2" ), + IM_INPUT_INT( "halfcorrelation" ), + IM_INPUT_INT( "halfarea" ), + IM_INPUT_INT( "balancetype" ), + IM_INPUT_INT( "mwidth" ) +}; + +/* Call im_lrmosaic via arg vector. + */ +static int +lrmosaic_vec( im_object *argv ) +{ + int bandno = *((int *) argv[3]); + int xr = *((int *) argv[4]); + int yr = *((int *) argv[5]); + int xs = *((int *) argv[6]); + int ys = *((int *) argv[7]); + int halfcorrelation = *((int *) argv[8]); + int halfarea = *((int *) argv[9]); + int balancetype = *((int *) argv[10]); + int mwidth = *((int *) argv[11]); + + return( im_lrmosaic( argv[0], argv[1], argv[2], + bandno, + xr, yr, xs, ys, + halfcorrelation, halfarea, + balancetype, mwidth ) ); +} + +/* Call im_lrmosaic1 via arg vector. + */ +static int +lrmosaic1_vec( im_object *argv ) +{ + int bandno = *((int *) argv[3]); + int xr1 = *((int *) argv[4]); + int yr1 = *((int *) argv[5]); + int xs1 = *((int *) argv[6]); + int ys1 = *((int *) argv[7]); + int xr2 = *((int *) argv[8]); + int yr2 = *((int *) argv[9]); + int xs2 = *((int *) argv[10]); + int ys2 = *((int *) argv[11]); + int halfcorrelation = *((int *) argv[12]); + int halfarea = *((int *) argv[13]); + int balancetype = *((int *) argv[14]); + int mwidth = *((int *) argv[15]); + + return( im_lrmosaic1( argv[0], argv[1], argv[2], + bandno, + xr1, yr1, xs1, ys1, + xr2, yr2, xs2, ys2, + halfcorrelation, halfarea, + balancetype, mwidth ) ); +} + +/* Description of im_lrmosaic. + */ +static im_function lrmosaic_desc = { + "im_lrmosaic", /* Name */ + "left-right mosaic of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + lrmosaic_vec, /* Dispatch function */ + IM_NUMBER( mosaic_args ), /* Size of arg list */ + mosaic_args /* Arg list */ +}; + +static im_arg_desc find_overlap_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_INPUT_INT( "bandno" ), + IM_INPUT_INT( "xr" ), + IM_INPUT_INT( "yr" ), + IM_INPUT_INT( "xs" ), + IM_INPUT_INT( "ys" ), + IM_INPUT_INT( "halfcorrelation" ), + IM_INPUT_INT( "halfarea" ), + IM_OUTPUT_INT( "dx0" ), + IM_OUTPUT_INT( "dy0" ), + IM_OUTPUT_DOUBLE( "scale1" ), + IM_OUTPUT_DOUBLE( "angle1" ), + IM_OUTPUT_DOUBLE( "dx1" ), + IM_OUTPUT_DOUBLE( "dy1" ) +}; + +/* Call im__find_lroverlap via arg vector. + */ +static int +find_lroverlap_vec( im_object *argv ) +{ + int bandno = *((int *) argv[2]); + int xr = *((int *) argv[3]); + int yr = *((int *) argv[4]); + int xs = *((int *) argv[5]); + int ys = *((int *) argv[6]); + int halfcorrelation = *((int *) argv[7]); + int halfarea = *((int *) argv[8]); + int *dx0 = (int *) argv[9]; + int *dy0 = (int *) argv[10]; + double *scale1 = (double *) argv[11]; + double *angle1 = (double *) argv[12]; + double *dx1 = (double *) argv[13]; + double *dy1 = (double *) argv[14]; + + IMAGE *t; + int result; + + if( !(t = im_open( "find_lroverlap_vec", "p" )) ) + return( -1 ); + result = im__find_lroverlap( argv[0], argv[1], t, + bandno, + xr, yr, xs, ys, + halfcorrelation, halfarea, + dx0, dy0, scale1, angle1, dx1, dy1 ); + im_close( t ); + + return( result ); +} + +/* Description of im__find_lroverlap. + */ +static im_function find_lroverlap_desc = { + "im__find_lroverlap", /* Name */ + "search for left-right overlap of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + find_lroverlap_vec, /* Dispatch function */ + IM_NUMBER( find_overlap_args ), /* Size of arg list */ + find_overlap_args /* Arg list */ +}; + +/* Description of im_lrmosaic1. + */ +static im_function lrmosaic1_desc = { + "im_lrmosaic1", /* Name */ + "first-order left-right mosaic of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + lrmosaic1_vec, /* Dispatch function */ + IM_NUMBER( mosaic1_args ), /* Size of arg list */ + mosaic1_args /* Arg list */ +}; + +/* Call im_tbmosaic via arg vector. + */ +static int +tbmosaic_vec( im_object *argv ) +{ + int bandno = *((int *) argv[3]); + int x1 = *((int *) argv[4]); + int y1 = *((int *) argv[5]); + int x2 = *((int *) argv[6]); + int y2 = *((int *) argv[7]); + int halfcorrelation = *((int *) argv[8]); + int halfarea = *((int *) argv[9]); + int balancetype = *((int *) argv[10]); + int mwidth = *((int *) argv[11]); + + return( im_tbmosaic( argv[0], argv[1], argv[2], + bandno, + x1, y1, x2, y2, + halfcorrelation, halfarea, + balancetype, mwidth ) ); +} + +/* Call im_tbmosaic1 via arg vector. + */ +static int +tbmosaic1_vec( im_object *argv ) +{ + int bandno = *((int *) argv[3]); + int xr1 = *((int *) argv[4]); + int yr1 = *((int *) argv[5]); + int xs1 = *((int *) argv[6]); + int ys1 = *((int *) argv[7]); + int xr2 = *((int *) argv[8]); + int yr2 = *((int *) argv[9]); + int xs2 = *((int *) argv[10]); + int ys2 = *((int *) argv[11]); + int halfcorrelation = *((int *) argv[12]); + int halfarea = *((int *) argv[13]); + int balancetype = *((int *) argv[14]); + int mwidth = *((int *) argv[15]); + + return( im_tbmosaic1( argv[0], argv[1], argv[2], + bandno, + xr1, yr1, xs1, ys1, + xr2, yr2, xs2, ys2, + halfcorrelation, halfarea, + balancetype, mwidth ) ); +} + +/* Call im__find_tboverlap via arg vector. + */ +static int +find_tboverlap_vec( im_object *argv ) +{ + int bandno = *((int *) argv[2]); + int xr = *((int *) argv[3]); + int yr = *((int *) argv[4]); + int xs = *((int *) argv[5]); + int ys = *((int *) argv[6]); + int halfcorrelation = *((int *) argv[7]); + int halfarea = *((int *) argv[8]); + int *dx0 = (int *) argv[9]; + int *dy0 = (int *) argv[10]; + double *scale1 = (double *) argv[11]; + double *angle1 = (double *) argv[12]; + double *dx1 = (double *) argv[13]; + double *dy1 = (double *) argv[14]; + + IMAGE *t; + int result; + + if( !(t = im_open( "find_tboverlap_vec", "p" )) ) + return( -1 ); + result = im__find_tboverlap( argv[0], argv[1], t, + bandno, + xr, yr, xs, ys, + halfcorrelation, halfarea, + dx0, dy0, scale1, angle1, dx1, dy1 ); + im_close( t ); + + return( result ); +} + +/* Description of im__find_tboverlap. + */ +static im_function find_tboverlap_desc = { + "im__find_tboverlap", /* Name */ + "search for top-bottom overlap of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + find_tboverlap_vec, /* Dispatch function */ + IM_NUMBER( find_overlap_args ), /* Size of arg list */ + find_overlap_args /* Arg list */ +}; + +/* Description of im_tbmosaic. + */ +static im_function tbmosaic_desc = { + "im_tbmosaic", /* Name */ + "top-bottom mosaic of in1 and in2",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + tbmosaic_vec, /* Dispatch function */ + IM_NUMBER( mosaic_args ), /* Size of arg list */ + mosaic_args /* Arg list */ +}; + +/* Description of im_tbmosaic1. + */ +static im_function tbmosaic1_desc = { + "im_tbmosaic1", /* Name */ + "first-order top-bottom mosaic of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + tbmosaic1_vec, /* Dispatch function */ + IM_NUMBER( mosaic1_args ), /* Size of arg list */ + mosaic1_args /* Arg list */ +}; + +/* Call im_lrmerge via arg vector. + */ +static int +lrmerge_vec( im_object *argv ) +{ + int dx = *((int *) argv[3]); + int dy = *((int *) argv[4]); + int mwidth = *((int *) argv[5]); + + return( im_lrmerge( argv[0], argv[1], argv[2], dx, dy, mwidth ) ); +} + +/* Call im_lrmerge1 via arg vector. + */ +static int +lrmerge1_vec( im_object *argv ) +{ + int xr1 = *((int *) argv[3]); + int yr1 = *((int *) argv[4]); + int xs1 = *((int *) argv[5]); + int ys1 = *((int *) argv[6]); + int xr2 = *((int *) argv[7]); + int yr2 = *((int *) argv[8]); + int xs2 = *((int *) argv[9]); + int ys2 = *((int *) argv[10]); + int mwidth = *((int *) argv[11]); + + return( im_lrmerge1( argv[0], argv[1], argv[2], + xr1, yr1, xs1, ys1, + xr2, yr2, xs2, ys2, mwidth ) ); +} + +/* Description of im_lrmerge. + */ +static im_function lrmerge_desc = { + "im_lrmerge", /* Name */ + "left-right merge of in1 and in2",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + lrmerge_vec, /* Dispatch function */ + IM_NUMBER( merge_args ), /* Size of arg list */ + merge_args /* Arg list */ +}; + +/* Description of im_lrmerge1. + */ +static im_function lrmerge1_desc = { + "im_lrmerge1", /* Name */ + "first-order left-right merge of ref and sec",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + lrmerge1_vec, /* Dispatch function */ + IM_NUMBER( merge1_args ), /* Size of arg list */ + merge1_args /* Arg list */ +}; + +/* Call im_tbmerge via arg vector. + */ +static int +tbmerge_vec( im_object *argv ) +{ + int dx = *((int *) argv[3]); + int dy = *((int *) argv[4]); + int mwidth = *((int *) argv[5]); + + return( im_tbmerge( argv[0], argv[1], argv[2], dx, dy, mwidth ) ); +} + +/* Call im_tbmerge1 via arg vector. + */ +static int +tbmerge1_vec( im_object *argv ) +{ + int xr1 = *((int *) argv[3]); + int yr1 = *((int *) argv[4]); + int xs1 = *((int *) argv[5]); + int ys1 = *((int *) argv[6]); + int xr2 = *((int *) argv[7]); + int yr2 = *((int *) argv[8]); + int xs2 = *((int *) argv[9]); + int ys2 = *((int *) argv[10]); + int mwidth = *((int *) argv[11]); + + return( im_tbmerge1( argv[0], argv[1], argv[2], + xr1, yr1, xs1, ys1, + xr2, yr2, xs2, ys2, mwidth ) ); +} + +/* Description of im_tbmerge. + */ +static im_function tbmerge_desc = { + "im_tbmerge", /* Name */ + "top-bottom merge of in1 and in2",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + tbmerge_vec, /* Dispatch function */ + IM_NUMBER( merge_args ), /* Size of arg list */ + merge_args /* Arg list */ +}; + +/* Description of im_tbmerge1. + */ +static im_function tbmerge1_desc = { + "im_tbmerge1", /* Name */ + "first-order top-bottom merge of in1 and in2",/* Description */ + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + tbmerge1_vec, /* Dispatch function */ + IM_NUMBER( merge1_args ), /* Size of arg list */ + merge1_args /* Arg list */ +}; + +/* affine args + */ +static im_arg_desc affine_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_DOUBLE( "c" ), + IM_INPUT_DOUBLE( "d" ), + IM_INPUT_DOUBLE( "dx" ), + IM_INPUT_DOUBLE( "dy" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ), + IM_INPUT_INT( "w" ), + IM_INPUT_INT( "h" ) +}; + +/* Call im_affine via arg vector. + */ +static int +affine_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double b = *((double *) argv[3]); + double c = *((double *) argv[4]); + double d = *((double *) argv[5]); + double dx = *((double *) argv[6]); + double dy = *((double *) argv[7]); + int x = *((int *) argv[8]); + int y = *((int *) argv[9]); + int w = *((int *) argv[10]); + int h = *((int *) argv[11]); + + return( im_affine( argv[0], argv[1], a, b, c, d, dx, dy, x, y, w, h ) ); +} + +/* Description of im_affine. + */ +static im_function affine_desc = { + "im_affine", /* Name */ + "affine transform", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + affine_vec, /* Dispatch function */ + IM_NUMBER( affine_args ), /* Size of arg list */ + affine_args /* Arg list */ +}; + +/* similarity args + */ +static im_arg_desc similarity_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_DOUBLE( "dx" ), + IM_INPUT_DOUBLE( "dy" ) +}; + +/* Call im_similarity via arg vector. + */ +static int +similarity_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double b = *((double *) argv[3]); + double dx = *((double *) argv[4]); + double dy = *((double *) argv[5]); + + return( im_similarity( argv[0], argv[1], a, b, dx, dy ) ); +} + +/* Description of im_similarity. + */ +static im_function similarity_desc = { + "im_similarity", /* Name */ + "similarity transformation", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + similarity_vec, /* Dispatch function */ + IM_NUMBER( similarity_args ), /* Size of arg list */ + similarity_args /* Arg list */ +}; + +/* match_linear args + */ +static im_arg_desc match_linear_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xref1" ), + IM_INPUT_INT( "yref1" ), + IM_INPUT_INT( "xsec1" ), + IM_INPUT_INT( "ysec1" ), + IM_INPUT_INT( "xref2" ), + IM_INPUT_INT( "yref2" ), + IM_INPUT_INT( "xsec2" ), + IM_INPUT_INT( "ysec2" ) +}; + +/* Call im_match_linear via arg vector. + */ +static int +match_linear_vec( im_object *argv ) +{ + int xref1 = *((int *) argv[3]); + int yref1 = *((int *) argv[4]); + int xsec1 = *((int *) argv[5]); + int ysec1 = *((int *) argv[6]); + int xref2 = *((int *) argv[7]); + int yref2 = *((int *) argv[8]); + int xsec2 = *((int *) argv[9]); + int ysec2 = *((int *) argv[10]); + + return( im_match_linear( argv[0], argv[1], argv[2], + xref1, yref1, xsec1, ysec1, + xref2, yref2, xsec2, ysec2 ) ); +} + +/* Description of im_match_linear. + */ +static im_function match_linear_desc = { + "im_match_linear", /* Name */ + "resample ref so that tie-points match", + IM_FN_PIO, /* Flags */ + match_linear_vec, /* Dispatch function */ + IM_NUMBER( match_linear_args ), /* Size of arg list */ + match_linear_args /* Arg list */ +}; + +/* match_linear_search args + */ +static im_arg_desc match_linear_search_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xref1" ), + IM_INPUT_INT( "yref1" ), + IM_INPUT_INT( "xsec1" ), + IM_INPUT_INT( "ysec1" ), + IM_INPUT_INT( "xref2" ), + IM_INPUT_INT( "yref2" ), + IM_INPUT_INT( "xsec2" ), + IM_INPUT_INT( "ysec2" ), + IM_INPUT_INT( "hwindowsize" ), + IM_INPUT_INT( "hsearchsize" ) +}; + +/* Call im_match_linear_search via arg vector. + */ +static int +match_linear_search_vec( im_object *argv ) +{ + int xref1 = *((int *) argv[3]); + int yref1 = *((int *) argv[4]); + int xsec1 = *((int *) argv[5]); + int ysec1 = *((int *) argv[6]); + int xref2 = *((int *) argv[7]); + int yref2 = *((int *) argv[8]); + int xsec2 = *((int *) argv[9]); + int ysec2 = *((int *) argv[10]); + int hwin = *((int *) argv[11]); + int hsrch = *((int *) argv[12]); + + return( im_match_linear_search( argv[0], argv[1], argv[2], + xref1, yref1, xsec1, ysec1, + xref2, yref2, xsec2, ysec2, + hwin, hsrch ) ); +} + +/* Description of im_match_linear_search. + */ +static im_function match_linear_search_desc = { + "im_match_linear_search", /* Name */ + "search sec, then resample so that tie-points match", + IM_FN_PIO, /* Flags */ + match_linear_search_vec, /* Dispatch function */ + IM_NUMBER( match_linear_search_args ),/* Size of arg list */ + match_linear_search_args /* Arg list */ +}; + +/* correl args + */ +static im_arg_desc correl_args[] = { + IM_INPUT_IMAGE( "ref" ), + IM_INPUT_IMAGE( "sec" ), + IM_INPUT_INT( "xref" ), + IM_INPUT_INT( "yref" ), + IM_INPUT_INT( "xsec" ), + IM_INPUT_INT( "ysec" ), + IM_INPUT_INT( "hwindowsize" ), + IM_INPUT_INT( "hsearchsize" ), + IM_OUTPUT_DOUBLE( "correlation" ), + IM_OUTPUT_INT( "x" ), + IM_OUTPUT_INT( "y" ) +}; + +/* Call im_correl via arg vector. + */ +static int +correl_vec( im_object *argv ) +{ + int xref = *((int *) argv[2]); + int yref = *((int *) argv[3]); + int xsec = *((int *) argv[4]); + int ysec = *((int *) argv[5]); + int cor = *((int *) argv[6]); + int area = *((int *) argv[7]); + int *x = (int *) argv[8]; + int *y = (int *) argv[9]; + double *correlation = (double *) argv[10]; + + return( im_correl( argv[0], argv[1], + xref, yref, xsec, ysec, cor, area, correlation, x, y ) ); +} + +/* Description of im_correl. + */ +static im_function correl_desc = { + "im_correl", /* Name */ + "search area around sec for match for area around ref", + IM_FN_PIO, /* Flags */ + correl_vec, /* Dispatch function */ + IM_NUMBER( correl_args ), /* Size of arg list */ + correl_args /* Arg list */ +}; + +/* similarity_area args + */ +static im_arg_desc similarity_area_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_DOUBLE( "dx" ), + IM_INPUT_DOUBLE( "dy" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ), + IM_INPUT_INT( "w" ), + IM_INPUT_INT( "h" ) +}; + +/* Call im_similarity_area via arg vector. + */ +static int +similarity_area_vec( im_object *argv ) +{ + double a = *((double *) argv[2]); + double b = *((double *) argv[3]); + double dx = *((double *) argv[4]); + double dy = *((double *) argv[5]); + int x = *((int *) argv[6]); + int y = *((int *) argv[7]); + int w = *((int *) argv[8]); + int h = *((int *) argv[9]); + + return( im_similarity_area( argv[0], argv[1], a, b, dx, dy, + x, y, w, h ) ); +} + +/* Description of im_similarity_area. + */ +static im_function similarity_area_desc = { + "im_similarity_area", /* Name */ + "output area xywh of similarity transformation", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + similarity_area_vec, /* Dispatch function */ + IM_NUMBER( similarity_area_args ), /* Size of arg list */ + similarity_area_args /* Arg list */ +}; + +/* global_balance args + */ +static im_arg_desc global_balance_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "gamma" ) +}; + +/* Call im_global_balance via arg vector. + */ +static int +global_balance_vec( im_object *argv ) +{ + double gamma = *((double *) argv[2]); + + return( im_global_balance( argv[0], argv[1], gamma ) ); +} + +/* Description of im_global_balance. + */ +static im_function global_balance_desc = { + "im_global_balance", /* Name */ + "automatically rebuild mosaic with balancing", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + global_balance_vec, /* Dispatch function */ + IM_NUMBER( global_balance_args ), /* Size of arg list */ + global_balance_args /* Arg list */ +}; + +/* Call im_global_balancef via arg vector. + */ +static int +global_balancef_vec( im_object *argv ) +{ + double gamma = *((double *) argv[2]); + + return( im_global_balancef( argv[0], argv[1], gamma ) ); +} + +/* Description of im_global_balancef. + */ +static im_function global_balancef_desc = { + "im_global_balancef", /* Name */ + "automatically rebuild mosaic with balancing, float output", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + global_balancef_vec, /* Dispatch function */ + IM_NUMBER( global_balance_args ), /* Size of arg list */ + global_balance_args /* Arg list */ +}; + +/* remosaic args + */ +static im_arg_desc remosaic_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "old_str" ), + IM_INPUT_STRING( "new_str" ) +}; + +/* Call im_remosaic via arg vector. + */ +static int +remosaic_vec( im_object *argv ) +{ + return( im_remosaic( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_remosaic. + */ +static im_function remosaic_desc = { + "im_remosaic", /* Name */ + "automatically rebuild mosaic with new files", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + remosaic_vec, /* Dispatch function */ + IM_NUMBER( remosaic_args ),/* Size of arg list */ + remosaic_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *mos_list[] = { + &affine_desc, + &correl_desc, + &find_lroverlap_desc, + &find_tboverlap_desc, + &global_balance_desc, + &global_balancef_desc, + &lrmerge_desc, + &lrmerge1_desc, + &lrmosaic_desc, + &lrmosaic1_desc, + &match_linear_desc, + &match_linear_search_desc, + &remosaic_desc, + &similarity_area_desc, + &similarity_desc, + &tbmerge_desc, + &tbmerge1_desc, + &tbmosaic_desc, + &tbmosaic1_desc +}; + +/* Package of functions. + */ +im_package im__mosaicing = { + "mosaicing", + IM_NUMBER( mos_list ), + mos_list +}; diff --git a/libsrc/mosaicing/similarity.c b/libsrc/mosaicing/similarity.c new file mode 100644 index 00000000..5db8883f --- /dev/null +++ b/libsrc/mosaicing/similarity.c @@ -0,0 +1,159 @@ +/* @(#) im_similarity_area() ... similarity transform. Like affine, but + * @(#) rotate/scale only. + * @(#) + * @(#) int im_similarity_area(in, out, a, b, dx, dy, w, h, x, y) + * @(#) IMAGE *in, *out; + * @(#) double a, b, dx, dy; + * @(#) int w, h, x, y; + * @(#) + * @(#) Forward transform + * @(#) X = a * x - b * y + dx + * @(#) Y = b * x + a * y + dy + * @(#) + * @(#) x and y are the coordinates in input image. + * @(#) X and Y are the coordinates in output image. + * @(#) (0,0) is the upper left corner. + * @(#) + * @(#) a and b DO NOT correspond to scale and angle directly + * @(#) + * @(#) scale = sqrt(a*a + b*b) , angle = arctan(a/b) + * @(#) + * @(#) im_similarity_area() returns 0 on success and -1 on error + * @(#) + * + * 3/3/98 JC + * - redone as wrapper for im_affine(), compatibility only + * 8/4/04 + * - transform rounding redone as part of the new im_embed thing + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "merge.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Call point from VIPS. + */ +int +im_similarity_area( IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy, + int ox, int oy, int ow, int oh ) +{ + Transformation trn; + + trn.oarea.left = ox; + trn.oarea.top = oy; + trn.oarea.width = ow; + trn.oarea.height = oh; + trn.iarea.left = 0; + trn.iarea.top = 0; + trn.iarea.width = in->Xsize; + trn.iarea.height = in->Ysize; + trn.a = a; + trn.b = -b; + trn.c = b; + trn.d = a; + trn.dx = dx; + trn.dy = dy; + + return( im__affine( in, out, &trn ) ); +} + +/* Set output area of trn so that it just holds all of our input pels. + */ +void +im__transform_set_area( Transformation *trn ) +{ + double xA, xB, xC, xD; + double yA, yB, yC, yD; + int xmin, xmax, ymin, ymax; + + im__transform_forward( trn, + trn->iarea.left, trn->iarea.top, + &xA, &yA ); + im__transform_forward( trn, + IM_RECT_RIGHT( &trn->iarea ) - 1, trn->iarea.top, + &xB, &yB ); + im__transform_forward( trn, + trn->iarea.left, IM_RECT_BOTTOM( &trn->iarea ) - 1, + &xC, &yC ); + im__transform_forward( trn, + IM_RECT_RIGHT( &trn->iarea ) - 1, + IM_RECT_BOTTOM( &trn->iarea ) - 1, + &xD, &yD ); + + xmin = IM_MIN( xA, IM_MIN( xB, IM_MIN( xC, xD ) ) ); + ymin = IM_MIN( yA, IM_MIN( yB, IM_MIN( yC, yD ) ) ); + xmax = IM_MAX( xA, IM_MAX( xB, IM_MAX( xC, xD ) ) ); + ymax = IM_MAX( yA, IM_MAX( yB, IM_MAX( yC, yD ) ) ); + + trn->oarea.left = xmin; + trn->oarea.top = ymin; + trn->oarea.width = xmax - xmin + 1; + trn->oarea.height = ymax - ymin + 1; +} + +/* Output the rect holding all our input PELs. + */ +int +im_similarity( IMAGE *in, IMAGE *out, + double a, double b, double dx, double dy ) +{ + Transformation trn; + + trn.iarea.left = 0; + trn.iarea.top = 0; + trn.iarea.width = in->Xsize; + trn.iarea.height = in->Ysize; + trn.a = a; + trn.b = -b; + trn.c = b; + trn.d = a; + trn.dx = dx; + trn.dy = dy; + im__transform_set_area( &trn ); + + if( im__affine( in, out, &trn ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/other/Makefile.am b/libsrc/other/Makefile.am new file mode 100644 index 00000000..e7759c25 --- /dev/null +++ b/libsrc/other/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libother.la + +libother_la_SOURCES = \ + cooc_funcs.c \ + glds_funcs.c \ + im_benchmark.c \ + im_dif_std.c \ + im_eye.c \ + im_grey.c \ + im_make_xy.c \ + im_meanstd.c \ + im_simcontr.c \ + im_sines.c \ + im_spatres.c \ + im_zone.c \ + other_dispatch.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/other/cooc_funcs.c b/libsrc/other/cooc_funcs.c new file mode 100644 index 00000000..5f3d9f41 --- /dev/null +++ b/libsrc/other/cooc_funcs.c @@ -0,0 +1,472 @@ +/* @(#) Calculates the cooccurrence matrix of an image and some of its + * @(#) features. The 256x256 cooccurrence matrix of im is held by m + * @(#) There should be enough margin around the box so the (dx,dy) can + * @(#) access neighbouring pixels outside the box + * @(#) + * @(#) Usage: + * @(#) int im_cooc_matrix(im, m, xpos, ypos, xsize, ysize, dx, dy, sym_flag) + * @(#) IMAGE *im, *m; + * @(#) int xpos, ypos, xsize, ysize; location of the box within im + * @(#) int dx, dy; displacements + * @(#) int sym_flag; + * @(#) + * @(#) int im_cooc_asm(m, asmoment) + * @(#) IMAGE *m; + * @(#) double *asmoment; + * @(#) + * @(#) int im_cooc_contrast(m, contrast) + * @(#) IMAGE *m; + * @(#) double *contrast; + * @(#) + * @(#) int im_cooc_correlation(m, correlation) + * @(#) IMAGE *m; + * @(#) double *correlation; + * @(#) + * @(#) int im_cooc_entropy(m, entropy) + * @(#) IMAGE *m; + * @(#) double *entropy; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * + * Copyright: N. Dessipris 1991 + * Written on: 2/12/1991 + * Updated on: 2/12/1991 + * 22/7/93 JC + * - extern decls removed + * - im_incheck() calls added + * 28/5/97 JC + * - protos added :( + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static +int im_cooc_sym(im, m, xpos, ypos, xsize, ysize, dx, dy) +IMAGE *im, *m; +int xpos, ypos, xsize, ysize; /* location of the box within im */ +int dx, dy; /* displacements */ +{ + PEL *input, *cpinput; + int *buf, *pnt, *cpnt; + double *line, *cpline; + int x, y; + int offset; + int bufofst; + int tempA, tempB; + int norm; + + if (im_iocheck(im, m) == -1) + { im_errormsg("im_cooc_sym: im_iocheck failed"); return(-1);} + if ((im->Bands != 1)||(im->Bbits != IM_BBITS_BYTE)||(im->BandFmt != IM_BANDFMT_UCHAR)) + { + im_errormsg("im_cooc_sym: Unable to accept input"); + return(-1); + } + if ( (xpos + xsize + dx > im->Xsize)|| (ypos + ysize + dy > im->Ysize) ) + { im_errormsg("im_cooc_sym: wrong args"); return(-1); } + if (im_cp_desc(m, im) == -1) + { im_errormsg("im_cooc_sym: im_cp_desc failed"); return(-1);} + m->Xsize = 256; + m->Ysize = 256; + m->Bbits = IM_BBITS_DOUBLE; + m->BandFmt = IM_BANDFMT_DOUBLE; + m->Type = IM_TYPE_B_W; + if (im_setupout(m) == -1) + {im_errormsg("im_cooc_sym: im_setupout failed"); return(-1);} +/* malloc space to keep the read values */ + buf = (int *)calloc( (unsigned)m->Xsize*m->Ysize, sizeof(int) ); + line = (double *)calloc( (unsigned)m->Xsize * m->Bands, sizeof(double)); + if ( (buf == NULL) || (line == NULL) ) + { im_errormsg("im_cooc_sym: calloc failed"); return(-1); } + input = (PEL*)im->data; + input += ( ypos * im->Xsize + xpos ); + offset = dy * im->Xsize + dx; + for ( y=0; yXsize; + for ( x=0; xXsize * tempB; + (*(buf + bufofst))++; + bufofst = tempB + m->Xsize * tempA; + (*(buf + bufofst))++; + cpinput++; + } + } + + norm = xsize * ysize * 2; + pnt = buf; + for ( y=0; yYsize; y++ ) + { + cpnt = pnt; + pnt += m->Xsize; + cpline = line; + for (x=0; xXsize; x++) + *cpline++ = (double)(*cpnt++)/(double)norm; + if (im_writeline( y, m, (PEL *) line ) == -1) + { + im_errormsg("im_cooc_sym: unable to im_writeline"); + return(-1); + } + } + free((char*)buf); + free((char*)line); + return(0); +} + +static +int im_cooc_ord(im, m, xpos, ypos, xsize, ysize, dx, dy) +IMAGE *im, *m; +int xpos, ypos, xsize, ysize; /* location of the box within im */ +int dx, dy; /* displacements */ +{ + PEL *input, *cpinput; + int *buf, *pnt, *cpnt; + double *line, *cpline; + int x, y; + int offset; + int bufofst; + int tempA, tempB; + int norm; + + if (im_iocheck(im, m) == -1) + { im_errormsg("im_cooc_ord: im_iocheck failed"); return(-1);} + if ((im->Bands != 1)||(im->Bbits != IM_BBITS_BYTE)||(im->BandFmt != IM_BANDFMT_UCHAR)) + { + im_errormsg("im_cooc_ord: Unable to accept input"); + return(-1); + } + if ( (xpos + xsize + dx > im->Xsize)|| (ypos + ysize + dy > im->Ysize) ) + { im_errormsg("im_cooc_ord: wrong args"); return(-1); } + if (im_cp_desc(m, im) == -1) + { im_errormsg("im_cooc_ord: im_cp_desc failed"); return(-1);} + m->Xsize = 256; + m->Ysize = 256; + m->Bbits = IM_BBITS_DOUBLE; + m->BandFmt = IM_BANDFMT_DOUBLE; + if (im_setupout(m) == -1) + {im_errormsg("im_cooc_ord: im_setupout failed"); return(-1);} +/* malloc space to keep the read values */ + buf = (int *)calloc( (unsigned)m->Xsize*m->Ysize, sizeof(int) ); + line = (double *)calloc( (unsigned)m->Xsize * m->Bands, sizeof(double)); + if ( (buf == NULL) || (line == NULL) ) + { im_errormsg("im_cooc_ord: calloc failed"); return(-1); } + input = (PEL*)im->data; + input += ( ypos * im->Xsize + xpos ); + offset = dy * im->Xsize + dx; + for ( y=0; yXsize; + for ( x=0; xXsize * tempB; + (*(buf + bufofst))++; + cpinput++; + } + } + + norm = xsize * ysize; + pnt = buf; + for ( y=0; yYsize; y++ ) + { + cpnt = pnt; + pnt += m->Xsize; + cpline = line; + for (x=0; xXsize; x++) + *cpline++ = (double)(*cpnt++)/(double)norm; + if (im_writeline( y, m, (PEL *) line ) == -1) + { + im_errormsg("im_cooc_ord: unable to im_writeline"); + return(-1); + } + } + free((char*)buf); + free((char*)line); + return(0); +} + +/* Keep the coocurrence matrix as a 256x256x1 double image */ + +int +im_cooc_matrix( IMAGE *im, IMAGE *m, + int xp, int yp, int xs, int ys, int dx, int dy, int flag ) +{ + if (flag == 0) + return( im_cooc_ord(im, m, xp, yp, xs, ys, dx, dy) ); + else if (flag == 1) /* symmetrical cooc */ + return( im_cooc_sym(im, m, xp, yp, xs, ys, dx, dy) ); + else + { im_errormsg("im_cooc_matrix: wrong flag!"); return(-1); } +} + +/* Calculate contrast, asmoment, entropy and correlation + */ +int +im_cooc_asm( IMAGE *m, double *asmoment ) +{ + double temp, tmpasm, *pnt; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 256 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { + im_errormsg("im_cooc_asm: unable to accept input"); + return(-1); + } + tmpasm = 0.0; + pnt = (double*)m->data; + for(i=0; iXsize * m->Ysize; i++) + { + temp = *pnt++; + tmpasm += temp * temp; + } + *asmoment = tmpasm; + return(0); +} + +int +im_cooc_contrast( IMAGE *m, double *contrast ) +{ + double dtemp, tmpcon, *pnt, *cpnt; + int x, y; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 256 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { + im_errormsg("im_cooc_contrast: unable to accept input"); + return(-1); + } + tmpcon = 0.0; + pnt = (double*)m->data; + for(y=0; yYsize; y++) + { + cpnt = pnt; + pnt += m->Xsize; + for(x=0; xXsize; x++) + { + dtemp = (double)( (y-x)*(y-x) ); + tmpcon += dtemp * (*cpnt); + cpnt++; + } + } + + *contrast = tmpcon; + return(0); +} + +static void +stats(buffer, size, pmean, pstd) +double *buffer; /* buffer contains the frequency distributions f[i] */ +int size; /* Note that sum(f[i]) = 1.0 and that the */ + /* cooccurence matrix is symmetrical */ +double *pmean, *pstd; +{ + double mean, std; + register int i; + double sumf; /* calculates the sum of f[i] */ + double temp; /* temporary variable */ + double *pbuffer; + double sumf2; /* calculates the sum of f[i]^2 */ + double correction; /* calulates the correction term for the variance */ + double variance; /* = (sumf2 - correction)/n, n=sum(f[i]) = 1 */ + + mean = 0.0; std = 0.0; + sumf = 0.0; sumf2 = 0.0; + pbuffer = buffer; + for (i=0; iXsize != 256 || m->Ysize != 256 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { + im_errormsg("im_cooc_correlation: unable to accept input"); + return(-1); + } + row = (double*)calloc( (unsigned)m->Ysize, sizeof(double)); + col = (double*)calloc( (unsigned)m->Xsize, sizeof(double)); + if ( row == NULL || col == NULL ) + { + im_errormsg("im_cooc_correlation: unable to calloc"); + return(-1); + } + pbuf = (double*)m->data; + for(j=0; jYsize; j++) + { + cpbuf = pbuf; + pbuf += m->Xsize; + sum=0.0; + for(i=0; iXsize; i++) + sum += *cpbuf++; + *(row+j) = sum; + } + + pbuf = (double*)m->data; + for(j=0; jYsize; j++) + { + cpbuf = pbuf; + pbuf++; + sum=0.0; + for(i=0; iXsize; i++) + { + sum += *cpbuf; + cpbuf += m->Xsize; + } + *(col+j) = sum; + } + + stats(row, m->Ysize, &mrow, &stdrow); + + stats(col, m->Ysize ,&mcol, &stdcol); +#ifdef DEBUG + fprintf(stderr, "rows: mean=%f std=%f\ncols: mean=%f std=%f\n", +mrow, stdrow, mcol, stdcol); +#endif + tmpcor = 0.0; + pbuf = (double*)m->data; + for(j=0; jYsize; j++) + { + cpbuf = pbuf; + pbuf += m->Xsize; + for(i=0; iXsize; i++) + { + dtemp = *cpbuf; + tmpcor += ( ((double)i)*((double)j)*dtemp); + cpbuf++; + } + } +#ifdef DEBUG + fprintf(stderr, "tmpcor=%f\n", tmpcor); +#endif + if ( (stdcol==0.0)||(stdrow==0) ) + { + im_errormsg("im_cooc_correlation: zero std"); + return(-1); + } + tmpcor = (tmpcor-(mcol*mrow))/(stdcol*stdrow); + *correlation = tmpcor; + free((char*)row); free((char*)col); + return(0); +} + +int +im_cooc_entropy( IMAGE *m, double *entropy ) +{ + double *pbuf, *pbufstart; + double *cpbuf; + register int i,j; + double tmpent, dtemp; + double val; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 256 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { + im_errormsg("im_cooc_entropy: unable to accept input"); + return(-1); + } + pbufstart = (double*)m->data; + + tmpent = 0.0; + pbuf = pbufstart; + for(j=0; jYsize; j++) + { + cpbuf = pbuf; + pbuf += m->Xsize; + for(i=0; iXsize; i++) + { + if(*cpbuf != 0) + { + dtemp = *cpbuf; + tmpent += (dtemp*log10(dtemp)); + } + cpbuf++; + } + } + val = tmpent*(-1); + +#ifdef DEBUG + fprintf(stderr,"ENT=%f\nwhich is %f bits\n", val, val/log10(2.0) ); +#endif + *entropy = (val/log10(2.0)); + return(0); +} diff --git a/libsrc/other/glds_funcs.c b/libsrc/other/glds_funcs.c new file mode 100644 index 00000000..51b82efd --- /dev/null +++ b/libsrc/other/glds_funcs.c @@ -0,0 +1,248 @@ +/* @(#) Calculates the spatial grey level differnce + * @(#) matrix of an image and some of its + * @(#) features. The 256x1 difference matrix of im is held by m + * @(#) There should be enough margin around the box so the (dx,dy) can + * @(#) access neighbouring pixels outside the box + * @(#) + * @(#) Usage: + * @(#) int im_glds_matrix(im, m, xpos, ypos, xsize, ysize, dx, dy) + * @(#) IMAGE *im, *m; + * @(#) int xpos, ypos, xsize, ysize; location of the box within im + * @(#) int dx, dy; displacements + * @(#) + * @(#) int im_glds_asm(m, asmoment) + * @(#) IMAGE *m; + * @(#) double *asmoment; + * @(#) + * @(#) int im_glds_contrast(m, contrast) + * @(#) IMAGE *m; + * @(#) double *contrast; + * @(#) + * @(#) int im_glds_entropy(m, entropy) + * @(#) IMAGE *m; + * @(#) double *entropy; + * @(#) + * @(#) int im_glds_mean(m, mean) + * @(#) IMAGE *m; + * @(#) double *mean; + * @(#) + * @(#) All functions return 0 on success and -1 on error + * + * Copyright: N. Dessipris, 1991 + * Written on: 2/12/1991 + * Modified on: + * 22/7/93 JC + * - im_incheck() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Keep the greylevel difference matrix as a 256x1 double image */ + +int +im_glds_matrix( IMAGE *im, IMAGE *m, + int xpos, int ypos, int xsize, int ysize, int dx, int dy ) +{ + PEL *in, *cpin; + int *b, *pb; + double *l, *pl; + int x, y; + int ofs; + int tmp; + int norm; + + if (im_iocheck(im, m) == -1) + { im_errormsg("im_glds_matrix: im_iocheck failed"); return(-1);} + + if ((im->Bands != 1)||(im->Bbits != IM_BBITS_BYTE)||(im->BandFmt != IM_BANDFMT_UCHAR)) + { im_errormsg("im_glds_matrix: Wrong input"); return(-1); } + + if ( (xpos + xsize + dx > im->Xsize)|| (ypos + ysize + dy > im->Ysize) ) + { im_errormsg("im_glds_matrix: wrong args"); return(-1); } + + if (im_cp_desc(m, im) == -1) + { im_errormsg("im_glds_matrix: im_cp_desc failed"); return(-1);} + m->Xsize = 256; m->Ysize = 1; + m->Bbits = IM_BBITS_DOUBLE; m->BandFmt = IM_BANDFMT_DOUBLE; + m->Type = IM_TYPE_B_W; + + if (im_setupout(m) == -1) + { im_errormsg("im_glds_matrix: im_setupout failed");return(-1);} + + b = (int *)calloc( (unsigned)m->Xsize, sizeof(int) ); + l = (double *)calloc( (unsigned)m->Xsize, sizeof(double)); + if ( (b == NULL) || (l == NULL) ) + { im_errormsg("im_glds_matrix: calloc failed"); return(-1); } + + in = (PEL*)im->data; + in += ( ypos * im->Xsize + xpos ); + ofs = dy * im->Xsize + dx; + for ( y=0; yXsize; + for ( x=0; xXsize; x++) + *pl++ = ((double)(*pb++))/(double)norm; + if (im_writeline( 0, m, (PEL *) l ) == -1) + {im_errormsg("im_glds_matrix: im_writeline failed");return(-1);} + + free((char*)b); free((char*)l); + return(0); +} + +/* @(#) Calculates the asmoment of the sglds matrix held by m + */ +int +im_glds_asm( IMAGE *m, double *asmoment ) +{ + double temp, tmpasm, *in; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 1 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + {im_errormsg("im_glds_asm: unable to accept input");return(-1);} + tmpasm = 0.0; + in = (double*)m->data; + for(i=0; iXsize; i++) + { + temp = *in++; + tmpasm += (temp*temp); + } + *asmoment = tmpasm; + return(0); +} + +/* @(#) Calculates the contrast of the coocurence matrix passed in buffer + */ +int +im_glds_contrast( IMAGE *m, double *contrast ) +{ + double tmpcon, *in; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 1 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { im_errormsg("im_glds_contrast: wrong input"); return(-1); } + tmpcon = 0.0; + in = (double*)m->data; + for(i=0; iXsize; i++) + { + tmpcon += ( ((double)i)*((double)i)*(*in) ); + in++; + } + *contrast = tmpcon; + return(0); +} + +/* @(#) Calculates the entropy of the glds vector passed in buffer + * @(#) Function returns the entropy based on log base 2. + */ +int +im_glds_entropy( IMAGE *m, double *entropy ) +{ + double tmpent, dtemp, *in; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 1 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { im_errormsg("im_glds_entropy: wrong input"); return(-1); } + tmpent = 0.0; + in = (double*)m->data; + for(i=0; iXsize; i++) + { + if(*in != 0) + { + dtemp = *in; + tmpent += (dtemp*log10(dtemp)); + } + in++; + } + *entropy = ((-1)*tmpent/log10(2.0)); + return(0); +} + +/* @(#) Calculates the mean of the sglds matrix passed in m + */ +int +im_glds_mean( IMAGE *m, double *mean ) +{ + double tmpmean, *in; + int i; + + if( im_incheck( m ) ) + return( -1 ); + + if (m->Xsize != 256 || m->Ysize != 1 || + m->Bands != 1 || m->BandFmt != IM_BANDFMT_DOUBLE) + { im_errormsg("im_glds_mean: wrong input"); return(-1); } + tmpmean = 0.0; + in = (double*)m->data; + for(i=0; iXsize; i++) + { + tmpmean += ( ((double)i)*(*in) ); + in++; + } + tmpmean = tmpmean/((double)m->Xsize); + *mean = tmpmean; + return(0); +} diff --git a/libsrc/other/im_benchmark.c b/libsrc/other/im_benchmark.c new file mode 100644 index 00000000..bd337013 --- /dev/null +++ b/libsrc/other/im_benchmark.c @@ -0,0 +1,287 @@ +/* @(#) Do a complicated compound operation for benchmarking the threading + * @(#) system. Input should be a large LABQ image, output is a large sRGB + * @(#) image. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_benchmark( IMAGE *in, IMAGE *out ) + * @(#) + * @(#) Returns 0 on sucess and -1 on error. + * @(#) + * + * 6/10/06 + * - hacked in + * 27/11/06 + * - added im_benchmarkn() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* + +VIPS SMP benchmark +------------------ + +This is adapted from the system used to generate images for POD: + + http://cima.ng-london.org.uk/~john/POD + +Images from a 10k by 10k studio digital camera are colour processed, resized, +cropped and sharpened. + +The original POD script was written in nip (see below). This operation is a +reimplementation in vanilla C to make it easier to run (and less fragile!). + +This thing was originally processing images off a remote server over a 100mbit +network. No attempt was made to make it quick (there was no point): you +could make it a lot faster very easily. + +------ benchmark in nip2 ----------- +#!/home/john/vips/bin/nip2 -s + +// get command-line arguments + +image_path = argv?1; +crop_id = parse_pint argv?2; +crop_left = parse_pint argv?3; +crop_top = parse_pint argv?4; +crop_width = parse_pint argv?5; +crop_height = parse_pint argv?6; +width = parse_pint argv?7; +height = parse_pint argv?8; +sharp = parse_pint argv?9; + +// scale down by this much to undo photographic's relativisation +darken = Vector [1.18, 1, 1]; + +// fudge factor in XYZ to get a match under NGC lights on uv-durable paper +white_point_adjust = Vector [1.06, 1, 1.01]; + +// brighten by this in XYZ to get relative colorimetry +brighten = 1.5; + +// blacks down by this much in LAB +blacks_down = Vector [-2, 0, 0]; + +// sharpen params for 400, 300, 200 and 150 dpi +// just change the size of the area we search +sharpen_params_table = [ + [ 11, 2.5, 40, 20, 0.5, 1.5 ], + [ 7, 2.5, 40, 20, 0.5, 1.5 ], + [ 5, 2.5, 40, 20, 0.5, 1.5 ], + [ 3, 2.5, 40, 20, 0.5, 1.5 ] +]; + +// convert D65 XYZ to D50 XYZ +D652D50 = recomb D652D50_direct; + +stage_crop in + = extract_area crop_left crop_top crop_width crop_height in, + crop_id != 0 + = in; + +// fit within a width / height +stage_shrink image + = image, factor > 1; // never upscale + = resize factor factor Interpolate.BILINEAR image +{ + hfactor = width / get_width image; + vfactor = height / get_height image; + factor = min_pair hfactor vfactor; +} + +// unphotoize, go to xyz, convert to D50, adjust white point, back to lab +stage_colour in + = if in?0 > 99 then Vector [100, 0, 0] else in''' +{ + // back to absolute + in' = in / darken; + + xyz = colour_transform_to Image_type.XYZ in'; + + xyz' = D652D50 xyz * white_point_adjust * brighten; + + in'' = colour_transform_to Image_type.LAB xyz'; + + // shadows down + in''' = in'' + blacks_down; +} + +stage_sharp in + = (sharpen params?0 params?1 params?2 params?3 params?4 params?5 @ + colour_transform_to Image_type.LABQ) in +{ + params = sharpen_params_table?sharp; +} + +// This was: +// +// stage_srgb in +// = (icc_export 8 "$VIPSHOME/share/nip2/data/sRGB.icm" 1 @ +// colour_transform_to Image_type.LABQ) in; +// +// but that uses lcms which is single-threaded. So for this benchmark, we use +// VIPS's own ->sRGB converter, which is less accurate but does thread. +stage_srgb in + = colour_transform_to Image_type.sRGB in; + +main = (get_image @ stage_srgb @ + stage_sharp @ stage_colour @ stage_shrink @ stage_crop @ + colour_transform_to Image_type.LAB @ Image_file) image_path; +------ benchmark in nip2 ----------- + + */ + +/* The main part of the benchmark ... transform labq to labq. Chain several of + * these together to get a CPU-bound operation. + */ +static int +benchmark( IMAGE *in, IMAGE *out ) +{ + IMAGE *t[18]; + double one[3] = { 1.0, 1.0, 1.0 }; + double zero[3] = { 0.0, 0.0, 0.0 }; + double darken[3] = { 1.0 / 1.18, 1.0, 1.0 }; + double whitepoint[3] = { 1.06, 1.0, 1.01 }; + double shadow[3] = { -2, 0, 0 }; + double white[3] = { 100, 0, 0 }; + DOUBLEMASK *d652d50 = im_create_dmaskv( "d652d50", 3, 3, + 1.13529, -0.0604663, -0.0606321, + 0.0975399, 0.935024, -0.0256156, + -0.0336428, 0.0414702, 0.994135 ); + + im_add_close_callback( out, + (im_callback_fn) im_free_dmask, d652d50, NULL ); + + return( + /* Set of descriptors for this operation. + */ + im_open_local_array( out, t, 18, "im_benchmark", "p" ) || + + /* Unpack to float. + */ + im_LabQ2Lab( in, t[0] ) || + + /* Crop 100 pixels off all edges. + */ + im_extract_area( t[0], t[1], + 100, 100, t[0]->Xsize - 200, t[0]->Ysize - 200 ) || + + /* Shrink by 10%, bilinear interp. + */ + im_affine( t[1], t[2], + 0.9, 0, 0, 0.9, 0, 0, + 0, 0, t[1]->Xsize * 0.9, t[1]->Ysize * 0.9 ) || + + /* Find L ~= 100 areas (white surround). + */ + im_extract_band( t[2], t[3], 0 ) || + im_moreconst( t[3], t[4], 99 ) || + + /* Adjust white point and shadows. + */ + im_lintra_vec( 3, darken, t[2], zero, t[5] ) || + im_Lab2XYZ( t[5], t[6] ) || + im_recomb( t[6], t[7], d652d50 ) || + im_lintra_vec( 3, whitepoint, t[7], zero, t[8] ) || + im_lintra( 1.5, t[8], 0.0, t[9] ) || + im_XYZ2Lab( t[9], t[10] ) || + im_lintra_vec( 3, one, t[10], shadow, t[11] ) || + + /* Make a solid white image. + */ + im_black( t[12], t[4]->Xsize, t[4]->Ysize, 3 ) || + im_lintra_vec( 3, zero, t[12], white, t[13] ) || + + /* Reattach border. + */ + im_ifthenelse( t[4], t[13], t[11], t[14] ) || + + /* Sharpen. + */ + im_Lab2LabQ( t[14], t[15] ) || + im_sharpen( t[15], out, 11, 2.5, 40, 20, 0.5, 1.5 ) + ); +} + +/* Chain n benchmarks together to get a CPU-bound operation. + */ +int +im_benchmarkn( IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *t[2]; + + if( n == 0 ) + /* To sRGB. + */ + return( im_LabQ2disp( in, out, im_col_displays( 7 ) ) ); + else + return( im_open_local_array( out, t, 2, "benchmarkn", "p" ) || + + benchmark( in, t[0] ) || + + /* Expand back to the original size again ... + * benchmark does a 200 pixel crop plus a 10% shrink, + * so if we chain many of them together the image gets + * too small. + */ + im_affine( t[0], t[1], + (double) in->Xsize / t[0]->Xsize, 0, 0, + (double) in->Ysize / t[0]->Ysize, + 0, 0, + 0, 0, in->Xsize, in->Ysize ) || + + im_benchmarkn( t[1], out, n - 1 ) ); +} + +int +im_benchmark2( IMAGE *in, double *out ) +{ + IMAGE *t; + + return( + !(t = im_open_local( in, "benchmarkn", "p" )) || + im_benchmarkn( in, t, 1 ) || + im_avg( t, out ) + ); +} + diff --git a/libsrc/other/im_dif_std.c b/libsrc/other/im_dif_std.c new file mode 100644 index 00000000..60b1e57c --- /dev/null +++ b/libsrc/other/im_dif_std.c @@ -0,0 +1,101 @@ +/* @(#) Program to calculate the stdev of the differnce image + * @(#) at a given displacement vector + * + * Written : 25/11/1987 + * Author : N. Dessipris + * Updated : 2/12/1991 + * 22/7/93 JC + * - im_incheck() added + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int im_dif_std(im, xpos, ypos, xsize, ysize, dx, dy, pmean, pstd) +IMAGE *im; +int xpos, ypos, xsize, ysize; /* location of the box within im */ +int dx, dy; /* displacements */ +double *pmean, *pstd; +{ + PEL *input, *cpinput; + double m, s; + int *buf, *pbuf; + int x, y; + int ofst, bufsize; + + + if( im_incheck( im ) ) + return( -1 ); + + if ((im->Bands != 1)||(im->Bbits != IM_BBITS_BYTE)||(im->BandFmt != IM_BANDFMT_UCHAR)) + {im_errormsg("im_dif_std: Unable to accept input"); return(-1);} + if ( (xpos + xsize + dx > im->Xsize)|| (ypos + ysize + dy > im->Ysize) ) + { im_errormsg("im_dif_std: wrong args"); return(-1); } + + bufsize = xsize * ysize; + buf = (int *)calloc( (unsigned)bufsize, sizeof(int) ); + if ( buf == NULL ) + { im_errormsg("im_dif_std: calloc failed"); return(-1); } + input = (PEL*)im->data; + input += ( ypos * im->Xsize + xpos ); + ofst = dy * im->Xsize + dx; + pbuf = buf; + for ( y=0; yXsize; + for ( x=0; x +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_feye( IMAGE *image, const int xsize, const int ysize, const double factor ) +{ + int x, y; + double constant; + double *lut; + float *line; + + /* Check input args + */ + if( im_outcheck( image ) ) + return( -1 ); + if( factor > 1.0 || factor <= 0.0 ) { + im_errormsg( "im_feye: factor should be in [1,0)" ); + return( -1 ); + } + + /* Set image descriptor + */ + im_initdesc( image, xsize, ysize, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + if( im_setupout( image ) ) + return( -1 ); + + /* Allocate space for line buffer. + */ + if( !(line = IM_ARRAY( image, xsize, float )) ) + return( -1 ); + + /* Make a lut for easy calculations. + */ + if( !(lut = IM_ARRAY( image, image->Xsize, double )) ) + return( -1 ); + constant = factor * IM_PI/(2*(xsize - 1)); + for( x = 0; x < image->Xsize; x++ ) + lut[x] = cos( constant*x*x ) / ((ysize - 1)*(ysize - 1)); + + /* Make image. + */ + for( y = 0; y < image->Ysize; y++ ) { + for( x = 0; x < image->Xsize; x++ ) + line[x] = y*y*lut[x]; + if( im_writeline( y, image, (PEL *) line ) ) + return( -1 ); + } + + return( 0 ); +} + +/* As above, but make a IM_BANDFMT_UCHAR image. + */ +int +im_eye( IMAGE *image, const int xsize, const int ysize, const double factor ) +{ + IMAGE *t1 = im_open_local( image, "im_eye:1", "p" ); + IMAGE *t2 = im_open_local( image, "im_eye:2", "p" ); + + if( !t1 ) + return( -1 ); + + /* Change range to [0,255]. + */ + if( im_feye( t1, xsize, ysize, factor ) || + im_lintra( 127.5, t1, 127.5, t2 ) || + im_clip( t2, image ) ) + return( -1 ); + + return( 0 ); +} + diff --git a/libsrc/other/im_grey.c b/libsrc/other/im_grey.c new file mode 100644 index 00000000..0dfec7f5 --- /dev/null +++ b/libsrc/other/im_grey.c @@ -0,0 +1,146 @@ +/* @(#) Creates a IM_BANDFMT_FLOAT grey level image of a specified size. Range is + * @(#) always [0,1]. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_fgrey( image, xsize, ysize ) + * @(#) IMAGE *image; + * @(#) int xsize, ysize; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 02/02/1990 + * Modified on: + * 22/7/93 JC + * - im_outcheck() added + * - externs removed + * 8/2/95 JC + * - ANSIfied + * - im_fgrey() made from im_grey() + * 31/8/95 JC + * - now makes [0,1], rather than [0,256) + * - im_grey() now defined in terms of im_fgrey() + * 2/3/98 JC + * - partialed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Generate function. + */ +static int +fgrey_gen( REGION *or ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int iwm = or->im->Xsize - 1; + + int x, y; + + for( y = 0; y < r->height; y++ ) { + float *q = (float *) IM_REGION_ADDR( or, le, y + to ); + + for( x = 0; x < r->width; x++ ) + q[x] = (float) (x + le) / iwm; + } + + return( 0 ); +} + +/* Make a one band grey ramp image. + */ +int +im_fgrey( IMAGE *out, const int xsize, const int ysize ) +{ + /* Check args. + */ + if( xsize <=0 || ysize <= 0 ) { + im_errormsg( "im_fgrey: bad size" ); + return( -1 ); + } + if( im_poutcheck( out ) ) + return( -1 ); + + /* Set image. + */ + im_initdesc( out, xsize, ysize, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( im_generate( out, NULL, fgrey_gen, NULL, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* As above, but make a IM_BANDFMT_UCHAR [0-255] image. + */ +int +im_grey( IMAGE *image, const int xsize, const int ysize ) +{ + IMAGE *t1 = im_open_local( image, "im_grey:1", "p" ); + IMAGE *t2 = im_open_local( image, "im_grey:2", "p" ); + + if( !t1 || !t2 ) + return( -1 ); + + /* Change range to [0,255]. + */ + if( im_fgrey( t1, xsize, ysize ) || + im_lintra( 255.0, t1, 0.0, t2 ) || + im_clip( t2, image ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/other/im_make_xy.c b/libsrc/other/im_make_xy.c new file mode 100644 index 00000000..1e601d14 --- /dev/null +++ b/libsrc/other/im_make_xy.c @@ -0,0 +1,116 @@ +/* @(#) Creates a 2 band IM_BANDFMT_UINT image of a specified size. Each pixel + * @(#) has band 0 == x coordinate, band 1 == y coordinate. + * @(#) + * @(#) Usage: + * @(#) + * @(#) int + * @(#) im_make_xy( image, xsize, ysize ) + * @(#) IMAGE *image; + * @(#) int xsize, ysize; + * @(#) + * @(#) Returns 0 on success and -1 on error + * @(#) + * + * 21/4/04 + * - from im_grey + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Generate function. + */ +static int +make_xy_gen( REGION *or ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int ri = IM_RECT_RIGHT( r ); + int bo = IM_RECT_BOTTOM( r ); + + int x, y; + + for( y = to; y < bo; y++ ) { + unsigned int *q = (unsigned int *) IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + q[0] = x; + q[1] = y; + q += 2; + } + } + + return( 0 ); +} + +/* Make a two band image ... band 0 is x coordinates, band 1 is y + * coordinates. + */ +int +im_make_xy( IMAGE *out, const int xsize, const int ysize ) +{ + /* Check args. + */ + if( xsize <=0 || ysize <= 0 ) { + im_errormsg( "im_make_xy: bad size" ); + return( -1 ); + } + if( im_poutcheck( out ) ) + return( -1 ); + + /* Set image. + */ + im_initdesc( out, xsize, ysize, 2, IM_BBITS_INT, IM_BANDFMT_UINT, + IM_CODING_NONE, IM_TYPE_MULTIBAND, 1.0, 1.0, 0, 0 ); + + /* Set hints - ANY is ok with us. + */ + if( im_demand_hint( out, IM_ANY, NULL ) ) + return( -1 ); + + /* Generate image. + */ + if( im_generate( out, NULL, make_xy_gen, NULL, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/other/im_meanstd.c b/libsrc/other/im_meanstd.c new file mode 100644 index 00000000..2edbfeff --- /dev/null +++ b/libsrc/other/im_meanstd.c @@ -0,0 +1,136 @@ +/* @(#) Calculates the mean and the standard deviation (std) of + * @(#) an int or double buffer of size size. + * @(#) + * @(#) Usage: + * @(#) int im__mean_std_double_buffer(buffer, size, pmean, pstd) + * @(#) double *buffer; + * @(#) int size; + * @(#) double *pmean, *pstd; + * @(#) + * @(#) int im__mean_std_int_buffer(buffer, size, pmean, pstd) + * @(#) int *buffer; + * @(#) int size; + * @(#) double *pmean, *pstd; + * @(#) + * @(#) Both functions return 0 on success and -1 on error + * + * Copyright: N. Dessipris 1991 + * Written on: 2/12/1991 + * Updated on: 2/12/1991 + * 22/7/93 JC + * - externs removed + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im__mean_std_double_buffer( double *buffer, int size, + double *pmean, double *pstd ) +{ + double mean, std; + register int i; + double sumf; + double temp; + double *pbuffer; + double sumf2; + double correction; /* calulates the correction term for the variance */ + double variance; /* = (sumf2 - correction)/n */ + + if (size <= 0) { + im_errormsg("im_mean_std_double_buffer: wrong args"); + return(-1); + } + mean = 0.0; std = 0.0; + sumf = 0.0; sumf2 = 0.0; + pbuffer = buffer; + for (i=0; i +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_simcontr( IMAGE *image, int xs, int ys ) +{ + int x, y; + unsigned char *line1, *line2, *cpline; + + +/* Check input args */ + if( im_outcheck( image ) ) + return( -1 ); + +/* Set now image properly */ + im_initdesc(image, xs, ys, 1, IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + +/* Set up image checking whether the output is a buffer or a file */ + if (im_setupout( image ) == -1 ) + { im_errormsg("im_simcontr: im_setupout failed"); return(-1); } +/* Create data */ + line1 = (unsigned char *)calloc((unsigned)xs, sizeof(char)); + line2 = (unsigned char *)calloc((unsigned)xs, sizeof(char)); + if ( (line1 == NULL) || (line2 == NULL) ) + { im_errormsg("im_simcontr: calloc failed"); return(-1); } + + cpline = line1; + for (x=0; x +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_sines( IMAGE *image, int xsize, int ysize, double horfreq, double verfreq ) +{ + int x, y; + float *line, *cpline; + int size; + double cons, factor; + double theta_rad, costheta, sintheta, ysintheta; + +/* Check input args */ + if( im_outcheck( image ) ) + return( -1 ); + if ( xsize <= 0 || ysize <= 0 ) + { im_errormsg("im_sines: wrong sizes"); return(-1); } + +/* Set now image properly */ + im_initdesc(image, xsize, ysize, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0); + +/* Set up image checking whether the output is a buffer or a file */ + if (im_setupout( image ) == -1 ) + { im_errormsg("im_sines: im_setupout failed"); return(-1); } +/* Create data */ + size = image->Xsize; + if ( (line=(float *)calloc((unsigned)size, sizeof(float))) == NULL ) + { im_errormsg("im_sines: calloc failed"); return(-1); } + +/* make angle in rad */ + if (horfreq == 0) + theta_rad = IM_PI/2.0; + else + theta_rad = atan(verfreq/horfreq); + costheta = cos(theta_rad); sintheta = sin(theta_rad); + factor = sqrt ((double)(horfreq*horfreq + verfreq*verfreq)); + cons = factor * IM_PI * 2.0/(double)image->Xsize; +/* There is a bug (rounding error ?) for horfreq=0, + *so do this calculation independantly */ + if ( horfreq != 0 ) + { + for (y=0; yYsize; y++) + { + ysintheta = y * sintheta; + cpline = line; + for (x=0; xXsize; x++) + *cpline++ = + (float)(cos(cons*(x*costheta-ysintheta))); + if ( im_writeline( y, image, (PEL *)line ) == -1 ) + { + im_errormsg("im_sines: im_writeline failed"); + free ( (char *)line ); + return( -1 ); + } + } + } + else + { + for (y=0; yYsize; y++) + { + cpline = line; + ysintheta = cos (- cons * y * sintheta); + for (x=0; xXsize; x++) + *cpline++ = (float)ysintheta; + if ( im_writeline( y, image, (PEL *)line ) == -1 ) + { + im_errormsg("im_sines: im_writeline failed"); + free ( (char *)line ); + return( -1 ); + } + } + } + free ( (char *)line ); + return(0); +} diff --git a/libsrc/other/im_spatres.c b/libsrc/other/im_spatres.c new file mode 100644 index 00000000..f8535f15 --- /dev/null +++ b/libsrc/other/im_spatres.c @@ -0,0 +1,146 @@ +/* @(#) Function which changes the spatial resolution of an image according to + * @(#) step + * @(#) + * @(#) int im_spatres(in, out, step) + * @(#) IMAGE *in, *out; + * @(#) int step; + * @(#) Returns either 0 (sucess) or -1 (fail) + * @(#) + * @(#) Picture can have any number of channels (max 64). + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 08/11/1989. + * Modified on: 19/01/1990. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_spatres( IMAGE *in, IMAGE *out, int step ) +{ + int x, y; /* horizontal and vertical direction */ + int z; /* 0 <= z < channel */ + int i, j; + int rounding, step2, sum; + unsigned char *values; + unsigned char *input, *cpinput, *cp2input, *line, *cpline, *pnt, *cpnt; + int os; + +/* Check args */ + if ( step < 1 ) + {im_errormsg("im_spatres: Invalid step %d\n", step);return(-1);} + + if ( (in->Xsize/step == 0)||(in->Ysize/step == 0) ) + {im_errormsg("im_spatres: Invalid step %d\n", step);return(-1);} + + if (im_iocheck(in, out) == -1) + { im_errormsg("im_spatres: im_iocheck failed"); return(-1); } + + if((in->Coding != IM_CODING_NONE)||(in->Bbits != 8)||(in->BandFmt !=IM_BANDFMT_UCHAR)) + { im_errormsg("im_spatres: wrong input"); return(-1); } + +/* Prepare output */ + if (im_cp_desc(out, in) == -1) + { im_errormsg("im_spatres: im_cp_desc failed"); return(-1); } + out->Xsize = in->Xsize - in->Xsize%step; + out->Ysize = in->Ysize - in->Ysize%step; + + if( im_setupout(out) == -1) + { im_errormsg("im_spatres: im_setupout failed"); return(-1); } + + /* Malloc buffer for one 'line' of input data */ + os = in->Xsize * in->Bands; + line = (unsigned char *)calloc((unsigned)os, sizeof(char)); + /* Malloc space for values */ + values = (unsigned char *)calloc((unsigned)out->Bands, sizeof(char)); + if ( line == NULL || values == NULL ) + { im_errormsg("im_spatres: calloc failed"); return(-1); } + + step2 = step * step; + rounding = step2/2; + input = (unsigned char *)in->data; + for ( y = 0; y < out->Ysize; y += step ) + { + cpinput = input; + input += os * step; + /* do the x loop out->Xsize / step times */ + cpline = line; + for (x = 0; x < out->Xsize; x += step) + { + cp2input = cpinput; + cpinput += step * out->Bands; /* ??? */ + for ( z = 0; z < out->Bands; z++ ) + { + pnt = cp2input + z; + sum = 0; + for ( j = 0; j < step; j++ ) + { + cpnt = pnt; + pnt += os; + for ( i = 0; i < step; i++ ) + { + sum += (int)*cpnt; + cpnt += out->Bands; + } + } + *(values + z) = (PEL)((sum + rounding)/step2); + } + /* for this x, write step*bands data */ + for ( j = 0; j < step; j++ ) + for ( z = 0; z < out->Bands; z++ ) + *cpline++ = *(values + z); + } + /* line is now ready. Write now step lines */ + for (j = 0; j < step; j++) + if ( im_writeline ( y+j, out, (PEL *)line ) == -1 ) + { + im_errormsg("im_spatres: im_writeline failed"); + free ( (char *)line ); free ( (char *)values ); + return( -1 ); + } + } /* end of the for (..y..) loop */ + + free ( (char *)line ); free ( (char *)values ); + return(0); +} diff --git a/libsrc/other/im_zone.c b/libsrc/other/im_zone.c new file mode 100644 index 00000000..59477461 --- /dev/null +++ b/libsrc/other/im_zone.c @@ -0,0 +1,127 @@ +/* @(#) square zone plate of size + * @(#) The center of the zone plate is at (xpos/2, ypos/2) + * @(#) + * @(#) Usage: + * @(#) + * @(#) int im_zone( image, size ) + * @(#) IMAGE *image; + * @(#) int size; + * @(#) + * @(#) int im_fzone( image, size ) + * @(#) IMAGE *image; + * @(#) int size; + * @(#) + * @(#) Returns 0 on sucess and -1 on error + * @(#) + * N. Dessipris 01/02/1991 + * + * 22/7/93 JC + * - externs removed + * - im_outcheck() added + * 30/8/95 JC + * - modernized + * - memory leaks fixed + * - split into im_zone() and im_fzone() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_fzone( IMAGE *image, int size ) +{ + int x, y; + int i, j; + + float *buf; + const int size2 = size/2; + + /* Check args. + */ + if( im_outcheck( image ) ) + return( -1 ); + if( size <= 0 || (size % 2) != 0 ) { + im_errormsg( "im_zone: size must be even and positive" ); + return( -1 ); + } + + /* Set up output image. + */ + im_initdesc( image, size, size, 1, IM_BBITS_FLOAT, IM_BANDFMT_FLOAT, + IM_CODING_NONE, IM_TYPE_B_W, 1.0, 1.0, 0, 0 ); + if( im_setupout( image ) ) + return( -1 ); + + /* Create output buffer. + */ + if( !(buf = IM_ARRAY( image, size, float )) ) + return( -1 ); + + /* Make zone plate. + */ + for( y = 0, j = -size2; j < size2; j++, y++ ) { + for( x = 0, i = -size2; i < size2; i++, x++ ) + buf[x] = cos( (IM_PI/size) * (i*i + j*j) ); + if( im_writeline( y, image, (PEL *) buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* As above, but make a IM_BANDFMT_UCHAR image. + */ +int +im_zone( IMAGE *im, int size ) +{ + IMAGE *t1 = im_open_local( im, "im_zone:1", "p" ); + IMAGE *t2 = im_open_local( im, "im_zone:2", "p" ); + + if( !t1 || !t2 ) + return( -1 ); + + if( im_fzone( t1, size ) || + im_lintra( 127.5, t1, 127.5, t2 ) || + im_clip( t2, im ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/other/man3/Makefile.am b/libsrc/other/man3/Makefile.am new file mode 100644 index 00000000..728322f5 --- /dev/null +++ b/libsrc/other/man3/Makefile.am @@ -0,0 +1,30 @@ +man_MANS = \ + im_benchmark.3 \ + im_cooc_asm.3 \ + im_cooc_contrast.3 \ + im_cooc_correlation.3 \ + im_cooc_entropy.3 \ + im_cooc_matrix.3 \ + im_dif_std.3 \ + im_eye.3 \ + im_feye.3 \ + im_fgrey.3 \ + im_fzone.3 \ + im_glds_asm.3 \ + im_glds_contrast.3 \ + im_glds_entropy.3 \ + im_glds_matrix.3 \ + im_glds_mean.3 \ + im_grey.3 \ + im_make_xy.3 \ + im_mean_std_double_buffer.3 \ + im_mean_std_int_buffer.3 \ + im_sines.3 \ + im_quantim.3 \ + im_quantlut.3 \ + im_simcontr.3 \ + im_spatres.3 \ + im_zone.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/other/man3/im_benchmark.3 b/libsrc/other/man3/im_benchmark.3 new file mode 100644 index 00000000..34707c8e --- /dev/null +++ b/libsrc/other/man3/im_benchmark.3 @@ -0,0 +1,16 @@ +.TH IM_BENCHMARK 3 "6 Oct 2006" +.SH NAME +im_benchmark \- do something complicated +.SH SYNOPSIS +.B #include + +int im_benchmark( IMAGE *in, IMAGE *out ) + +.SH DESCRIPTION +.B im_benchmark(3) +performs a complicated operation (based on a real example of VIPS usage) on a +LABPACK image. It is useful for benchmarking the VIPS threading system. It +should speed up (mostly) linearly with more CPUs. + +.SH RETURNED VALUES +The function returns 0 on success and -1 on error. diff --git a/libsrc/other/man3/im_cooc_asm.3 b/libsrc/other/man3/im_cooc_asm.3 new file mode 100644 index 00000000..536836b7 --- /dev/null +++ b/libsrc/other/man3/im_cooc_asm.3 @@ -0,0 +1 @@ +.so man3/im_cooc_matrix.3 diff --git a/libsrc/other/man3/im_cooc_contrast.3 b/libsrc/other/man3/im_cooc_contrast.3 new file mode 100644 index 00000000..536836b7 --- /dev/null +++ b/libsrc/other/man3/im_cooc_contrast.3 @@ -0,0 +1 @@ +.so man3/im_cooc_matrix.3 diff --git a/libsrc/other/man3/im_cooc_correlation.3 b/libsrc/other/man3/im_cooc_correlation.3 new file mode 100644 index 00000000..536836b7 --- /dev/null +++ b/libsrc/other/man3/im_cooc_correlation.3 @@ -0,0 +1 @@ +.so man3/im_cooc_matrix.3 diff --git a/libsrc/other/man3/im_cooc_entropy.3 b/libsrc/other/man3/im_cooc_entropy.3 new file mode 100644 index 00000000..536836b7 --- /dev/null +++ b/libsrc/other/man3/im_cooc_entropy.3 @@ -0,0 +1 @@ +.so man3/im_cooc_matrix.3 diff --git a/libsrc/other/man3/im_cooc_matrix.3 b/libsrc/other/man3/im_cooc_matrix.3 new file mode 100644 index 00000000..12f70df6 --- /dev/null +++ b/libsrc/other/man3/im_cooc_matrix.3 @@ -0,0 +1,88 @@ +.TH IM_COOC_MATRIX 3 "2 Dec 1991" +.SH NAME +im_cooc_matrix, im_cooc_asm, im_cooc_contrast, im_cooc_correlation, +im_cooc_entropy \- calculate the co-occurrence matrix and features on it +.SH SYNOPSIS +.B #include + +int im_cooc_matrix(im, m, xp, yp, xs, ys, dx, dy, sym) +.br +.B IMAGE *im, *m; +.br +.B int xp, yp, xs, ys; +.br +.B int dx, dy; +.br +.B int sym; + +.br +.B int im_cooc_asm(m, asmoment) +.br +.B IMAGE *m; +.br +.B double *asmoment; + +.br +.B int im_cooc_contrast(m, contrast) +.br +.B IMAGE *m; +.br +.B double *contrast; + +.br +.B int im_cooc_correlation(m, correlation) +.br +.B IMAGE *m; +.br +.B double *correlation; + +.br +.B int im_cooc_entropy(m, entropy) +.br +.B IMAGE *m; +.br +.B double *entropy; + +.SH DESCRIPTION +.B im_cooc_matrix() +creates a 256 by 256 one channel co-occurrence matrix of the box determined by +the parameters (xp, yp; xs, ys) within the image pointed by the IMAGE +descriptor im. The matrix is written onto the IMAGE descriptor m. The +displacement vector is determined by (dx, dy). The user must ensure that +there is enough border pixels around the box within im dictated by the +displacement vector (dx,dy) or else the program fails. All entries of the +co-occurrence matrix are double normalised to the number of pairs involved. +This function is a direct implementation of the paper: Haralick R. M., +Shanmugan K. and Dinstein I., 'Textural features for image classification', +IEEE Transactions on Systems, Man, and Cybernetics, Vol. SMC-3, No 6, Nov. +1973, pp 610-621. Input im should be one band unsigned char image. + +If flag sym is 1, the created co-occurrence matrix is symmetric that is +dispacement vectors (dx, dy), (-dx, -dy) create exactly the same matrix. If +sym is 0, the created co-occurrence matrix is not symmetric that is +dispacement vectors (dx, dy), (-dx, -dy) create different matrices. + +.B im_cooc_asm() +calculates the angular second moment of the co-occurrence matrix held by m. +The result is returned into the location pointed by asmoment. + +.B im_cooc_contrast() +calculates the contrast of the co-occurrence matrix held by m. +The result is returned into the location pointed by contrast. + +.B im_cooc_correlation() +calculates the correlation of the co-occurrence matrix held by m. +The result is returned into the location pointed by correlation. + +.B im_cooc_entropy() +calculates the entropy of the co-occurrence matrix held by m. +The result is returned into the location pointed by entropy. +.SH RETURNED VALUES +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_glds_matrix(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 2/12/1991 diff --git a/libsrc/other/man3/im_dif_std.3 b/libsrc/other/man3/im_dif_std.3 new file mode 100644 index 00000000..8d505818 --- /dev/null +++ b/libsrc/other/man3/im_dif_std.3 @@ -0,0 +1,40 @@ +.TH IM_DIF_STD 3 "10 May 1991" +.SH NAME +im_dif_std \- calculate the mean and the standard deviation of the difference image for a given displacement vector +.SH SYNOPSIS +.B #include + +int im_dif_std(im, xp, yp, xs, ys, dx, dy, mean, std) +.br +.B IMAGE *im; +.br +.B int xp, yp, xs, ys; +.br +.B int dx, dy; +.br +.B double *mean, *std; + +.SH DESCRIPTION +.B im_dif_std() +calculates the mean and the standard deviation of the difference image +created by the displacement vector (dx,dy) on the box (xp,yp;xs,ys) defined +on the image pointed by im. More specifically the difference image is +of an image given the dispacement vector (dx, dy) is defined as follows: +For each point of the original image, the start of the vector (dx,dy) is +set on that point. The difference image at this point is defined as the +difference of the values pointed by the vector (end value - start value). +The function returns the mean and the standard deviation of the +difference images measured on the area (xp,yp;xs,ys) of im. + +Input im should be one band unsigned char image and it should +have been set by a call to im_mmapin(3) or im_setbuf(3). + +.SH RETURNED VALUES(3) +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_cooc_matrix(3), im_glds_matrix(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_eye.3 b/libsrc/other/man3/im_eye.3 new file mode 100644 index 00000000..33216d07 --- /dev/null +++ b/libsrc/other/man3/im_eye.3 @@ -0,0 +1,47 @@ +.TH IM_EYE 3 "10 May 1991" +.SH NAME +im_eye, im_feye \- creates a pattern which shows the spatial response of the human visual system +.SH SYNOPSIS +.B #include + +.B int im_eye(image, xsize, ysize, factor) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; +.br +.B double factor; + +.B int im_feye(image, xsize, ysize, factor) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; +.br +.B double factor; + +.SH DESCRIPTION +.B im_feye() +creates an one band float image with pels in [-1,+1] of a pattern which has +the following properties: +.br +- the spatial frequency increases from left to right. +.br +- the grey level intensity decreases from top to bottom. +.br +The sizes of the produced image are determined by the entered arguments +xsize and ysize. The variable factor which should be between 0 and 1, +determines the number of maximum spatial frequencies present along the +horizontal direction. + +.B im_eye() +behaves exactly as im_feye(), but scales the output to [0,255]. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_grey(3), im_zone(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_feye.3 b/libsrc/other/man3/im_feye.3 new file mode 100644 index 00000000..0f1cc8cb --- /dev/null +++ b/libsrc/other/man3/im_feye.3 @@ -0,0 +1 @@ +.so man3/im_eye.3 diff --git a/libsrc/other/man3/im_fgrey.3 b/libsrc/other/man3/im_fgrey.3 new file mode 100644 index 00000000..af1bec37 --- /dev/null +++ b/libsrc/other/man3/im_fgrey.3 @@ -0,0 +1 @@ +.so man3/im_grey.3 diff --git a/libsrc/other/man3/im_fzone.3 b/libsrc/other/man3/im_fzone.3 new file mode 100644 index 00000000..4d100b95 --- /dev/null +++ b/libsrc/other/man3/im_fzone.3 @@ -0,0 +1 @@ +.so man3/im_zone.3 diff --git a/libsrc/other/man3/im_glds_asm.3 b/libsrc/other/man3/im_glds_asm.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/other/man3/im_glds_asm.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/other/man3/im_glds_contrast.3 b/libsrc/other/man3/im_glds_contrast.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/other/man3/im_glds_contrast.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/other/man3/im_glds_entropy.3 b/libsrc/other/man3/im_glds_entropy.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/other/man3/im_glds_entropy.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/other/man3/im_glds_matrix.3 b/libsrc/other/man3/im_glds_matrix.3 new file mode 100644 index 00000000..2f3aaf4f --- /dev/null +++ b/libsrc/other/man3/im_glds_matrix.3 @@ -0,0 +1,78 @@ +.TH IM_SGLDS_MATRIX 3 "10 May 1991" +.SH NAME +im_glds_matrix, im_glds_asm, im_glds_contrast, im_glds_mean, im_glds_entropy \- calculate the spatial grey level difference matrix and features on it +.SH SYNOPSIS +.B #include + +int im_glds_matrix(im, m, xp, yp, xs, ys, dx, dy) +.br +.B IMAGE *im, *m; +.br +.B int xp, yp, xs, ys; +.br +.B int dx, dy; + +.B int im_glds_asm(m, asmoment) +.br +.B IMAGE *m; +.br +.B double *asmoment; + +.B int im_glds_contrast(m, contrast) +.br +.B IMAGE *m; +.br +.B double *contrast; + +.B int im_glds_entropy(m, entropy) +.br +.B IMAGE *m; +.br +.B double *entropy; + +.B int im_glds_mean(m, mean) +.br +.B IMAGE *m; +.br +.B double *mean; + +.SH DESCRIPTION +.B im_glds_matrix() +creates a 256 by 1 one channel spatial grey level difference matrix (sglds) of +the box determined by the parameters (xp, yp; xs, ys) within the image pointed +by the IMAGE descriptor im. The matrix is written onto the IMAGE descriptor +m. The displacement vector is determined by (dx, dy). The user must ensure +that there is enough border pixels around the box within im dictated by the +displacement vector (dx,dy) or else the program fails. im should be one-band +unsigned char. + +All entries of the sgld matrix are double normalised to the number of pairs +involved. This function is a direct implementation of the paper: Haralick R. +M., Shanmugan K. and Dinstein I., 'Textural features for image +classification', IEEE Transactions on Systems, Man, and Cybernetics, Vol. +SMC-3, No 6, Nov. 1973, pp 610-621. + +.B im_glds_asm() +calculates the angular second moment of the co-occurrence matrix held by m. +The result is returned into the location pointed by asmoment. + +.B im_glds_contrast() +calculates the contrast of the sglds matrix held by m. The result is returned +into the location pointed by contrast. + +.B im_glds_entropy() +calculates the entropy of the sglds matrix held by m. The result is returned +into the location pointed by entropy. + +.B im_glds_mean() +calculates the mean of the sglds matrix held by m. The result is returned +into the location pointed by mean. +.SH RETURNED VALUES +All functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_cooc_matrix(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_glds_mean.3 b/libsrc/other/man3/im_glds_mean.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/other/man3/im_glds_mean.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/other/man3/im_grey.3 b/libsrc/other/man3/im_grey.3 new file mode 100644 index 00000000..1c9af7c5 --- /dev/null +++ b/libsrc/other/man3/im_grey.3 @@ -0,0 +1,46 @@ +.TH IM_GREY 3 "10 May 1991" +.SH NAME +im_grey, im_fgrey, im_make_xy \- creates a grey scale +.SH SYNOPSIS +.B #include + +.B int im_grey( image, xsize, ysize ) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; + +.B int im_fgrey( image, xsize, ysize ) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; + +.B int im_make_xy( image, xsize, ysize ) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; + +.SH DESCRIPTION +.B im_grey(3) +creates a one-band FMTUCHAR grey scale image of sizes xsize by +ysize. The intensity varies from 0 (left) to 255 (right). + +.B im_fgrey(3) +works as +.B im_grey(3), +except that the output image is FMTFLOAT, +allowing pixel values from 0 (left) to 1.0 (right). + +.B im_make_xy(3) +makes a two-band FMTUINT image where each pixel in band 0 has a value equal to +the x coordinate, and each pixel in band 1 has a value equal to the y +coordinate. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_black(3) +.SH COPYRIGHT +Birkbeck College and the National Gallery, 1995 diff --git a/libsrc/other/man3/im_make_xy.3 b/libsrc/other/man3/im_make_xy.3 new file mode 100644 index 00000000..af1bec37 --- /dev/null +++ b/libsrc/other/man3/im_make_xy.3 @@ -0,0 +1 @@ +.so man3/im_grey.3 diff --git a/libsrc/other/man3/im_mean_std_double_buffer.3 b/libsrc/other/man3/im_mean_std_double_buffer.3 new file mode 100644 index 00000000..8467ffdc --- /dev/null +++ b/libsrc/other/man3/im_mean_std_double_buffer.3 @@ -0,0 +1 @@ +.so man3/im_mean_std_int_buffer.3 diff --git a/libsrc/other/man3/im_mean_std_int_buffer.3 b/libsrc/other/man3/im_mean_std_int_buffer.3 new file mode 100644 index 00000000..bf2898ad --- /dev/null +++ b/libsrc/other/man3/im_mean_std_int_buffer.3 @@ -0,0 +1,38 @@ +.TH IM_MEAN_STD 3 "10 May 1991" +.SH NAME +im_mean_std_double_buffer, im_mean_std_int_buffer \- calculates the mean and the std of data held by a by an int or double buffer +.SH SYNOPSIS +.B #include + +int im_mean_std_int_buffer(buf, size, mean, std) +.br +.B int *buf; +.br +.B int size; +.br +.B double *mean, *std; + +int im_mean_std_double_buffer(buf, size, mean, std) +.br +.B double *buf; +.br +.B int size; +.br +.B double *mean, *std; + +.SH DESCRIPTION +im_mean_std_int_buffer() and im_mean_std_double_buffer() +calculate the mean and the standard deviation (std) of data held by the +integer or double buffer buf. The buffer has size elements. The results are +returned to the locations pointed by mean and std. +It is the responsibility of the calling function to ensure that there is +enough data in the buffer. +.SH RETURN VALUE +Both functions returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_avg(3), im_stats(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_quantim.3 b/libsrc/other/man3/im_quantim.3 new file mode 100644 index 00000000..c25c5138 --- /dev/null +++ b/libsrc/other/man3/im_quantim.3 @@ -0,0 +1,53 @@ +.TH IM_QUANTIM 3 "10 May 1991" +.SH NAME +im_quantim, im_quantlut, im_spatres \- quantise an image or a lut +.SH SYNOPSIS +.B #include + +.B int im_quantim(in, out, no_of_bits) +.br +.B IMAGE *in, *out; +.br +.B int no_of_bits; + +.B int im_quantlut(lut, no_of_bits) +.br +.B IMAGE *lut; +.br +.B int no_of_bits; + +.B int im_spatres(in, out, step) +.br +.B IMAGE *in, *out; +.br +.B int step; +.SH DESCRIPTION +.B im_quantim() +quantises the image file held by in using no_of_bits and writes the result to +out. The no_of_bits should be between 1 and 7 inclusive. Input can have any +number of bands. The function expects as input a valid unsigned char image. + +These functions are here for compatibility only. You should use the boolean +operations im_andconst(3), im_orconst(3), im_shrink(3), im_zoom(3) and +im_lowpass(3). + +.B im_quantlut() +creates an one band unsigned char +lookup table which is used by im_quantim(3). +The no_of_bits should be between 1 and 7 inclusive. + +.B im_spatres() +reduces the spatial resolution of in by averaging step*step pixels +and replicating the result in out. The function can be used in +order to show the effect of reducing the spatial resolution of a given image +and the importance of post-filtering before displaying. +Input can have any number of bands. +.SH RETURN VALUE +All functions returns 0 on success and -1 on error. +.SH SEE ALSO +im_andconst(3), im_orconst(3), im_shrink(3), im_zoom(3) im_lowpass(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_quantlut.3 b/libsrc/other/man3/im_quantlut.3 new file mode 100644 index 00000000..81b965d9 --- /dev/null +++ b/libsrc/other/man3/im_quantlut.3 @@ -0,0 +1 @@ +.so man3/im_quantim.3 diff --git a/libsrc/other/man3/im_simcontr.3 b/libsrc/other/man3/im_simcontr.3 new file mode 100644 index 00000000..2871fa8f --- /dev/null +++ b/libsrc/other/man3/im_simcontr.3 @@ -0,0 +1,27 @@ +.TH IM_SIMCONTR 3 "10 May 1991" +.SH NAME +im_simcontr \- shows the effect of simultaneous contrast +.SH SYNOPSIS +.B #include + +.B int im_simcontr( image, xsize, ysize ) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; +.SH DESCRIPTION +.B im_simcontr() +creates an unsigned char one band grey scale image of sizes xsize by ysize. +The created pattern consists of two neighbouring squares one dark (left +square) and one light (right square). The left +one containes a smaller light square and the right +one containes a darker square. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_grey(3) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_sines.3 b/libsrc/other/man3/im_sines.3 new file mode 100644 index 00000000..4ee48707 --- /dev/null +++ b/libsrc/other/man3/im_sines.3 @@ -0,0 +1,29 @@ +.TH IM_SINES 3 "10 May 1991" +.SH NAME +im_sines \- creates a spatial sine wave form +.SH SYNOPSIS +.B #include + +.B int im_sines(image, xsize, ysize, horfreq, verfreq) +.br +.B IMAGE *image; +.br +.B int xsize, ysize; +.br +.B double horfreq, verfreq; +.SH DESCRIPTION +.B im_sines() +creates a float one band image of the a sine waveform in two dimensions. +The sizes of the created image are xsize by ysize. The number of +horizontal and vertical spatial frequencies are determined by the variables +horfreq and verfreq respectively. The function is the base for creating +displayable sine waves and square waves in two dimensions. +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE\ ALSO +im_grey(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/man3/im_spatres.3 b/libsrc/other/man3/im_spatres.3 new file mode 100644 index 00000000..81b965d9 --- /dev/null +++ b/libsrc/other/man3/im_spatres.3 @@ -0,0 +1 @@ +.so man3/im_quantim.3 diff --git a/libsrc/other/man3/im_zone.3 b/libsrc/other/man3/im_zone.3 new file mode 100644 index 00000000..14b1ec19 --- /dev/null +++ b/libsrc/other/man3/im_zone.3 @@ -0,0 +1,38 @@ +.TH IM_ZONE 3 "10 May 1991" +.SH NAME +im_zone, im_fzone \- creates a zone plate +.SH SYNOPSIS +.B #include + +.B int im_zone(image, size) +.br +.B IMAGE *image; +.br +.B int size; + +.B int im_fzone(image, size) +.br +.B IMAGE *image; +.br +.B int size; + +.SH DESCRIPTION + +.B im_fzone() +creates a float one band image of a zone plate of size size by size. Pels are +in the range [-1,+1]. The zone plate has spatial frequencies increasing from +0 the center (at size/2, size/2) up to infinity at the edges. size must be +positive and even. + +.B im_zone() +behaves exactly as im_fzone(), but writes a FMTUCHAR image scaled to the range +0-255. + +.SH RETURN VALUE +The function returns 0 on success and -1 on error. +.SH SEE ALSO +im_grey(3), im_fgrey(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 10/05/1991 diff --git a/libsrc/other/other_dispatch.c b/libsrc/other/other_dispatch.c new file mode 100644 index 00000000..33bf5f5b --- /dev/null +++ b/libsrc/other/other_dispatch.c @@ -0,0 +1,332 @@ +/* Function dispatch tables for other. + * + * J. Cupitt, 8/2/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Args for im_eye. + */ +static im_arg_desc eye_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xsize" ), + IM_INPUT_INT( "ysize" ), + IM_INPUT_DOUBLE( "factor" ) +}; + +/* Call im_eye via arg vector. + */ +static int +eye_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + double factor = *((double *) argv[3]); + + return( im_eye( argv[0], xsize, ysize, factor ) ); +} + +/* Description of im_eye. + */ +static im_function eye_desc = { + "im_eye", /* Name */ + "generate IM_BANDFMT_UCHAR [0,255] frequency/amplitude image", + 0, /* Flags */ + eye_vec, /* Dispatch function */ + IM_NUMBER( eye_args ), /* Size of arg list */ + eye_args /* Arg list */ +}; + +/* Call im_feye via arg vector. + */ +static int +feye_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + double factor = *((double *) argv[3]); + + return( im_feye( argv[0], xsize, ysize, factor ) ); +} + +/* Description of im_feye. + */ +static im_function feye_desc = { + "im_feye", /* Name */ + "generate IM_BANDFMT_FLOAT [-1,1] frequency/amplitude image", + 0, /* Flags */ + feye_vec, /* Dispatch function */ + IM_NUMBER( eye_args ), /* Size of arg list */ + eye_args /* Arg list */ +}; + +/* Args for im_zone. + */ +static im_arg_desc zone_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "size" ) +}; + +/* Call im_zone via arg vector. + */ +static int +zone_vec( im_object *argv ) +{ + int size = *((int *) argv[1]); + + return( im_zone( argv[0], size ) ); +} + +/* Description of im_zone. + */ +static im_function zone_desc = { + "im_zone", /* Name */ + "generate IM_BANDFMT_UCHAR [0,255] zone plate image", /* Description */ + 0, /* Flags */ + zone_vec, /* Dispatch function */ + IM_NUMBER( zone_args ), /* Size of arg list */ + zone_args /* Arg list */ +}; + +/* Call im_fzone via arg vector. + */ +static int +fzone_vec( im_object *argv ) +{ + int size = *((int *) argv[1]); + + return( im_fzone( argv[0], size ) ); +} + +/* Description of im_fzone. + */ +static im_function fzone_desc = { + "im_fzone", /* Name */ + "generate IM_BANDFMT_FLOAT [-1,1] zone plate image", /* Description */ + 0, /* Flags */ + fzone_vec, /* Dispatch function */ + IM_NUMBER( zone_args ), /* Size of arg list */ + zone_args /* Arg list */ +}; + +/* Args for im_benchmark. + */ +static im_arg_desc benchmark_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_benchmark via arg vector. + */ +static int +benchmark_vec( im_object *argv ) +{ + return( im_benchmarkn( argv[0], argv[1], 1 ) ); +} + +/* Description of im_benchmark. + */ +static im_function benchmark_desc = { + "im_benchmark", /* Name */ + "do something complicated for testing", /* Description */ + IM_FN_PIO, /* Flags */ + benchmark_vec, /* Dispatch function */ + IM_NUMBER( benchmark_args ), /* Size of arg list */ + benchmark_args /* Arg list */ +}; + +/* Args for im_benchmark2. + */ +static im_arg_desc benchmark2_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_DOUBLE( "value" ) +}; + +/* Call im_benchmark2 via arg vector. + */ +static int +benchmark2_vec( im_object *argv ) +{ + double f; + + if( im_benchmark2( argv[0], &f ) ) + return( -1 ); + + *((double *) argv[1]) = f; + + return( 0 ); +} + +/* Description of im_benchmark2. + */ +static im_function benchmark2_desc = { + "im_benchmark2", /* Name */ + "do something complicated for testing", /* Description */ + IM_FN_PIO, /* Flags */ + benchmark2_vec, /* Dispatch function */ + IM_NUMBER( benchmark2_args ), /* Size of arg list */ + benchmark2_args /* Arg list */ +}; + +/* Args for im_benchmarkn. + */ +static im_arg_desc benchmarkn_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "n" ) +}; + +/* Call im_benchmarkn via arg vector. + */ +static int +benchmarkn_vec( im_object *argv ) +{ + int n = *((int *) argv[2]); + + return( im_benchmarkn( argv[0], argv[1], n ) ); +} + +/* Description of im_benchmarkn. + */ +static im_function benchmarkn_desc = { + "im_benchmarkn", /* Name */ + "do something complicated for testing", /* Description */ + IM_FN_PIO, /* Flags */ + benchmarkn_vec, /* Dispatch function */ + IM_NUMBER( benchmarkn_args ), /* Size of arg list */ + benchmarkn_args /* Arg list */ +}; + +/* Args for im_grey. + */ +static im_arg_desc grey_args[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "xsize" ), + IM_INPUT_INT( "ysize" ) +}; + +/* Call im_grey via arg vector. + */ +static int +grey_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + + return( im_grey( argv[0], xsize, ysize ) ); +} + +/* Description of im_grey. + */ +static im_function grey_desc = { + "im_grey", /* Name */ + "generate IM_BANDFMT_UCHAR [0,255] grey scale image", /* Description */ + 0, /* Flags */ + grey_vec, /* Dispatch function */ + IM_NUMBER( grey_args ), /* Size of arg list */ + grey_args /* Arg list */ +}; + +/* Call im_fgrey via arg vector. + */ +static int +fgrey_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + + return( im_fgrey( argv[0], xsize, ysize ) ); +} + +/* Description of im_fgrey. + */ +static im_function fgrey_desc = { + "im_fgrey", /* Name */ + "generate IM_BANDFMT_FLOAT [0,1] grey scale image", /* Description */ + 0, /* Flags */ + fgrey_vec, /* Dispatch function */ + IM_NUMBER( grey_args ), /* Size of arg list */ + grey_args /* Arg list */ +}; + +/* Call im_make_xy via arg vector. + */ +static int +make_xy_vec( im_object *argv ) +{ + int xsize = *((int *) argv[1]); + int ysize = *((int *) argv[2]); + + return( im_make_xy( argv[0], xsize, ysize ) ); +} + +/* Description of im_make_xy. + */ +static im_function make_xy_desc = { + "im_make_xy", /* Name */ + "generate image with pixel value equal to coordinate", /* Description */ + 0, /* Flags */ + make_xy_vec, /* Dispatch function */ + IM_NUMBER( grey_args ), /* Size of arg list */ + grey_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *other_list[] = { + &benchmark_desc, + &benchmark2_desc, + &benchmarkn_desc, + &eye_desc, + &grey_desc, + &feye_desc, + &fgrey_desc, + &fzone_desc, + &make_xy_desc, + &zone_desc +}; + +/* Package of functions. + */ +im_package im__other = { + "other", + IM_NUMBER( other_list ), + other_list +}; diff --git a/libsrc/relational/Makefile.am b/libsrc/relational/Makefile.am new file mode 100644 index 00000000..df0be055 --- /dev/null +++ b/libsrc/relational/Makefile.am @@ -0,0 +1,11 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = librelational.la + +librelational_la_SOURCES = \ + im_ifthenelse.c \ + im_blend.c \ + relational.c \ + relational_dispatch.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/relational/im_blend.c b/libsrc/relational/im_blend.c new file mode 100644 index 00000000..78ddf958 --- /dev/null +++ b/libsrc/relational/im_blend.c @@ -0,0 +1,364 @@ +/* @(#) Two images as input: must match in size and type. Build an output + * @(#) image blending pixels together according to a conditional image. + * @(#) + * @(#) The conditional image can have n bands or 1 band. If n bands, then we + * @(#) choose from the two source images an element at a time. If 1 band, + * @(#) then choose from the source images a pixel at a time. + * @(#) + * @(#) int + * @(#) im_blend( c, a, b, out ) + * @(#) IMAGE *c, *a, *b; + * @(#) IMAGE *out; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail). + * + * Modified: + * 15/4/05 + * - from im_ifthenelse() + * 8/7/05 + * - oops, broken for some combinations of band differences (thanks Joe) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define iblend1( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( i = 0, x = 0; x < n; i++, x += bands ) { \ + const int v = c[i]; \ + \ + for( z = x; z < x + bands; z++ ) \ + q[z] = (v * a[z] + (255 - v) * b[z] + 128) / 255; \ + } \ +} + +#define iblendn( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( x = 0; x < n; x += bands ) { \ + for( z = x; z < x + bands; z++ ) { \ + const int v = c[z]; \ + \ + q[z] = (v * a[z] + (255 - v) * b[z] + 128) / 255; \ + } \ + } \ +} + +#define fblend1( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( i = 0, x = 0; x < n; i++, x += bands ) { \ + const double v = c[i] / 255.0; \ + \ + for( z = x; z < x + bands; z++ ) \ + q[z] = v * a[z] + (1.0 - v) * b[z]; \ + } \ +} + +#define fblendn( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( x = 0; x < n; x += bands ) { \ + for( z = x; z < x + bands; z++ ) { \ + const double v = c[z] / 255.0; \ + \ + q[z] = v * a[z] + (1.0 - v) * b[z]; \ + } \ + } \ +} + +#define cblend1( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( i = 0, x = 0; x < n; i++, x += bands ) { \ + const double v = c[i] / 255.0; \ + \ + for( z = x; z < x + 2 * bands; z++ ) \ + q[z] = v * a[z] + (1.0 - v) * b[z]; \ + } \ +} + +#define cblendn( TYPE ) { \ + TYPE *a = (TYPE *) ap; \ + TYPE *b = (TYPE *) bp; \ + TYPE *q = (TYPE *) qp; \ + \ + for( x = 0; x < n; x += bands ) { \ + for( z = x; z < x + bands; z++ ) { \ + const double v = c[z] / 255.0; \ + \ + q[2 * z] = v * a[2 * z] + (1.0 - v) * b[2 * z]; \ + q[2 * z + 1] = v * a[2 * z + 1] + \ + (1.0 - v) * b[2 * z + 1]; \ + } \ + } \ +} + +/* Blend with a 1-band conditional image. + */ +static void +blend1_buffer( PEL *qp, PEL *c, PEL *ap, PEL *bp, int width, IMAGE *im ) +{ + int i, x, z; + const int bands = im->Bands; + const int n = width * bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + iblend1( unsigned char ); break; + case IM_BANDFMT_CHAR: + iblend1( signed char ); break; + case IM_BANDFMT_USHORT: + iblend1( unsigned short ); break; + case IM_BANDFMT_SHORT: + iblend1( signed short ); break; + case IM_BANDFMT_UINT: + iblend1( unsigned int ); break; + case IM_BANDFMT_INT: + iblend1( signed int ); break; + case IM_BANDFMT_FLOAT: + fblend1( float ); break; + case IM_BANDFMT_DOUBLE: + fblend1( double ); break; + case IM_BANDFMT_COMPLEX: + cblend1( float ); break; + case IM_BANDFMT_DPCOMPLEX: + cblend1( double ); break; + + default: + assert( 0 ); + } +} + +/* Blend with a many band conditional image. + */ +static void +blendn_buffer( PEL *qp, PEL *c, PEL *ap, PEL *bp, int width, IMAGE *im ) +{ + int x, z; + const int bands = im->Bands; + const int n = width * bands; + + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: + iblendn( unsigned char ); break; + case IM_BANDFMT_CHAR: + iblendn( signed char ); break; + case IM_BANDFMT_USHORT: + iblendn( unsigned short ); break; + case IM_BANDFMT_SHORT: + iblendn( signed short ); break; + case IM_BANDFMT_UINT: + iblendn( unsigned int ); break; + case IM_BANDFMT_INT: + iblendn( signed int ); break; + case IM_BANDFMT_FLOAT: + fblendn( float ); break; + case IM_BANDFMT_DOUBLE: + fblendn( double ); break; + case IM_BANDFMT_COMPLEX: + cblendn( float ); break; + case IM_BANDFMT_DPCOMPLEX: + cblendn( double ); break; + + default: + assert( 0 ); + } +} + +static int +blend_gen( REGION *or, REGION **ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + IMAGE *c = ir[0]->im; + IMAGE *a = ir[1]->im; + + int c_elements = r->width * c->Bands; + int x, y; + + int all0, all255; + + /* Ask for condition pixels. + */ + if( im_prepare( ir[0], r ) ) + return( -1 ); + + /* Is the conditional all zero or all non-zero? We can avoid asking + * for one of the inputs to be calculated. + */ + all0 = *((PEL *) IM_REGION_ADDR( ir[0], le, to )) == 0; + all255 = *((PEL *) IM_REGION_ADDR( ir[0], le, to )) == 255; + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + + for( x = 0; x < c_elements; x++ ) { + all0 &= p[x] == 0; + all255 &= p[x] == 255; + } + + if( !all0 && !all255 ) + break; + } + + if( all255 ) { + /* All 255. Point or at the then image. + */ + if( im_prepare( ir[1], r ) || + im_region_region( or, ir[1], r, r->left, r->top ) ) + return( -1 ); + } + else if( all0 ) { + /* All zero. Point or at the else image. + */ + if( im_prepare( ir[2], r ) || + im_region_region( or, ir[2], r, r->left, r->top ) ) + return( -1 ); + } + else { + /* Mix of set and clear ... ask for both then and else parts and + * interleave. + */ + if( im_prepare( ir[1], r ) || im_prepare( ir[2], r ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + PEL *cp = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + PEL *ap = (PEL *) IM_REGION_ADDR( ir[1], le, y ); + PEL *bp = (PEL *) IM_REGION_ADDR( ir[2], le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + if( c->Bands == 1 ) + blend1_buffer( q, cp, ap, bp, r->width, a ); + else + blendn_buffer( q, cp, ap, bp, r->width, a ); + } + } + + return( 0 ); +} + +int +im_blend( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out ) +{ + IMAGE **in; + + /* If a and b are both LABPACK, repack agan after the blend. + */ + if( a->Coding == IM_CODING_LABQ || b->Coding == IM_CODING_LABQ ) { + IMAGE *t[3]; + int repack = a->Coding == IM_CODING_LABQ && + b->Coding == IM_CODING_LABQ; + + if( im_open_local_array( out, t, 3, "relational-1", "p" ) ) + return( -1 ); + + if( a->Coding == IM_CODING_LABQ ) { + if( im_LabQ2Lab( a, t[0] ) ) + return( -1 ); + a = t[0]; + } + + if( b->Coding == IM_CODING_LABQ ) { + if( im_LabQ2Lab( b, t[1] ) ) + return( -1 ); + b = t[1]; + } + + if( repack ) + return( im_blend( c, a, b, t[2] ) || + im_Lab2LabQ( t[2], out ) ); + else + return( im_blend( c, a, b, out ) ); + } + + /* Check args. + */ + if( a->Coding != IM_CODING_NONE || b->Coding != IM_CODING_NONE || + c->Coding != IM_CODING_NONE ) { + im_error( "im_blend", _( "images not uncoded" ) ); + return( -1 ); + } + if( a->BandFmt != b->BandFmt || + a->Bands != b->Bands ) { + im_error( "im_blend", + _( "size and format of then and else must match" ) ); + return( -1 ); + } + if( c->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_blend", + _( "conditional image must be uchar" ) ); + return( -1 ); + } + if( c->Bands != 1 && c->Bands != a->Bands ) { + im_error( "im_blend", + _( "conditional image must be one band or same as " + "then and else images" ) ); + return( -1 ); + } + if( im_piocheck( c, out ) || im_pincheck( a ) || im_pincheck( b ) ) + return( -1 ); + if( im_demand_hint( out, IM_THINSTRIP, a, b, c, NULL ) ) + return( -1 ); + + /* Make output image. + */ + if( im_cp_descv( out, a, b, c, NULL ) || + !(in = im_allocate_input_array( out, c, a, b, NULL )) || + im_generate( out, + im_start_many, blend_gen, im_stop_many, + in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/relational/im_ifthenelse.c b/libsrc/relational/im_ifthenelse.c new file mode 100644 index 00000000..7ffa6d48 --- /dev/null +++ b/libsrc/relational/im_ifthenelse.c @@ -0,0 +1,211 @@ +/* @(#) Two images as input: must match in size and type. Build an output + * @(#) image choosing pixels from the left if a conditional image is + * @(#) non-zero and from the right otherwise. + * @(#) + * @(#) The conditional image can have n bands or 1 band. If n bands, then we + * @(#) choose from the two source images an element at a time. If 1 band, + * @(#) then choose from the source images a pixel at a time. + * @(#) + * @(#) int + * @(#) im_ifthenelse( c, a, b, out ) + * @(#) IMAGE *c, *a, *b; + * @(#) IMAGE *out; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail). + * + * Modified: + * 9/2/95 JC + * - partialed and ANSIfied + * 11/9/95 JC + * - return( 0 ) missing! oops + * 15/4/05 + * - now just evals left/right if all zero/all one + * 7/10/06 + * - set THINSTRIP + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +ifthenelse_gen( REGION *or, REGION **ir ) +{ + Rect *r = &or->valid; + int le = r->left; + int to = r->top; + int bo = IM_RECT_BOTTOM(r); + + IMAGE *c = ir[0]->im; + IMAGE *a = ir[1]->im; + + int size, width; + int i, x, y, z; + + int all0, alln0; + + if( c->Bands == 1 ) { + /* Copying PEL-sized units with a one-band conditional. + */ + size = IM_IMAGE_SIZEOF_PEL( a ); + width = r->width; + } + else { + /* Copying ELEMENT sized-units with an n-band conditional. + */ + size = IM_IMAGE_SIZEOF_ELEMENT( a ); + width = r->width * a->Bands; + } + + if( im_prepare( ir[0], r ) ) + return( -1 ); + + /* Is the conditional all zero or all non-zero? We can avoid asking + * for one of the inputs to be calculated. + */ + all0 = *((PEL *) IM_REGION_ADDR( ir[0], le, to )) == 0; + alln0 = *((PEL *) IM_REGION_ADDR( ir[0], le, to )) != 0; + for( y = to; y < bo; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + + for( x = 0; x < width; x++ ) { + all0 &= p[x] == 0; + alln0 &= p[x] != 0; + } + + if( !all0 && !alln0 ) + break; + } + + if( alln0 ) { + /* All non-zero. Point or at the then image. + */ + if( im_prepare( ir[1], r ) || + im_region_region( or, ir[1], r, r->left, r->top ) ) + return( -1 ); + } + else if( all0 ) { + /* All zero. Point or at the else image. + */ + if( im_prepare( ir[2], r ) || + im_region_region( or, ir[2], r, r->left, r->top ) ) + return( -1 ); + } + else { + /* Mix of set and clear ... ask for both then and else parts + * and interleave. + */ + if( im_prepare( ir[1], r ) || im_prepare( ir[2], r ) ) + return( -1 ); + + for( y = to; y < bo; y++ ) { + PEL *cp = (PEL *) IM_REGION_ADDR( ir[0], le, y ); + PEL *ap = (PEL *) IM_REGION_ADDR( ir[1], le, y ); + PEL *bp = (PEL *) IM_REGION_ADDR( ir[2], le, y ); + PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); + + for( x = 0, i = 0; i < width; i++, x += size ) { + if( cp[i] ) + for( z = x; z < x + size; z++ ) + q[z] = ap[z]; + else + for( z = x; z < x + size; z++ ) + q[z] = bp[z]; + } + } + } + + return( 0 ); +} + +/* if-then-else. Use IM_BANDFMT_UCHAR image to choose between two other images. + * Either: all same number of bands, or choice image is one band, others are + * n band. + */ +int +im_ifthenelse( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out ) +{ + IMAGE **in; + + /* Check args. + */ + if( a->Coding != IM_CODING_NONE && a->Coding != IM_CODING_LABQ ) { + im_error( "im_ifthenelse", + _( "then image must be uncoded or labpack" ) ); + return( -1 ); + } + if( b->Coding != IM_CODING_NONE && b->Coding != IM_CODING_LABQ ) { + im_error( "im_ifthenelse", + _( "else image must be uncoded or labpack" ) ); + return( -1 ); + } + if( c->Coding != IM_CODING_NONE ) { + im_error( "im_ifthenelse", + _( "condition image must be uncoded" ) ); + return( -1 ); + } + if( a->BandFmt != b->BandFmt || + a->Bands != b->Bands ) { + im_error( "im_ifthenelse", + _( "size and format of then and else must match" ) ); + return( -1 ); + } + if( c->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_ifthenelse", + _( "conditional image must be uchar" ) ); + return( -1 ); + } + if( c->Bands != 1 && c->Bands != a->Bands ) { + im_error( "im_ifthenelse", + _( "conditional image must be one band or same as " + "then and else images" ) ); + return( -1 ); + } + + /* Make output image. + */ + if( im_demand_hint( out, IM_THINSTRIP, c, a, b, NULL ) || + im_cp_descv( out, a, b, c, NULL ) || + !(in = im_allocate_input_array( out, c, a, b, NULL )) || + im_generate( out, + im_start_many, ifthenelse_gen, im_stop_many, + in, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libsrc/relational/man3/Makefile.am b/libsrc/relational/man3/Makefile.am new file mode 100644 index 00000000..1e6c0562 --- /dev/null +++ b/libsrc/relational/man3/Makefile.am @@ -0,0 +1,24 @@ +man_MANS = \ + im_equal.3 \ + im_equalconst.3 \ + im_ifthenelse.3 \ + im_less.3 \ + im_lessconst.3 \ + im_lesseq.3 \ + im_lesseqconst.3 \ + im_more.3 \ + im_moreconst.3 \ + im_moreeq.3 \ + im_moreeqconst.3 \ + im_blend.3 \ + im_notequal.3 \ + im_notequalconst.3 \ + im_equal_vec.3 \ + im_less_vec.3 \ + im_lesseq_vec.3 \ + im_more_vec.3 \ + im_moreeq_vec.3 \ + im_notequal_vec.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/relational/man3/im_blend.3 b/libsrc/relational/man3/im_blend.3 new file mode 100644 index 00000000..fab8f2d5 --- /dev/null +++ b/libsrc/relational/man3/im_blend.3 @@ -0,0 +1 @@ +.so man3/im_ifthenelse.3 diff --git a/libsrc/relational/man3/im_equal.3 b/libsrc/relational/man3/im_equal.3 new file mode 100644 index 00000000..e953cda7 --- /dev/null +++ b/libsrc/relational/man3/im_equal.3 @@ -0,0 +1,141 @@ +.TH RELATIONAL 3 "30 October 1992" +.SH NAME +im_equal, im_notequal, im_equalconst, im_equal_vec, im_notequalconst, +im_notequal_vec, im_less, im_lessconst, im_less_vec, +im_more, im_moreconst, im_more_vec, im_lesseq, im_lesseqconst, im_lesseq_vec, +im_moreeq, +im_moreeqconst, im_moreeq_vec \- relational tests on images. +.SH SYNOPSIS +.B #include + +.B int im_equal(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_equalconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_equal_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_notequal(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_notequalconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_notequal_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_less(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_lessconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_less_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_more(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_moreconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_more_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_lesseq(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_lesseqconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_lesseq_vec(a, out, n) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.B int im_moreeq(a, b, out) +.br +.B IMAGE *a, *b, *out; + +.B int im_moreeqconst(a, out, c) +.br +.B IMAGE *a, *out; +.br +.B double c; + +.B int im_moreeq_vec(a, out, n, v) +.br +.B IMAGE *a, *out; +.br +.B int n; +.br +.B double *v; + +.SH DESCRIPTION +These functions perform a range of relational tests between images, or between +an image and a constant. Functions which take two images as arguments require +that the two images be the same size and have the same number of bands. They +can be of mixed type: you may compare an unsigned char image with a double +image. + +All functions return an unsigned char image, with the same number of bands as +the input images, in which band elements have been set to 255 for true and 0 +for false. + +The logical functions (im_andimage(3), im_orimage(3), im_eorimage(3)) may be +used to combine boolean images to make more complex tests. The selection +function im_ifthenelse(3) may be used to take action on the result of a test. + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. +.SH SEE ALSO +im_ifthenelse(3), im_andimage(3). +.SH COPYRIGHT +National Gallery, 1992 +.SH AUTHOR +J. Cupitt diff --git a/libsrc/relational/man3/im_equal_vec.3 b/libsrc/relational/man3/im_equal_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_equal_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_equalconst.3 b/libsrc/relational/man3/im_equalconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_equalconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_ifthenelse.3 b/libsrc/relational/man3/im_ifthenelse.3 new file mode 100644 index 00000000..38c07e11 --- /dev/null +++ b/libsrc/relational/man3/im_ifthenelse.3 @@ -0,0 +1,39 @@ +.TH IM_IFTHENELSE 3 "30 October 1992" +.SH NAME +im_ifthenelse \- use an unsigned char image to join two images together +.SH SYNOPSIS +.B #include + +.B int im_ifthenelse(c, a, b, out) +.br +.B IMAGE *c, *a, *b, *out; + +.B int im_blend(c, a, b, out) +.br +.B IMAGE *c, *a, *b, *out; + +.SH DESCRIPTION +.B im_ifthenelse +builds an output image which uses some pels from a and some from b: if the +conditional image c is non-zero at that point, the pel comes from a; if it +is zero, the pel comes from b. + +The conditional image c can have either 1 band, in which case entire pels +come either from a or b, or n bands, where n is the number of bands in both a +and b, in which case individual band elements are chosen from a and b. + +Images a and b must match in size, type and number of bands. + +.B im_blend(3) +works just as im_ifthenelse(3), except that instead of selecting between a and +b, values in the condition image are used to softly blend between the two. 255 +means a only, 0 means b only, 128 means 50:50. + +.SH RETURN VALUE +0 on success and -1 on error. +.SH SEE ALSO +im_equal(3), im_and(3). +.SH COPYRIGHT +National Gallery +.SH AUTHOR +J. Cupitt diff --git a/libsrc/relational/man3/im_less.3 b/libsrc/relational/man3/im_less.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_less.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_less_vec.3 b/libsrc/relational/man3/im_less_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_less_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_lessconst.3 b/libsrc/relational/man3/im_lessconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_lessconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_lesseq.3 b/libsrc/relational/man3/im_lesseq.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_lesseq.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_lesseq_vec.3 b/libsrc/relational/man3/im_lesseq_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_lesseq_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_lesseqconst.3 b/libsrc/relational/man3/im_lesseqconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_lesseqconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_more.3 b/libsrc/relational/man3/im_more.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_more.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_more_vec.3 b/libsrc/relational/man3/im_more_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_more_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_moreconst.3 b/libsrc/relational/man3/im_moreconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_moreconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_moreeq.3 b/libsrc/relational/man3/im_moreeq.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_moreeq.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_moreeq_vec.3 b/libsrc/relational/man3/im_moreeq_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_moreeq_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_moreeqconst.3 b/libsrc/relational/man3/im_moreeqconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_moreeqconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_notequal.3 b/libsrc/relational/man3/im_notequal.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_notequal.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_notequal_vec.3 b/libsrc/relational/man3/im_notequal_vec.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_notequal_vec.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/man3/im_notequalconst.3 b/libsrc/relational/man3/im_notequalconst.3 new file mode 100644 index 00000000..141a819b --- /dev/null +++ b/libsrc/relational/man3/im_notequalconst.3 @@ -0,0 +1 @@ +.so man3/im_equal.3 diff --git a/libsrc/relational/relational.c b/libsrc/relational/relational.c new file mode 100644 index 00000000..ec9c7542 --- /dev/null +++ b/libsrc/relational/relational.c @@ -0,0 +1,738 @@ +/* @(#) Relational operations on VASARI images. All return a unsigned + * @(#) char image with the same number of bands as the input images. 255 + * @(#) for true, 0 for false. All work with mixed images types: eg. + * @(#) comparing float and byte. + * @(#) + * @(#) int im_equal( a, b, out ) int im_notequal( a, b, out ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *b, *out; + * @(#) + * @(#) + * @(#) int im_equalconst( a, out, c ) int im_notequalconst( a, out, c ) + * @(#) IMAGE *a, *out; IMAGE *a, *out; + * @(#) double c; double c; + * @(#) + * @(#) int im_less( a, b, out ) int im_lessconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) double c; + * @(#) + * @(#) int im_more( a, b, out ) int im_moreconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) double c; + * @(#) + * @(#) int im_lesseq( a, b, out ) int im_lesseqconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) double c; + * @(#) + * @(#) int im_moreeq( a, b, out ) int im_moreeqconst( a, out, c ) + * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; + * @(#) double c; + * @(#) + * @(#) Returns either 0 (success) or -1 (fail). + * + * Modified: + * 26/7/93 JC + * - >,<,>=,<= tests now as (double) to prevent compiler warnings. Should + * split into int/float cases really for speed. + * 25/1/95 JC + * - partialized + * - updated + * 7/2/95 JC + * - oops! bug with doubles fixed + * 3/7/98 JC + * - vector versions added ... im_equal_vec(), im_lesseq_vec() etc + * - small tidies + * - should be a bit faster, lots of *q++ changed to q[x] + * 10/3/03 JC + * - reworked to remove nested #defines: a bit slower, but much smaller + * - all except _vec forms now work on complex + * 31/7/03 JC + * - oops, relational_format was broken for some combinations + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Save a bit of typing. + */ +#define UC IM_BANDFMT_UCHAR +#define C IM_BANDFMT_CHAR +#define US IM_BANDFMT_USHORT +#define S IM_BANDFMT_SHORT +#define UI IM_BANDFMT_UINT +#define I IM_BANDFMT_INT +#define F IM_BANDFMT_FLOAT +#define M IM_BANDFMT_COMPLEX +#define D IM_BANDFMT_DOUBLE +#define DM IM_BANDFMT_DPCOMPLEX + +/* Type conversions for relational operators. For two input types, give the + * smallest common type, that is, the smallest type which can completely + * express the range of each. + */ +static int relational_format[10][10] = { + /* UC C US S UI I F M D DM */ +/* UC */ { UC, S, US, S, UI, I, F, M, D, DM }, +/* C */ { S, C, I, S, D, I, F, M, D, DM }, +/* US */ { US, I, US, I, UI, I, F, M, D, DM }, +/* S */ { S, S, I, S, D, I, F, M, D, DM }, +/* UI */ { UI, D, UI, D, UI, D, F, M, D, DM }, +/* I */ { I, I, I, I, D, I, F, M, D, DM }, +/* F */ { F, F, F, F, F, F, F, M, D, DM }, +/* M */ { M, M, M, M, M, M, M, M, DM, DM }, +/* D */ { D, D, D, D, D, D, D, DM, D, DM }, +/* DM */ { DM, DM, DM, DM, DM, DM, DM, DM, DM, DM } +}; + +/* Check input images, cast both up to the smallest common type, and invoke + * the process function. + */ +static int +relational_process( char *name, IMAGE **in, IMAGE *out, + im_wrapmany_fn fn, void *b ) +{ + int i, n; + + /* Count args. + */ + for( n = 0; in[n]; n++ ) { + if( in[n]->Coding != IM_CODING_NONE ) { + im_errormsg( "%s: uncoded images only", name ); + return( -1 ); + } + } + + /* Check sizes match. We don't need to check xsize/ysize, as wrapmany + * does this for us. + */ + for( i = 1; i < n; i++ ) + if( in[0]->Bands != in[i]->Bands ) { + im_errormsg( "%s: images differ in numbers of bands", + name ); + return( -1 ); + } + + /* Prepare the output image. + */ + if( im_cp_desc_array( out, in ) ) + return( -1 ); + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = IM_BBITS_BYTE; + + /* For binary ops, cast inputs up to a common format. + */ + if( n == 2 ) { + int fmt = relational_format[in[0]->BandFmt][in[1]->BandFmt]; + IMAGE *t[3]; + + if( im_open_local_array( out, t, 2, "relational-1", "p" ) ) + return( -1 ); + t[2] = NULL; + + for( i = 0; i < n; i++ ) + if( im_clip2fmt( in[i], t[i], fmt ) ) + return( -1 ); + + if( im_wrapmany( t, out, fn, t[0], b ) ) + return( -1 ); + } + else + if( im_wrapmany( in, out, fn, in[0], b ) ) + return( -1 ); + + return( 0 ); +} + +/* Switch over bandfmt, calling a complexd and a non-complex processor. + */ +#define SWITCH( T, P_REAL, P_COMPLEX ) \ + switch( T ) {\ + case IM_BANDFMT_UCHAR: \ + P_REAL( unsigned char ); \ + break; \ + case IM_BANDFMT_CHAR: \ + P_REAL( char ); \ + break; \ + case IM_BANDFMT_USHORT: \ + P_REAL( unsigned short ); \ + break; \ + case IM_BANDFMT_SHORT: \ + P_REAL( short ); \ + break; \ + case IM_BANDFMT_UINT: \ + P_REAL( unsigned int ); \ + break; \ + case IM_BANDFMT_INT: \ + P_REAL( int ); \ + break; \ + case IM_BANDFMT_FLOAT: \ + P_REAL( float ); \ + break; \ + case IM_BANDFMT_DOUBLE: \ + P_REAL( double ); \ + break; \ + case IM_BANDFMT_COMPLEX: \ + P_COMPLEX( float ); \ + break; \ + case IM_BANDFMT_DPCOMPLEX: \ + P_COMPLEX( double ); \ + break; \ + default:\ + error_exit( "relational: internal error" );\ + } + +static void +equal_buffer( PEL **p, PEL *q, int n, IMAGE *a ) +{ + int ne = n * a->Bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + int x; + +#define EQUAL_REAL( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + if( i[x] == j[x] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ +} + +#define EQUAL_COMPLEX( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) { \ + if( i[0] == j[0] && i[1] == j[1] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ + \ + i += 2; \ + j += 2; \ + } \ +} + + SWITCH( a->BandFmt, EQUAL_REAL, EQUAL_COMPLEX ); +} + +int +im_equal( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( relational_process( "im_equal", invec, out, + (im_wrapmany_fn) equal_buffer, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static void +notequal_buffer( PEL **p, PEL *q, int n, IMAGE *a ) +{ + int ne = n * a->Bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + int x; + +#define NOTEQUAL_REAL( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + if( i[x] != j[x] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ +} + +#define NOTEQUAL_COMPLEX( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) { \ + if( i[0] != j[0] || i[1] != j[1] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ + \ + i += 2; \ + j += 2; \ + } \ +} + + SWITCH( a->BandFmt, NOTEQUAL_REAL, NOTEQUAL_COMPLEX ); +} + +int +im_notequal( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( relational_process( "im_equal", invec, out, + (im_wrapmany_fn) notequal_buffer, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* strdup a vector of doubles. + */ +static double * +numdup( IMAGE *out, int n, double *c ) +{ + double *p = IM_ARRAY( out, n, double ); + int i; + + if( !p ) + return( NULL ); + + for( i = 0; i < n; i++ ) + p[i] = c[i]; + + return( p ); +} + +static void +equalvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) +{ + int x, b, i; + +#define EQUALVEC_REAL( TYPE ) { \ + TYPE *p = (TYPE *) in[0]; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < a->Bands; b++, i++ ) \ + if( p[i] == c[b] ) \ + out[i] = 255; \ + else \ + out[i] = 0; \ +} + +/* Sanity failure! + */ +#define EQUALVEC_COMPLEX( TYPE ) assert( 0 ); + + SWITCH( a->BandFmt, EQUALVEC_REAL, EQUALVEC_COMPLEX ); +} + +int +im_equal_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + double *p; + + if( n != in->Bands ) { + im_errormsg( "im_equal_vec: vec size does not match bands" ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_errormsg( "im_equal_vec: not implemented for complex" ); + return( -1 ); + } + + invec[0] = in; invec[1] = NULL; + if( !(p = numdup( out, n, c )) || + relational_process( "im_equal_vec", invec, out, + (im_wrapmany_fn) equalvec_buffer, (void *) p ) ) + return( -1 ); + + return( 0 ); +} + +static double * +mkvec( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + int i; + + if( !(v = IM_ARRAY( out, in->Bands, double )) ) + return( NULL ); + for( i = 0; i < in->Bands; i++ ) + v[i] = c; + + return( v ); +} + +int +im_equalconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_equal_vec( in, out, in->Bands, v ) ); +} + +static void +notequalvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) +{ + int x, b, i; + +#define NOTEQUALVEC_REAL( TYPE ) { \ + TYPE *p = (TYPE *) in[0]; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < a->Bands; b++, i++ ) \ + if( p[i] != c[b] ) \ + out[i] = 255; \ + else \ + out[i] = 0; \ +} + +#define NOTEQUALVEC_COMPLEX( TYPE ) assert( 0 ); + + SWITCH( a->BandFmt, NOTEQUALVEC_REAL, NOTEQUALVEC_COMPLEX ); +} + +int +im_notequal_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + double *p; + + if( n != in->Bands ) { + im_errormsg( "im_notequal_vec: vec size does not match bands" ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_errormsg( "im_notequal_vec: not implemented for complex" ); + return( -1 ); + } + + invec[0] = in; invec[1] = NULL; + if( !(p = numdup( out, n, c )) || + relational_process( "im_notequal_vec", invec, out, + (im_wrapmany_fn) notequalvec_buffer, (void *) p ) ) + return( -1 ); + + return( 0 ); +} + +int +im_notequalconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_notequal_vec( in, out, in->Bands, v ) ); +} + +static void +less_buffer( PEL **p, PEL *q, int n, IMAGE *a, IMAGE *b ) +{ + int ne = n * a->Bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + int x; + +#define LESS_REAL( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + if( i[x] < j[x] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ +} + +/* Take the mod and compare that. + */ +#define LESS_COMPLEX( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) { \ + double m1 = sqrt( i[0] * i[0] + i[1] * i[1] ); \ + double m2 = sqrt( j[0] * j[0] + j[1] * j[1] ); \ + \ + if( m1 < m2 ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ + \ + i += 2; \ + j += 2; \ + } \ +} + + SWITCH( a->BandFmt, LESS_REAL, LESS_COMPLEX ); +} + +int +im_less( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( relational_process( "im_less", invec, out, + (im_wrapmany_fn) less_buffer, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static void +lessvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) +{ + int x, b, i; + +#define LESSVEC_REAL( TYPE ) { \ + TYPE *p = (TYPE *) in[0]; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < a->Bands; b++, i++ ) \ + if( p[i] < c[b] ) \ + out[i] = 255; \ + else \ + out[i] = 0; \ +} + +#define LESSVEC_COMPLEX( TYPE ) assert( 0 ); + + SWITCH( a->BandFmt, LESSVEC_REAL, LESSVEC_COMPLEX ); +} + +int +im_less_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + double *p; + + if( n != in->Bands ) { + im_errormsg( "im_less_vec: vec size does not match bands" ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_errormsg( "im_less_vec: not implemented for complex" ); + return( -1 ); + } + + invec[0] = in; invec[1] = NULL; + if( !(p = numdup( out, n, c )) || + relational_process( "im_less_vec", invec, out, + (im_wrapmany_fn) lessvec_buffer, (void *) p ) ) + return( -1 ); + + return( 0 ); +} + +int +im_lessconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_less_vec( in, out, in->Bands, v ) ); +} + +static void +lesseq_buffer( PEL **p, PEL *q, int n, IMAGE *a, IMAGE *b ) +{ + int ne = n * a->Bands; + PEL *p1 = p[0]; + PEL *p2 = p[1]; + int x; + +#define LESSEQ_REAL( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) \ + if( i[x] <= j[x] ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ +} + +/* Take the mod and compare that. + */ +#define LESSEQ_COMPLEX( TYPE ) { \ + TYPE *i = (TYPE *) p1; \ + TYPE *j = (TYPE *) p2; \ + \ + for( x = 0; x < ne; x++ ) { \ + double m1 = sqrt( i[0] * i[0] + i[1] * i[1] ); \ + double m2 = sqrt( j[0] * j[0] + j[1] * j[1] ); \ + \ + if( m1 <= m2 ) \ + q[x] = 255; \ + else \ + q[x] = 0; \ + \ + i += 2; \ + j += 2; \ + } \ +} + + SWITCH( a->BandFmt, LESSEQ_REAL, LESSEQ_COMPLEX ); +} + +int +im_lesseq( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + IMAGE *invec[3]; + + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + if( relational_process( "im_lesseq", invec, out, + (im_wrapmany_fn) lesseq_buffer, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static void +lesseqvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) +{ + int x, b, i; + +#define LESSEQVEC_REAL( TYPE ) { \ + TYPE *p = (TYPE *) in[0]; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < a->Bands; b++, i++ ) \ + if( p[i] <= c[b] ) \ + out[i] = 255; \ + else \ + out[i] = 0; \ +} + +#define LESSEQVEC_COMPLEX( TYPE ) assert( 0 ); + + SWITCH( a->BandFmt, LESSEQVEC_REAL, LESSEQVEC_COMPLEX ); +} + +int +im_lesseq_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *invec[2]; + double *p; + + if( n != in->Bands ) { + im_errormsg( "im_lesseq_vec: vec size does not match bands" ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_errormsg( "im_lesseq_vec: not implemented for complex" ); + return( -1 ); + } + + invec[0] = in; invec[1] = NULL; + if( !(p = numdup( out, n, c )) || + relational_process( "im_lesseq_vec", invec, out, + (im_wrapmany_fn) lesseqvec_buffer, (void *) p ) ) + return( -1 ); + + return( 0 ); +} + +int +im_lesseqconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_lesseq_vec( in, out, in->Bands, v ) ); +} + +int +im_more( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + return( im_less( in2, in1, out ) ); +} + +int +im_more_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *t; + + /* Same as not (lesseq x). + */ + if( !(t = im_open_local( out, "im_more_vec-1", "p" )) || + im_lesseq_vec( in, t, n, c ) || + im_eorconst( t, out, 255 ) ) + return( -1 ); + + return( 0 ); +} + +int +im_moreconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_more_vec( in, out, in->Bands, v ) ); +} + +int +im_moreeq( IMAGE *in1, IMAGE *in2, IMAGE *out ) +{ + return( im_lesseq( in2, in1, out ) ); +} + +int +im_moreeq_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + IMAGE *t; + + /* Same as not (less x). + */ + if( !(t = im_open_local( out, "im_moreeq_vec-1", "p" )) || + im_less_vec( in, t, n, c ) || + im_eorconst( t, out, 255 ) ) + return( -1 ); + + return( 0 ); +} + +int +im_moreeqconst( IMAGE *in, IMAGE *out, double c ) +{ + double *v; + + return( !(v = mkvec( in, out, c )) || + im_moreeq_vec( in, out, in->Bands, v ) ); +} diff --git a/libsrc/relational/relational_dispatch.c b/libsrc/relational/relational_dispatch.c new file mode 100644 index 00000000..b57c5b86 --- /dev/null +++ b/libsrc/relational/relational_dispatch.c @@ -0,0 +1,513 @@ +/* VIPS function dispatch tables for relational. + * + * J. Cupitt, 23/2/95 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Two images in, one out. + */ +static im_arg_desc two_in_one_out[] = { + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* One image plus one constant in, one image out. + */ +static im_arg_desc const_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLE( "c" ) +}; + +/* One image plus doublevec in, one image out. + */ +static im_arg_desc vec_in_one_out[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_DOUBLEVEC( "vec" ) +}; + +/* Call im_equal via arg vector. + */ +static int +equal_vec( im_object *argv ) +{ + return( im_equal( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_equal. + */ +static im_function equal_desc = { + "im_equal", /* Name */ + "two images equal in value", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + equal_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_equalconst via arg vector. + */ +static int +equalconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_equalconst( argv[0], argv[1], c ) ); +} + +/* Description of im_equalconst. + */ +static im_function equalconst_desc = { + "im_equalconst", /* Name */ + "image equals const", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + equalconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_equal_vec via arg vector. + */ +static int +equal_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_equal_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_equal_vec. + */ +static im_function equal_vec_desc = { + "im_equal_vec", /* Name */ + "image equals doublevec", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + equal_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_notequal via arg vector. + */ +static int +notequal_vec( im_object *argv ) +{ + return( im_notequal( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_notequal. + */ +static im_function notequal_desc = { + "im_notequal", /* Name */ + "two images not equal in value",/* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + notequal_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_notequalconst via arg vector. + */ +static int +notequalconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_notequalconst( argv[0], argv[1], c ) ); +} + +/* Description of im_notequalconst. + */ +static im_function notequalconst_desc = { + "im_notequalconst", /* Name */ + "image does not equal const", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + notequalconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_notequal_vec via arg vector. + */ +static int +notequal_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_notequal_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_notequal_vec. + */ +static im_function notequal_vec_desc = { + "im_notequal_vec", /* Name */ + "image does not equal doublevec", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + notequal_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_less via arg vector. + */ +static int +less_vec( im_object *argv ) +{ + return( im_less( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_less. + */ +static im_function less_desc = { + "im_less", /* Name */ + "in1 less than in2 in value", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + less_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_lessconst via arg vector. + */ +static int +lessconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_lessconst( argv[0], argv[1], c ) ); +} + +/* Description of im_lessconst. + */ +static im_function lessconst_desc = { + "im_lessconst", /* Name */ + "in less than const", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + lessconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_less_vec via arg vector. + */ +static int +less_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_less_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_less_vec. + */ +static im_function less_vec_desc = { + "im_less_vec", /* Name */ + "in less than doublevec", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + less_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_more via arg vector. + */ +static int +more_vec( im_object *argv ) +{ + return( im_more( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_more. + */ +static im_function more_desc = { + "im_more", /* Name */ + "in1 more than in2 in value", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + more_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_moreconst via arg vector. + */ +static int +moreconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_moreconst( argv[0], argv[1], c ) ); +} + +/* Description of im_moreconst. + */ +static im_function moreconst_desc = { + "im_moreconst", /* Name */ + "in more than const", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + moreconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_more_vec via arg vector. + */ +static int +more_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_more_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_more_vec. + */ +static im_function more_vec_desc = { + "im_more_vec", /* Name */ + "in more than doublevec", /* Description */ + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + more_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_moreeq via arg vector. + */ +static int +moreeq_vec( im_object *argv ) +{ + return( im_moreeq( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_moreeq. + */ +static im_function moreeq_desc = { + "im_moreeq", /* Name */ + "in1 more than or equal to in2 in value", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + moreeq_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_moreeqconst via arg vector. + */ +static int +moreeqconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_moreeqconst( argv[0], argv[1], c ) ); +} + +/* Description of im_moreeqconst. + */ +static im_function moreeqconst_desc = { + "im_moreeqconst", /* Name */ + "in more than or equal to const", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + moreeqconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_moreeq_vec via arg vector. + */ +static int +moreeq_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_moreeq_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_moreeq_vec. + */ +static im_function moreeq_vec_desc = { + "im_moreeq_vec", /* Name */ + "in more than or equal to doublevec", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + moreeq_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* Call im_lesseq via arg vector. + */ +static int +lesseq_vec( im_object *argv ) +{ + return( im_lesseq( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_lesseq. + */ +static im_function lesseq_desc = { + "im_lesseq", /* Name */ + "in1 less than or equal to in2 in value", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + lesseq_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* Arg list */ +}; + +/* Call im_lesseqconst via arg vector. + */ +static int +lesseqconst_vec( im_object *argv ) +{ + double c = *((double *) argv[2]); + + return( im_lesseqconst( argv[0], argv[1], c ) ); +} + +/* Description of im_lesseqconst. + */ +static im_function lesseqconst_desc = { + "im_lesseqconst", /* Name */ + "in less than or equal to const", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + lesseqconst_vec, /* Dispatch function */ + IM_NUMBER( const_in_one_out ), /* Size of arg list */ + const_in_one_out /* Arg list */ +}; + +/* Call im_lesseq_vec via arg vector. + */ +static int +lesseq_vec_vec( im_object *argv ) +{ + im_doublevec_object *rv = (im_doublevec_object *) argv[2]; + + return( im_lesseq_vec( argv[0], argv[1], rv->n, rv->vec ) ); +} + +/* Description of im_lesseq_vec. + */ +static im_function lesseq_vec_desc = { + "im_lesseq_vec", /* Name */ + "in less than or equal to doublevec", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + lesseq_vec_vec, /* Dispatch function */ + IM_NUMBER( vec_in_one_out ), /* Size of arg list */ + vec_in_one_out /* Arg list */ +}; + +/* If-then-else args. + */ +static im_arg_desc ifthenelse_args[] = { + IM_INPUT_IMAGE( "cond" ), + IM_INPUT_IMAGE( "in1" ), + IM_INPUT_IMAGE( "in2" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_blend via arg vector. + */ +static int +blend_vec( im_object *argv ) +{ + return( im_blend( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_blend. + */ +static im_function blend_desc = { + "im_blend", /* Name */ + "use cond image to blend between images in1 and in2", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + blend_vec, /* Dispatch function */ + IM_NUMBER( ifthenelse_args ), /* Size of arg list */ + ifthenelse_args /* Arg list */ +}; + +/* Call im_ifthenelse via arg vector. + */ +static int +ifthenelse_vec( im_object *argv ) +{ + return( im_ifthenelse( argv[0], argv[1], argv[2], argv[3] ) ); +} + +/* Description of im_ifthenelse. + */ +static im_function ifthenelse_desc = { + "im_ifthenelse", /* Name */ + "use cond image to choose pels from image in1 or in2", + IM_FN_PTOP | IM_FN_PIO, /* Flags */ + ifthenelse_vec, /* Dispatch function */ + IM_NUMBER( ifthenelse_args ), /* Size of arg list */ + ifthenelse_args /* Arg list */ +}; + +/* Package up all these functions. + */ +static im_function *relational_list[] = { + &blend_desc, + &equal_desc, + &equal_vec_desc, + &equalconst_desc, + &ifthenelse_desc, + &less_desc, + &less_vec_desc, + &lessconst_desc, + &lesseq_desc, + &lesseq_vec_desc, + &lesseqconst_desc, + &more_desc, + &more_vec_desc, + &moreconst_desc, + &moreeq_desc, + &moreeq_vec_desc, + &moreeqconst_desc, + ¬equal_desc, + ¬equal_vec_desc, + ¬equalconst_desc +}; + +/* Package of functions. + */ +im_package im__relational = { + "relational", + IM_NUMBER( relational_list ), + relational_list +}; diff --git a/libsrc/video/Makefile.am b/libsrc/video/Makefile.am new file mode 100644 index 00000000..83f443a1 --- /dev/null +++ b/libsrc/video/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = man3 + +noinst_LTLIBRARIES = libvideo.la + +libvideo_la_SOURCES = \ + video_dispatch.c \ + im_video_v4l1.c \ + im_video_test.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libsrc/video/im_video_test.c b/libsrc/video/im_video_test.c new file mode 100644 index 00000000..b2144f2a --- /dev/null +++ b/libsrc/video/im_video_test.c @@ -0,0 +1,51 @@ +/* test video grabber ... just generates noise and optional errors + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_video_test( IMAGE *im, int brightness, int error ) +{ + if( error ) { + im_error( "im_video_test", _( "error requested" ) ); + return( -1 ); + } + else + return( im_gaussnoise( im, 720, 576, brightness, 20 ) ); +} + diff --git a/libsrc/video/im_video_v4l1.c b/libsrc/video/im_video_v4l1.c new file mode 100644 index 00000000..4f846fee --- /dev/null +++ b/libsrc/video/im_video_v4l1.c @@ -0,0 +1,677 @@ +/* video grab for linux ... uses the original v4l + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifdef HAVE_VIDEODEV + +/* Lots of debugging output. +#define DEBUG + */ + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Zero freed mem to help catch stray pointers. + */ +#ifdef NDEBUG +#define FREE( S ) { if( S ) { (void) im_free( (char *) (S) ); \ + (char *) (S) = NULL; } } +#else +#define FREE( S ) { if( S ) { memset( (char *)(S), 0, sizeof( *(S) ) ); \ + (void) im_free( (char *) (S) ); (S) = NULL; } } +#endif /*NDEBUG*/ + +#define FREEF( F, S ) { if( S ) { (void) F( S ); (S) = NULL; } } +#define FREEFI( F, S ) { if( S ) { (void) F( S ); (S) = 0; } } +#define SETSTR( S, V ) \ + { const char *sst = (V); FREE( S ); (S) = im_strdup( NULL, sst ); } + +/* Max channels on a device. + */ +#define IM_MAXCHANNELS (10) + +/* Video input sources, e.g. tuner, svideo, composite. + */ +#define TUNER (0) +#define COMPOSITE (1) +#define SVIDEO (2) + +typedef struct lgrab { + /* Mmap here, plus file descriptor. + */ + char *device; + char *capture_buffer; + int capture_size; + int fd; + + /* Current settings. + */ + int c_channel; + int c_width; + int c_height; + int c_ngrabs; + + /* Extract capabilities here. + */ + struct video_capability capability; + struct video_channel channel[IM_MAXCHANNELS]; + struct video_window window; + struct video_picture picture; + struct video_mbuf mbuf; + struct video_mmap mmap; +} LGrab; + +#ifdef DEBUG +/* Decode various things ... capability bits etc. + */ +typedef struct { + int value; + const char *name; + const char *description; +} Decode; + +static const Decode decode_palette[] = { + { VIDEO_PALETTE_GREY, + "VIDEO_PALETTE_GREY", "Linear greyscale" }, + { VIDEO_PALETTE_HI240, + "VIDEO_PALETTE_HI240", "High 240 cube (BT848)" }, + { VIDEO_PALETTE_RGB565, + "VIDEO_PALETTE_RGB565", "565 16 bit RGB" }, + { VIDEO_PALETTE_RGB24, + "VIDEO_PALETTE_RGB24", "24bit RGB" }, + { VIDEO_PALETTE_RGB32, + "VIDEO_PALETTE_RGB32", "32bit RGB" }, + { VIDEO_PALETTE_RGB555, + "VIDEO_PALETTE_RGB555", "555 15bit RGB" }, + { VIDEO_PALETTE_YUV422, + "VIDEO_PALETTE_YUV422", "YUV422 capture" }, + { VIDEO_PALETTE_YUYV, + "VIDEO_PALETTE_YUYV", "" }, + { VIDEO_PALETTE_UYVY, + "VIDEO_PALETTE_UYVY", "" }, + { VIDEO_PALETTE_YUV420, + "VIDEO_PALETTE_YUV420", "" }, + { VIDEO_PALETTE_YUV411, + "VIDEO_PALETTE_YUV411", "YUV411 capture" }, + { VIDEO_PALETTE_RAW, + "VIDEO_PALETTE_RAW", "RAW capture (BT848)" }, + { VIDEO_PALETTE_YUV422P, + "VIDEO_PALETTE_YUV422P", "YUV 4:2:2 Planar" }, + { VIDEO_PALETTE_YUV411P, + "VIDEO_PALETTE_YUV411P", "YUV 4:1:1 Planar" }, + { VIDEO_PALETTE_YUV420P, + "VIDEO_PALETTE_YUV420P", "YUV 4:2:0 Planar" }, + { VIDEO_PALETTE_YUV410P, + "VIDEO_PALETTE_YUV410P", "YUV 4:1:0 Planar" } +}; + +static const Decode decode_type[] = { + { VIDEO_TYPE_TV, + "VIDEO_TYPE_TV", "TV" }, + { VIDEO_TYPE_CAMERA, + "VIDEO_TYPE_CAMERA", "Camera" }, +}; + +static const Decode decode_vtype[] = { + { VID_TYPE_CAPTURE, + "VID_TYPE_CAPTURE", "Can capture to memory" }, + { VID_TYPE_TUNER, + "VID_TYPE_TUNER", "Has Tuner" }, + { VID_TYPE_TELETEXT, + "VID_TYPE_TELETEXT", "Has Teletext" }, + { VID_TYPE_OVERLAY, + "VID_TYPE_OVERLAY", "Chromakeyed overlay" }, + { VID_TYPE_CLIPPING, + "VID_TYPE_CLIPPING", "Overlay clipping" }, + { VID_TYPE_FRAMERAM, + "VID_TYPE_FRAMERAM", "Overlay overwrites frame buffer memory" }, + { VID_TYPE_SCALES, + "VID_TYPE_SCALES", "Hardware supports image scaling" }, + { VID_TYPE_MONOCHROME, + "VID_TYPE_MONOCHROME", "Image capture is grey scale only" }, + { VID_TYPE_SUBCAPTURE, + "VID_TYPE_SUBCAPTURE", "Capture sub-image" }, + { VID_TYPE_MPEG_DECODER, + "VID_TYPE_MPEG_DECODER", "Can decode MPEG streams" }, + { VID_TYPE_MPEG_ENCODER, + "VID_TYPE_MPEG_ENCODER", "Can encode MPEG streams" }, + { VID_TYPE_MJPEG_DECODER, + "VID_TYPE_MJPEG_DECODER", "Can decode MJPEG streams" }, + { VID_TYPE_MJPEG_ENCODER, + "VID_TYPE_MJPEG_ENCODER", "Can encode MJPEG streams" } +}; + +static const Decode decode_ctype[] = { + { VIDEO_VC_TUNER, + "VIDEO_VC_TUNER", "Has tuner" }, + { VIDEO_VC_AUDIO, + "VIDEO_VC_AUDIO", "Has audio" } +}; + +/* Prettyprint a value. + */ +static void +decode_print( const Decode *decode, int ndecode, int value ) +{ + int i; + + for( i = 0; i < ndecode; i++ ) + if( decode[i].value == value ) { + printf( "%s, %s", + decode[i].name, decode[i].description ); + return; + } + + printf( "unknown (%p)", value ); +} + +/* Prettyprint a set of flags. + */ +static void +decode_print_flags( const Decode *decode, int ndecode, int flags ) +{ + int i; + + printf( "0x%x ", (unsigned int) flags ); + + for( i = 0; i < ndecode; i++ ) + if( decode[i].value & flags ) { + printf( "[" ); + decode_print( decode, ndecode, + decode[i].value & flags ); + printf( "] " ); + flags &= -1 ^ decode[i].value; + } + + if( flags ) + printf( "[unknown extra flags 0x%x]", (unsigned int) flags ); +} +#endif /*DEBUG*/ + +static int +lgrab_ioctl( LGrab *lg, int request, void *argp ) +{ + if( !lg->fd ) { + im_error( "lgrab_ioctl", _( "no file descriptor" ) ); + return( -1 ); + } + + if( ioctl( lg->fd, request, argp ) < 0 ) { + im_error( "lgrab_ioctl", _( "ioctl(0x%x) failed: %s" ), + (unsigned int) request, strerror( errno ) ); + return( -1 ); + } + + return( 0 ); +} + +static void +lgrab_destroy( LGrab *lg ) +{ + if( lg->fd != -1 ) { + int zero = 0; + + (void) lgrab_ioctl( lg, VIDIOCCAPTURE, &zero ); + close( lg->fd ); + lg->fd = -1; + } + + if( lg->capture_buffer ) { + munmap( lg->capture_buffer, lg->capture_size ); + lg->capture_buffer = NULL; + } + + FREE( lg->device ); + FREE( lg ); +} + +static LGrab * +lgrab_new( const char *device ) +{ + LGrab *lg = IM_NEW( NULL, LGrab ); + int i; + + if( !lg ) + return( NULL ); + + lg->device = NULL; + lg->capture_buffer = NULL; + lg->capture_size = 0; + lg->fd = -1; + + lg->c_channel = -1; + lg->c_width = -1; + lg->c_height = -1; + lg->c_ngrabs = 1; + + SETSTR( lg->device, device ); + if( !lg->device || (lg->fd = open( lg->device, O_RDWR )) == -1 ) { + im_error( "lgrab_new", _( "cannot open video device \"%s\"" ), + lg->device ); + lgrab_destroy( lg ); + return( NULL ); + } + + if( lgrab_ioctl( lg, VIDIOCGCAP, &lg->capability ) ) { + im_error( "lgrab_new", _( "cannot get video capability" ) ); + lgrab_destroy( lg ); + return( NULL ); + } + + /* Check that it can capture to memory. + */ + if( !(lg->capability.type & VID_TYPE_CAPTURE) ) { + im_error( "lgrab_new", _( "card cannot capture to memory" ) ); + lgrab_destroy( lg ); + return( NULL ); + } + + /* Read channel info. + */ + for( i = 0; i < IM_MIN( lg->capability.channels, IM_MAXCHANNELS ); + i++ ) { + lg->channel[i].channel = i; + if( lgrab_ioctl( lg, VIDIOCGCHAN, &lg->channel[i] ) ) { + lgrab_destroy( lg ); + return( NULL ); + } + } + + /* Get other props. + */ + if( lgrab_ioctl( lg, VIDIOCGWIN, &lg->window) || + lgrab_ioctl( lg, VIDIOCGPICT, &lg->picture) ) { + lgrab_destroy( lg ); + return( NULL ); + } + + /* Set 24 bit mode. + */ + lg->picture.depth = 24; + lg->picture.palette = VIDEO_PALETTE_RGB24; + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) { + lgrab_destroy( lg ); + return( NULL ); + } + + return( lg ); +} + +#ifdef DEBUG +static void +lgrab_dump_capability( struct video_capability *capability ) +{ + printf( "capability->name = \"%s\"\n", capability->name ); + printf( "capability->channels = %d\n", capability->channels ); + printf( "capability->audios = %d\n", capability->audios ); + printf( "capability->maxwidth = %d\n", capability->maxwidth ); + printf( "capability->maxheight = %d\n", capability->maxheight ); + printf( "capability->minwidth = %d\n", capability->maxwidth ); + printf( "capability->minheight = %d\n", capability->maxheight ); + printf( "capability->type = " ); + decode_print_flags( decode_vtype, IM_NUMBER( decode_vtype ), + capability->type ); + printf( "\n" ); +} + +static void +lgrab_dump_channel( struct video_channel *channel ) +{ + printf( "channel->channel = %d\n", channel->channel ); + printf( "channel->name = \"%s\"\n", channel->name ); + printf( "channel->tuners = %d\n", channel->tuners ); + + printf( "channel->flags = " ); + decode_print_flags( decode_ctype, IM_NUMBER( decode_ctype ), + channel->flags ); + printf( "\n" ); + printf( "channel->type = " ); + decode_print( decode_type, IM_NUMBER( decode_type ), + channel->type ); + printf( "\n" ); + printf( "channel->norm = %d\n", channel->norm ); +} + +static void +lgrab_dump_picture( struct video_picture *picture ) +{ + printf( "picture->brightness = %d\n", picture->brightness ); + printf( "picture->hue = %d\n", picture->hue ); + printf( "picture->colour = %d\n", picture->colour ); + printf( "picture->contrast = %d\n", picture->contrast ); + printf( "picture->whiteness = %d\n", picture->whiteness ); + printf( "picture->depth = %d\n", picture->depth ); + printf( "picture->palette = " ); + decode_print( decode_palette, IM_NUMBER( decode_palette ), + picture->palette ); + printf( "\n" ); +} + +static void +lgrab_dump( LGrab *lg ) +{ + int i; + + printf( "lg->device = \"%s\"\n", lg->device ); + printf( "lg->capture_buffer = %p\n", + lg->capture_buffer ); + printf( "lg->capture_size = 0x%x\n", + (unsigned int) lg->capture_size ); + printf( "lg->fd = %d\n", lg->fd ); + + printf( "lg->c_channel = %d\n", lg->c_channel ); + printf( "lg->c_width = %d\n", lg->c_width ); + printf( "lg->c_height = %d\n", lg->c_height ); + printf( "lg->c_ngrabs = %d\n", lg->c_ngrabs ); + + lgrab_dump_capability( &lg->capability ); + for( i = 0; i < lg->capability.channels; i++ ) + lgrab_dump_channel( &lg->channel[i] ); + lgrab_dump_picture( &lg->picture ); + + printf( "mbuf->size = 0x%x\n", (unsigned int) lg->mbuf.size ); + printf( "mbuf->frames = %d\n", lg->mbuf.frames ); + printf( "mbuf->offsets = " ); + for( i = 0; i < lg->mbuf.frames; i++ ) + printf( "0x%x ", (unsigned int) lg->mbuf.offsets[i] ); + printf( "\n" ); +} +#endif /*DEBUG*/ + +static int +lgrab_set_capture_size( LGrab *lg, int width, int height ) +{ + lg->c_width = width; + lg->c_height = height; + + lg->window.clipcount = 0; + lg->window.flags = 0; + lg->window.x = 0; + lg->window.y = 0; + lg->window.width = width; + lg->window.height = height; + if( lgrab_ioctl( lg, VIDIOCSWIN, &lg->window ) ) + return( -1 ); + + /* Make sure the correct amount of memory is mapped. + */ + if( lgrab_ioctl( lg, VIDIOCGMBUF, &lg->mbuf ) ) + return( -1 ); + + if( lg->capture_buffer ) { + munmap( lg->capture_buffer, lg->capture_size ); + lg->capture_buffer = NULL; + } + + lg->capture_size = lg->mbuf.size; + if( !(lg->capture_buffer = mmap( 0, lg->capture_size, + PROT_READ | PROT_WRITE, MAP_SHARED, lg->fd, 0 )) ) { + im_error( "lgrab_set_capture_size", + _( "unable to map memory" ) ); + return( -1 ); + } + + return( 0 ); +} + +static int +lgrab_set_channel( LGrab *lg, int channel ) +{ + if( channel < 0 || channel >= lg->capability.channels ) { + im_error( "lgrab_set_channel", + _( "channel not between 0 and %d" ), + lg->capability.channels - 1 ); + return( -1 ); + } + + if( lgrab_ioctl( lg, VIDIOCSCHAN, &lg->channel[channel] ) ) + return( -1 ); + lg->c_channel = channel; + + return( 0 ); +} + +static int +lgrab_set_brightness( LGrab *lg, int brightness ) +{ + lg->picture.brightness = IM_CLIP( 0, brightness, 65535 ); + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) + return( -1 ); + + return( 0 ); +} + +static int +lgrab_set_colour( LGrab *lg, int colour ) +{ + lg->picture.colour = IM_CLIP( 0, colour, 65535 ); + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) + return( -1 ); + + return( 0 ); +} + +static int +lgrab_set_contrast( LGrab *lg, int contrast ) +{ + lg->picture.contrast = IM_CLIP( 0, contrast, 65535 ); + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) + return( -1 ); + + return( 0 ); +} + +static int +lgrab_set_hue( LGrab *lg, int hue ) +{ + lg->picture.hue = IM_CLIP( 0, hue, 65535 ); + if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) + return( -1 ); + + return( 0 ); +} + +static int +lgrab_set_ngrabs( LGrab *lg, int ngrabs ) +{ + lg->c_ngrabs = IM_CLIP( 1, ngrabs, 1000 ); + + return( 0 ); +} + +/* Grab a single frame. + */ +static int +lgrab_capture1( LGrab *lg ) +{ + lg->mmap.format = lg->picture.palette; + lg->mmap.frame = 0; + lg->mmap.width = lg->c_width; + lg->mmap.height = lg->c_height; + if( lgrab_ioctl( lg, VIDIOCMCAPTURE, &lg->mmap ) || + lgrab_ioctl( lg, VIDIOCSYNC, &lg->mmap.frame ) ) + return( -1 ); + + return( 0 ); +} + +/* Grab and average many frames. + */ +static int +lgrab_capturen( LGrab *lg ) +{ + if( lg->c_ngrabs == 1 ) { + if( lgrab_capture1( lg ) ) + return( -1 ); + } + else { + int i, j; + int npx = lg->c_width * lg->c_height * 3; + unsigned int *acc; + + if( !(acc = IM_ARRAY( NULL, npx, unsigned int )) ) + return( -1 ); + memset( acc, 0, npx * sizeof( unsigned int ) ); + + for( i = 0; i < lg->c_ngrabs; i++ ) { + if( lgrab_capture1( lg ) ) { + FREE( acc ); + return( -1 ); + } + + for( j = 0; j < npx; j++ ) + acc[j] += (unsigned char) lg->capture_buffer[j]; + } + + for( j = 0; j < npx; j++ ) { + int avg = (acc[j] + lg->c_ngrabs / 2) / lg->c_ngrabs; + + lg->capture_buffer[j] = IM_CLIP( 0, avg, 255 ); + } + + FREE( acc ); + } + + return( 0 ); +} + +static int +lgrab_capture( LGrab *lg, IMAGE *im ) +{ + int x, y; + unsigned char *line; + + if( lgrab_capturen( lg ) ) + return( -1 ); + + if( im_outcheck( im ) ) + return( -1 ); + im_initdesc( im, lg->c_width, lg->c_height, 3, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, + IM_CODING_NONE, IM_TYPE_MULTIBAND, 1.0, 1.0, 0, 0 ); + if( im_setupout( im ) ) + return( -1 ); + if( !(line = IM_ARRAY( im, + IM_IMAGE_SIZEOF_LINE( im ), unsigned char )) ) + return( -1 ); + + for( y = 0; y < lg->c_height; y++ ) { + unsigned char *p = (unsigned char *) lg->capture_buffer + + y * IM_IMAGE_SIZEOF_LINE( im ); + unsigned char *q = line; + + for( x = 0; x < lg->c_width; x++ ) { + q[0] = p[2]; + q[1] = p[1]; + q[2] = p[0]; + + p += 3; + q += 3; + } + + if( im_writeline( y, im, line ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im_video_v4l1( IMAGE *im, const char *device, + int channel, int brightness, int colour, int contrast, int hue, + int ngrabs ) +{ + LGrab *lg; + + if( !(lg = lgrab_new( device )) ) + return( -1 ); + + if( lgrab_set_capture_size( lg, + lg->capability.maxwidth, lg->capability.maxheight ) || + lgrab_set_channel( lg, channel ) || + lgrab_set_brightness( lg, brightness ) || + lgrab_set_colour( lg, colour ) || + lgrab_set_contrast( lg, contrast ) || + lgrab_set_hue( lg, hue ) || + lgrab_set_ngrabs( lg, ngrabs ) || + lgrab_capture( lg, im ) ) { + lgrab_destroy( lg ); + return( -1 ); + } + +#ifdef DEBUG + printf( "Successful capture with:\n" ); + lgrab_dump( lg ); +#endif /*DEBUG*/ + + lgrab_destroy( lg ); + + return( 0 ); +} + +#else /*!HAVE_VIDEODEV*/ + +#include + +int +im_video_v4l1( IMAGE *im, const char *device, + int channel, int brightness, int colour, int contrast, int hue, + int ngrabs ) +{ + im_error( "im_video_v4l1", + _( "compiled without im_video_v4l1 support" ) ); + return( -1 ); +} + +#endif /*HAVE_VIDEODEV*/ diff --git a/libsrc/video/im_video_v4l1.h b/libsrc/video/im_video_v4l1.h new file mode 100644 index 00000000..57ece650 --- /dev/null +++ b/libsrc/video/im_video_v4l1.h @@ -0,0 +1,76 @@ +/* single frame video capture on linux + */ + +/* + + Copyright (C) 1991-2003 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Video input sources, e.g. tuner, svideo, composite. + */ +#define IM_MAXCHANNELS (10) +#define TUNER (0) +#define COMPOSITE (1) +#define SVIDEO (2) + +typedef struct lgrab { + /* Mmap here, plus file descriptor. + */ + char *device; + char *capture_buffer; + int capture_size; + int fd; + + /* Current settings. + */ + int c_channel; + int c_width; + int c_height; + int c_ngrabs; + + /* Extract capabilities here. + */ + struct video_capability capability; + struct video_channel channel[IM_MAXCHANNELS]; + struct video_window window; + struct video_picture picture; + struct video_mbuf mbuf; + struct video_mmap mmap; +} LGrab; + +void lgrab_destroy( LGrab *lg ); +LGrab *lgrab_new( const char *device ); + +int lgrab_set_capture_size( LGrab *lg, int width, int height ); +int lgrab_set_channel( LGrab *lg, int channel ); +int lgrab_set_brightness( LGrab *lg, int brightness ); +int lgrab_set_colour( LGrab *lg, int colour ); +int lgrab_set_contrast( LGrab *lg, int contrast ); +int lgrab_set_hue( LGrab *lg, int hue ); +int lgrab_set_ngrabs( LGrab *lg, int ngrabs ); +int lgrab_capture( LGrab *lg, IMAGE *im ); +int lgrab_grab( IMAGE *im, const char *device, + int channel, int brightness, int colour, int contrast, int hue, + int ngrabs ); + diff --git a/libsrc/video/man3/Makefile.am b/libsrc/video/man3/Makefile.am new file mode 100644 index 00000000..e9c5172e --- /dev/null +++ b/libsrc/video/man3/Makefile.am @@ -0,0 +1,5 @@ +man_MANS = \ + im_video_v4l1.3 + +EXTRA_DIST = ${man_MANS} + diff --git a/libsrc/video/man3/im_video_v4l1.3 b/libsrc/video/man3/im_video_v4l1.3 new file mode 100644 index 00000000..c3e71a16 --- /dev/null +++ b/libsrc/video/man3/im_video_v4l1.3 @@ -0,0 +1,39 @@ +.TH VIDEO 3 "3 March 2001" +.SH NAME +im_video_v4l1 \- various image grabbers + +.SH SYNOPSIS +.B #include + +.B int +.B im_video_v4l1( IMAGE *im, const char *device, int channel, +.B int brightness, int colour, int contrast, int hue, +.B int ngrabs ) + +.SH DESCRIPTION +These functions grab single video frames from various devices. Which of these +functions work depends upon how your VIPS has been configured and compiled, +and your platform. As a result, they are far from portable ... you want a +layer on top of these functions. + +.B im_video_v4l1(3) +grabs a frame using Video4Linux. It grabs a 24-bit RGB colour image, at the +maximum resolution your card allows. + +.B device +should typically be "/dev/video". +.B channel +selects the channel to acquire: usually 0 is TV, and 1 is composite video. +.B brightness, +.B colour, +.B contrast +and +.B hue +set grab parameters. Each should be in the range (0 - 32768). 32768 is usually +the value you want. +.B ngrabs +sets the number of frames the card should average. Higher values are slower, +but typically less noisy (and slightly softer). + +.SH RETURN VALUE +All functions return 0 on success and -1 on error. diff --git a/libsrc/video/video_dispatch.c b/libsrc/video/video_dispatch.c new file mode 100644 index 00000000..6a9c959e --- /dev/null +++ b/libsrc/video/video_dispatch.c @@ -0,0 +1,115 @@ +/* function dispatch tables for video + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +static int +video_v4l1_vec( im_object *argv ) +{ + IMAGE *out = argv[0]; + char *device = (char *) argv[1]; + int channel = *((int*)argv[2]); + int brightness = *((int*)argv[3]); + int colour = *((int*)argv[4]); + int contrast = *((int*)argv[5]); + int hue = *((int*)argv[6]); + int ngrabs = *((int*)argv[7]); + + return( im_video_v4l1( out, device, + channel, brightness, colour, contrast, hue, ngrabs ) ); +} + +static im_arg_desc video_v4l1_arg_types[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_STRING( "device" ), + IM_INPUT_INT( "channel" ), + IM_INPUT_INT( "brightness" ), + IM_INPUT_INT( "colour" ), + IM_INPUT_INT( "contrast" ), + IM_INPUT_INT( "hue" ), + IM_INPUT_INT( "ngrabs" ) +}; + +static im_function video_v4l1_desc = { + "im_video_v4l1", /* Name */ + "grab a video frame with v4l1", /* Description */ + IM_FN_NOCACHE, /* Flags */ + video_v4l1_vec, /* Dispatch function */ + IM_NUMBER( video_v4l1_arg_types ), /* Size of arg list */ + video_v4l1_arg_types /* Arg list */ +}; + +static int +video_test_vec( im_object *argv ) +{ + IMAGE *out = argv[0]; + int brightness = *((int*)argv[1]); + int error = *((int*)argv[2]); + + return( im_video_test( out, brightness, error ) ); +} + +static im_arg_desc video_test_arg_types[] = { + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "brightness" ), + IM_INPUT_INT( "error" ) +}; + +static im_function video_test_desc = { + "im_video_test", /* Name */ + "test video grabber", /* Description */ + IM_FN_NOCACHE, /* Flags */ + video_test_vec, /* Dispatch function */ + IM_NUMBER( video_test_arg_types ), /* Size of arg list */ + video_test_arg_types /* Arg list */ +}; + +static im_function *video_list[] = { + &video_test_desc, + &video_v4l1_desc +}; + +im_package im__video = { + "video", /* Package name */ + IM_NUMBER( video_list ), /* Function list */ + video_list +}; + + diff --git a/libsrc/vips.def b/libsrc/vips.def new file mode 100644 index 00000000..f6c9450a --- /dev/null +++ b/libsrc/vips.def @@ -0,0 +1,535 @@ +EXPORTS + error_exit + im_BandFmt2char + im_Coding2char + im_Compression2char + im_LCh2Lab + im_LCh2UCS + im_Lab2LCh + im_Lab2LabQ + im_Lab2LabS + im_Lab2UCS + im_Lab2XYZ + im_Lab2XYZ_temp + im_Lab2disp + im_LabQ2Lab + im_LabQ2LabS + im_LabQ2XYZ + im_LabQ2disp + im_LabQ2disp_build_table + im_LabQ2disp_table + im_LabS2Lab + im_LabS2LabQ + im_Type2char + im_UCS2LCh + im_UCS2Lab + im_UCS2XYZ + im_XYZ2Lab + im_XYZ2Lab_temp + im_XYZ2UCS + im_XYZ2Yxy + im_XYZ2disp + im_XYZ2sRGB + im_Yxy2XYZ + im_abs + im_acostra + im_add + im_add_close_callback + im_add_eval_callback + im_add_evalend_callback + im_addgnoise + im_affine + im_allocate_input_array + im_allocate_vargv + im_amiMSBfirst + im_and_vec + im_andconst + im_andimage + im_append_Hist + im_asintra + im_atantra + im_avg + im_bandjoin + im_bernd + im_binfile + im_black + im_blend + im_c2amph + im_c2imag + im_c2ps + im_c2real + im_c2rect + im_ceil + im_char2BandFmt + im_char2Coding + im_char2Compression + im_char2Type + im_circle + im_clamp + im_clear_error_string + im_clip + im_clip2c + im_clip2cm + im_clip2d + im_clip2dcm + im_clip2f + im_clip2fmt + im_clip2i + im_clip2s + im_clip2ui + im_clip2us + im_close + im_close_plugins + im_cmulnorm + im_cntlines + im_col_C2Cucs + im_col_Ch2ab + im_col_Ch2hucs + im_col_Chucs2h + im_col_Cucs2C + im_col_L2Lucs + im_col_Lab2XYZ + im_col_Lucs2L + im_col_XYZ2Lab + im_col_XYZ2rgb + im_col_ab2Ch + im_col_ab2h + im_col_dE00 + im_col_dECMC + im_col_display_name + im_col_displays + im_col_make_tables_RGB + im_col_make_tables_UCS + im_col_pythagoras + im_col_rgb2XYZ + im_compass + im_conv + im_conv_raw + im_convf + im_convf_raw + im_convsep + im_convsep_raw + im_convsepf + im_convsepf_raw + im_convsub + im_cooc_asm + im_cooc_contrast + im_cooc_correlation + im_cooc_entropy + im_cooc_matrix + im_copy + im_copy_dmask_matrix + im_copy_imask_matrix + im_copy_matrix_dmask + im_copy_matrix_imask + im_copy_set + im_correl + im_costra + im_cp_Hist + im_cp_desc + im_create_dmask + im_create_dmaskv + im_create_fmask + im_create_imask + im_create_imaskv + im_crwrhd + im_dE00_fromLab + im_dECMC_fromLab + im_dECMC_fromdisp + im_dE_fromLab + im_dE_fromXYZ + im_dE_fromdisp + im_debugim + im_demand_hint + im_demand_hint_array + im_desc_hd + im_deviate + im_dhint2char + im_diagnostics + im_dif_std + im_dilate + im_dilate_raw + im_disp2Lab + im_disp2XYZ + im_disp_ps + im_divide + im_dmat_alloc + im_dtype2char + im_dup_dmask + im_dup_imask + im_dvector + im_embed + im_eor_vec + im_eorconst + im_eorimage + im_equal + im_equal_vec + im_equalconst + im_erode + im_erode_raw + im_errormsg + im_errormsg_system + im_errorstring + im_existsf + im_exp10tra + im_expntra + im_expntra_vec + im_exptra + im_extract + im_extract_area + im_extract_band + im_eye + im_falsecolour + im_fastcor + im_fastcor_raw + im_fastline + im_fastlineuser + im_fav4 + im_feye + im_fgrey + im_find_function + im_find_package + im_fliphor + im_flipver + im_flood + im_flood_blob + im_floor + im_flt_image_freq + im_fmat_alloc + im_fractsurf + im_free + im_free_dmask + im_free_dmat + im_free_dvector + im_free_fmat + im_free_fvector + im_free_imask + im_free_imat + im_free_ivector + im_free_vargv + im_freqflt + im_fvector + im_fwfft + im_fzone + im_gadd + im_gaddim + im_gammacorrect + im_gauss_dmask + im_gauss_imask + im_gaussnoise + im_gbandjoin + im_generate + im_gfadd + im_glds_asm + im_glds_contrast + im_glds_entropy + im_glds_matrix + im_glds_mean + im_global_balance + im_global_balancef + im_gradient + im_gradient_old + im_grey + im_guess_prefix + im_header_double + im_header_int + im_header_string + im_heq + im_hist + im_histcum + im_histeq + im_histgr + im_histlin + im_histnD + im_histnorm + im_histplot + im_histspec + im_histspec_old + im_hsp + im_icc_ac2rc + im_icc_export + im_icc_export_depth + im_icc_import + im_icc_present + im_icc_transform + im_identity + im_identity_ushort + im_ifthenelse + im_image + im_image_sanity + im_imat_alloc + im_incheck + im_init + im_initdesc + im_inithd + im_insert + im_insert_noexpand + im_insertplace + im_invert + im_invertlut + im_invfft + im_invfftr + im_invmat + im_iocheck + im_isMSBfirst + im_iscomplex + im_isfile + im_isfloat + im_isint + im_isjpeg + im_ismagick + im_ismonotonic + im_ispartial + im_ispng + im_ispoweroftwo + im_isppm + im_istiff + im_istiffpyramid + im_istifftiled + im_isuint + im_isscalar + im_isvips + im_iterate + im_ivector + im_jpeg2vips + im_jpeg2vips_header + im_lab_morph + im_less + im_less_vec + im_lessconst + im_lesseq + im_lesseq_vec + im_lesseqconst + im_lhisteq + im_lhisteq_raw + im_lindetect + im_lindetect_old + im_line + im_lintra + im_lintra_vec + im_list_add + im_list_append + im_list_eq + im_list_fix + im_list_fold + im_list_free + im_list_index + im_list_insert + im_list_len + im_list_map + im_list_map_rev + im_list_member + im_list_pos + im_list_remove + im_litecor + im_load_plugin + im_load_plugins + im_local + im_local_array + im_lock + im_lock_destroy + im_lock_init + im_log10tra + im_log_dmask + im_log_imask + im_logtra + im_lrjoin + im_lrmerge + im_lrmerge1 + im_lrmosaic + im_lrmosaic1 + im_magick2vips + im_magick2vips_header + im_makerw + im_malloc + im_map_packages + im_mapfile + im_mapfilerw + im_maplut + im_mask2vips + im_matcat + im_match_linear + im_match_linear_search + im_matinv + im_matmul + im_mattrn + im_max + im_maxpos + im_maxvalue + im_measure + im_min + im_minpos + im_more + im_more_vec + im_moreconst + im_moreeq + im_moreeq_vec + im_moreeqconst + im_mpercent + im_multiply + im_notequal + im_notequal_vec + im_notequalconst + im_offsets45 + im_offsets90 + im_open + im_open_header + im_openin + im_openinrw + im_openout + im_or_vec + im_orconst + im_orimage + im_outcheck + im_package_of_function + im_paintrect + im_partial + im_path_is_absolute + im_pincheck + im_piocheck + im_plotmask + im_plotpoint + im_png2vips + im_png2vips_header + im_poutcheck + im_powtra + im_powtra_vec + im_ppm2vips + im_ppm2vips_header + im_prepare + im_prepare_thread + im_prepare_to + im_print + im_print_dmask + im_print_imask + im_printdesc + im_printhd + im_printlines + im_profile + im_rank + im_rank_raw + im_read_dmask + im_read_imask + im_readhist + im_readpoint + im_recomb + im_rect_dup + im_rect_equalsrect + im_rect_includespoint + im_rect_includesrect + im_rect_intersectrect + im_rect_isempty + im_rect_marginadjust + im_rect_normalise + im_rect_unionrect + im_region_create + im_region_equalsregion + im_region_free + im_region_image + im_region_local + im_region_mmap_window + im_region_position + im_region_region + im_remainder + im_remainderconst + im_remainderconst_vec + im_remapfilerw + im_remosaic + im_resize_linear + im_ri2c + im_rot180 + im_rot270 + im_rot90 + im_rotate_dmask45 + im_rotate_dmask90 + im_rotate_imask45 + im_rotate_imask90 + im_rotquad + im_run_command + im_rwcheck + im_sRGB2XYZ + im_scale + im_scale_dmask + im_scaleps + im_semaphore_destroy + im_semaphore_down + im_semaphore_downn + im_semaphore_init + im_semaphore_up + im_semaphore_upn + im_setbox + im_setbuf + im_setupout + im_sharpen + im_shiftleft + im_shiftright + im_shrink + im_sign + im_simcontr + im_similarity + im_similarity_area + im_sines + im_sintra + im_slice + im_smear + im_smudge + im_snprintf + im_spatres + im_spcor + im_spcor_raw + im_start_many + im_start_one + im_stats + im_stdif + im_stdif_raw + im_stop_many + im_stop_one + im_strdup + im_stretch3 + im_strncpy + im_strrstr + im_subsample + im_subtract + im_system + im_tantra + im_tbjoin + im_tbmerge + im_tbmerge1 + im_tbmosaic + im_tbmosaic1 + im_thread_create + im_thread_join + im_threadgroup_create + im_threadgroup_free + im_thresh + im_tiff2vips + im_tiff2vips_header + im_tone_analyse + im_tone_build + im_tone_map + im_unlock + im_unmapfile + im_updatehist + im_verrormsg + im_version + im_version_string + im_video_v4l1 + im_vips2bufjpeg + im_vips2jpeg + im_vips2mask + im_vips2mimejpeg + im_vips2png + im_vips2ppm + im_vips2tiff + im_vsnprintf + im_warning + im_wrapmany + im_wrapone + im_write_dmask + im_write_dmask_name + im_write_imask + im_write_imask_name + im_writeline + im_zerox + im_zone + im_zoom diff --git a/libsrcCC/Makefile.am b/libsrcCC/Makefile.am new file mode 100644 index 00000000..c1e4bc24 --- /dev/null +++ b/libsrcCC/Makefile.am @@ -0,0 +1,20 @@ +INCLUDES = -I$(top_srcdir)/include @VIPS_CFLAGS@ + +lib_LTLIBRARIES = libvipsCC.la + +libvipsCC_la_SOURCES = \ + VImage.cc \ + VError.cc \ + VDisplay.cc \ + VMask.cc + +libvipsCC_la_LDFLAGS = \ + -version-info @LIBRARY_CURRENT@:@LIBRARY_REVISION@:@LIBRARY_AGE@ + +libvipsCC_la_LIBADD = \ + $(top_builddir)/libsrc/libvips.la @VIPS_LIBS@ + +vipsc++.cc: + vips --cppc all > vipsc++.cc + +EXTRA_DIST = vipsc++.cc diff --git a/libsrcCC/VDisplay.cc b/libsrcCC/VDisplay.cc new file mode 100644 index 00000000..d2279d42 --- /dev/null +++ b/libsrcCC/VDisplay.cc @@ -0,0 +1,187 @@ +// Object part of VDisplay class + +/* + + Copyright (C) 1991-2001 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +VIPS_NAMESPACE_START + +/* Refcounting stuff first. + */ + +// Free an im_col_display +static void +free_display( im_col_display *d ) +{ + if( d->d_name ) + im_free( d->d_name ); + im_free( d ); +} + +// Dupe an im_col_display +static im_col_display * +dup_display( im_col_display *in ) throw( VError ) +{ + im_col_display *out; + + if( !(out = IM_NEW( NULL, im_col_display )) ) + verror(); + + *out = *in; + if( in->d_name ) + if( !(out->d_name = strdup( in->d_name )) ) { + free_display( out ); + verror( "out of memory" ); + } + + return( out ); +} + +// Remove lut +void VDisplay::refblock::cleanlut() +{ + if( luts ) { + im_free( luts ); + luts = 0; + } +} + +// Remove attached things +void VDisplay::refblock::cleanref() +{ + if( disp && priv ) { + free_display( disp ); + disp = 0; + priv = 0; + } + cleanlut(); +} + +// Get ready to write to disp +void VDisplay::refblock::wready() throw( VError ) +{ + cleanlut(); + if( !priv ) { + disp = dup_display( disp ); + priv = 1; + } +} + +// Check that luts are up-to-date +void VDisplay::refblock::cluts() throw( VError ) +{ + if( !luts ) + if( !(luts = im_col_make_tables_RGB( NULL, disp )) ) + verror(); +} + +VDisplay::~VDisplay() +{ + ref->nrefs--; + if( !ref->nrefs ) + delete ref; +} + +VDisplay &VDisplay::operator=( const VDisplay &a ) +{ + ref->nrefs--; + + if( ref->nrefs > 0 ) + // Need fresh + ref = new refblock; + else + // Recycle old + ref->cleanref(); + + ref = a.ref; + ref->nrefs++; + + return( *this ); +} + +VDisplay::VDisplay( const char *name ) throw( VError ) +{ + // Search for a matching name in the VIPS colour list + im_col_display *scr = im_col_display_name( name ); + + if( !scr ) { + VError err; + + err.app( "VDisplay error: " ); + err.app( "unknown display type \"" ).app( name ).app( "\"\n" ); + err.app( "display should be one of:" ); + + for( int i = 0; (scr = im_col_displays( i )); i++ ) { + err.app( " \"" ); + err.app( im_col_displays( i )->d_name ); + err.app( "\"" ); + } + + err.app( "\n" ); + + throw( err ); + } + + // Install display + ref = new refblock; + ref->disp = scr; +} + +VDisplay::VDisplay() +{ + // Just use sRGB + ref = new refblock; + ref->disp = im_col_displays( 7 ); +} + +/* + +Setters and getters. We used to have a lot of code of the form: + +float &VDisplay::YCW() + { ref->wready(); return( ((im_col_display*)ref->disp)->d_YCW ); } + +This should be split to separate setters/getters so we can exploit const. Too +annoying to do this on such a useless class (I'm certain no one used these +functions anyway), fix in vips8. + + */ + +VIPS_NAMESPACE_END + diff --git a/libsrcCC/VError.cc b/libsrcCC/VError.cc new file mode 100644 index 00000000..2a0427b4 --- /dev/null +++ b/libsrcCC/VError.cc @@ -0,0 +1,98 @@ +// Code for error type + +/* + + Copyright (C) 1991-2001 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +VIPS_NAMESPACE_START + +void VError::perror() +{ + std::cerr << _what; + exit( 1 ); +} + +void VError::perror( const char *name ) +{ + std::cerr << name << ": " << _what; + exit( 1 ); +} + +// Add a new bit to the end of the error buffer +VError &VError::app( const int i ) +{ + char buf[ 256 ]; + + sprintf( buf, "%d", i ); + _what += buf; + + return( *this ); +} + +VError &VError::app( std::string txt ) +{ + _what += txt; + + return( *this ); +}; + +void VError::ostream_print( std::ostream &file ) const +{ + file << _what; +} + +void verror( std::string str ) throw( VError ) +{ + VError err; + + err.app( "VIPS error: " ); + if( str == "" ) { + err.app( im_error_buffer() ); + im_error_clear(); + } + else + err.app( str ).app( "\n" ); + + throw( err ); +} + +VIPS_NAMESPACE_END diff --git a/libsrcCC/VImage.cc b/libsrcCC/VImage.cc new file mode 100644 index 00000000..5d60829f --- /dev/null +++ b/libsrcCC/VImage.cc @@ -0,0 +1,384 @@ +// Object part of VImage class + +/* + + Copyright (C) 1991-2001 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* +#define DEBUG + */ + +VIPS_NAMESPACE_START + +void VImage::refblock::debug_print() +{ + std::list::iterator i; + + printf( "refblock %p:\n", this ); + printf( " im = %p", im ); + if( im && im->filename ) + printf( " (im->filename = \"%s\")", im->filename ); + printf( "\n" ); + printf( " close_on_delete = %d\n", close_on_delete ); + printf( " nrefs (refs to us) = %d\n", nrefs ); + printf( " orefs (refs we make) = refblocks " ); + for( i = orefs.begin(); i != orefs.end(); i++ ) + printf( "%p ", *i ); + printf( "\n" ); +} + +// dump all refblocks for debugging +void VImage::print_all() +{ +#ifdef DEBUG + std::list::iterator i; + + printf( "*** VImage::refblock::print_all() start\n" ); + for( i = all_refblock.begin(); i != all_refblock.end(); i++ ) + (*i)->debug_print(); + printf( "*** VImage::refblock::print_all() end\n" ); +#endif /*DEBUG*/ +} + +// easy call from C version +void im__ccp_print_all() +{ + VImage::print_all(); +} + +// constructor +VImage::refblock::refblock() +{ + im = 0; + close_on_delete = 1; + nrefs = 1; + +#ifdef DEBUG + all_refblock.push_front( this ); +#endif /*DEBUG*/ +} + +// Add a ref - this (output image) depends upon IMAGE in +void VImage::refblock::addref( refblock *in ) throw( VError ) +{ + if( this == in ) + verror( "sanity failure" ); + + in->nrefs++; + orefs.push_front( in ); +} + +VImage::refblock::~refblock() throw( VError ) +{ +#ifdef DEBUG + printf( "VImage::refblock::removeref(): death!\n" ); + debug_print(); +#endif /*DEBUG*/ + + std::list::iterator i; + + if( close_on_delete && im ) { + if( im_close( im ) ) + verror(); + im = 0; + } + + // remove any refs we have ... may trigger other destructs in turn + for( i = orefs.begin(); i != orefs.end(); i++ ) + (*i)->removeref(); + +#ifdef DEBUG + all_refblock.remove( this ); +#endif /*DEBUG*/ +} + +// Remove a ref +void VImage::refblock::removeref() throw( VError ) +{ + nrefs--; + if( nrefs < 0 ) + verror( "too many closes!" ); + if( nrefs == 0 ) + delete this; +} + +// Init with name ... means open for read. +VImage::VImage( const char *name, const char *mode ) throw( VError ) +{ + _ref = new refblock; + + if( !(_ref->im = im_open( name, mode )) ) + verror(); + _ref->close_on_delete = 1; + +#ifdef DEBUG + printf( "VImage::VImage( \"%s\", \"%s\" )\n", name, mode ); + _ref->debug_print(); +#endif /*DEBUG*/ +} + +// Build a VImage from an IMAGE structure +VImage::VImage( im__IMAGE *in ) +{ + _ref = new refblock; + + _ref->im = in; + _ref->close_on_delete = 0; + +#ifdef DEBUG + printf( "VImage::VImage( IMAGE* %p )\n", in ); + _ref->debug_print(); +#endif /*DEBUG*/ +} + +// Build from memory buffer +VImage::VImage( void *buffer, int width, int height, + int bands, TBandFmt format ) throw( VError ) +{ + _ref = new refblock; + + if( !(_ref->im = im_image( buffer, width, height, + bands, int( format ) )) ) + verror(); + _ref->close_on_delete = 1; + +#ifdef DEBUG + printf( "VImage::VImage( void* %p, %d, %d )\n", + buffer, width, height ); + _ref->debug_print(); +#endif /*DEBUG*/ +} + +// Empty init ... means open intermediate +VImage::VImage() throw( VError ) +{ + static int id = 0; + char filename[256]; + + _ref = new refblock; + + /* This is not 100% safe if VIPS threading is not implemented on this + * platform ... but it doesn't really matter. + */ + g_mutex_lock( im__global_lock ); + im_snprintf( filename, 256, "intermediate image #%d", id++ ); + g_mutex_unlock( im__global_lock ); + + if( !(_ref->im = im_open( filename, "p" )) ) + verror(); + _ref->close_on_delete = 1; + +#ifdef DEBUG + printf( "VImage::VImage()\n" ); + _ref->debug_print(); +#endif /*DEBUG*/ +} + +// Copy constructor +VImage::VImage( const VImage &a ) +{ + _ref = a._ref; + _ref->nrefs++; +} + +// Assignment +VImage &VImage::operator=( const VImage &a ) throw( VError ) +{ + _ref->removeref(); + _ref = a._ref; + _ref->nrefs++; + + return( *this ); +} + +// Extract underlying data pointer +void *VImage::data() const throw( VError ) +{ + if( im_incheck( _ref->im ) ) + verror(); + + return( (void *) _ref->im->data ); +} + +void VImage::debug_print() +{ + im_printdesc( image() ); +} + +// Write this to a VImage +VImage VImage::write( VImage out ) throw( VError ) +{ + if( im_copy( _ref->im, out._ref->im ) ) + verror(); + out._ref->addref( _ref ); + + return( out ); +} + +VImage VImage::write( const char *name ) throw( VError ) +{ + VImage out( name, "w" ); + + if( im_copy( _ref->im, out._ref->im ) ) + verror(); + out._ref->addref( _ref ); + + return( out ); +} + +VImage VImage::write() throw( VError ) +{ + VImage out( "VImage:w1", "t" ); + + if( im_copy( _ref->im, out._ref->im ) ) + verror(); + out._ref->addref( _ref ); + + return( out ); +} + +// Projection functions to get header fields +int VImage::Xsize() { return( _ref->im->Xsize ); } +int VImage::Ysize() { return( _ref->im->Ysize ); } +int VImage::Bands() { return( _ref->im->Bands ); } +VImage::TBandFmt VImage::BandFmt() + { return( (TBandFmt) _ref->im->BandFmt ); } +VImage::TCoding VImage::Coding() + { return( (TCoding) _ref->im->Coding ); } +VImage::TType VImage::Type() { return( (TType) _ref->im->Type ); } +float VImage::Xres() { return( _ref->im->Xres ); } +float VImage::Yres() { return( _ref->im->Yres ); } +int VImage::Length() { return( _ref->im->Length ); } +VImage::TCompression VImage::Compression() + { return( (TCompression) _ref->im->Compression ); } +short VImage::Level() { return( _ref->im->Level ); } +int VImage::Xoffset() { return( _ref->im->Xoffset ); } +int VImage::Yoffset() { return( _ref->im->Yoffset ); } + +// Derived fields +const char *VImage::filename() { return( _ref->im->filename ); } +const char *VImage::Hist() { return( im_history_get( _ref->im ) ); } + +// Set header fields and setbuf() in one go. +void VImage::initdesc( int x, int y, int b, + TBandFmt f, TCoding c, TType t, float xr, float yr, int xo, int yo ) + throw( VError ) +{ + static int fmt[] = { + 0, // NOTSET + IM_BBITS_BYTE, IM_BBITS_BYTE, // uchar/char + IM_BBITS_SHORT, IM_BBITS_SHORT, // ushort/short + IM_BBITS_INT, IM_BBITS_INT, // uint/int + IM_BBITS_FLOAT, // float types ... + IM_BBITS_COMPLEX, + IM_BBITS_DOUBLE, + IM_BBITS_DPCOMPLEX + }; + + im_initdesc( _ref->im, x, y, b, + fmt[(int)f + 1], f, c, t, xr, yr, xo, yo ); + if( im_setupout( _ref->im ) ) + verror(); +} + +// Create a Vargv from a name +Vargv::Vargv( const char *name ) +{ + im_function *f = im_find_function( (char *) name ); + + if( !f ) + verror(); + + fn = (im__function *) f; + base = new im_object[f->argc]; + if( im_allocate_vargv( f, base ) ) { + delete[] base; + verror(); + } +} + +// Destroy a Vargv +Vargv::~Vargv() +{ + im_function *f = (im_function *) fn; + + // free any memory allocated for input vectors + // this is the stuff allocated in each function during _object* build, + // see vipsc++.cc + for( int i = 0; i < f->argc; i++ ) { + im_type_desc *ty = f->argv[i].desc; + + if( !(ty->flags & IM_TYPE_OUTPUT) ) { + if( strcmp( ty->type, IM_TYPE_IMAGEVEC ) == 0 || + strcmp( ty->type, IM_TYPE_DOUBLEVEC ) == 0 || + strcmp( ty->type, IM_TYPE_INTVEC ) == 0 ) { + // will work for doublevec and intvec too + im_imagevec_object *io = + (im_imagevec_object *) base[i]; + + if( io->vec ) { + delete[] io->vec; + io->vec = NULL; + } + } + } + } + + im_free_vargv( f, base ); + delete[] base; +} + +// Call the function +void +Vargv::call() +{ + im_function *f = (im_function *) fn; + + if( f->disp( base ) ) + verror(); +} + +/* Insert automatically generated wrappers for VIPS image processing + * functions. + */ +#include "vipsc++.cc" + +VIPS_NAMESPACE_END diff --git a/libsrcCC/VMask.cc b/libsrcCC/VMask.cc new file mode 100644 index 00000000..479b5952 --- /dev/null +++ b/libsrcCC/VMask.cc @@ -0,0 +1,644 @@ +// Object part of VMask class + +/* + + Copyright (C) 1991-2001 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +VIPS_NAMESPACE_START + +/* Functions for VMask - refcounting layer over VPMask. + */ + +VMask::~VMask() +{ + ref->nrefs--; + if( !ref->nrefs ) + delete ref; +} + +VMask &VMask::operator=( const VMask &a ) +{ + // Loosing ref to LHS + ref->nrefs--; + + if( ref->nrefs > 0 ) + // Need fresh refblock + ref = new refblock; + else + // Recycle old refblock + delete ref->pmask; + + // LHS now points to RHS + ref = a.ref; + ref->nrefs++; + + return( *this ); +} + +// Make sure this is a private copy of pmask --- dup if nrefs != 1 +void VMask::make_private() +{ + if( ref->nrefs > 1 ) { + // Make fresh refblock + refblock *ref2 = new refblock; + + // And copy the mask + ref2->pmask = ref->pmask->dup(); + ref->nrefs--; + ref = ref2; + } +} + +void VMask::ostream_print( std::ostream &file ) const +{ + file << *(ref->pmask); +} + +// Embed INTMASK in VIMask +void VIMask::embed( INTMASK *i ) throw( VError ) +{ + if( ref->pmask ) + verror( "embed: VIMask not empty" ); + ref->pmask = new _private_detail::VPIMask( i ); +} + +// Type conversions: implicit INTMASK to DOUBLEMASK +VIMask::operator VDMask() +{ + VDMask out( xsize(), ysize() ); + + out.mask().dptr->scale = scale(); + out.mask().dptr->offset = offset(); + + for( int i = 0; i < size(); i++ ) + out[i] = (*this)[i]; + + return( out ); +} + + +// Forward ref of VImage class +class VImage; + +// Type conversions: implicit DOUBLEMASK to INTMASK +VDMask::operator VIMask() +{ + VIMask out( xsize(), ysize() ); + + out.mask().iptr->scale = int( scale() ); + out.mask().iptr->offset = int( offset() ); + + for( int i = 0; i < size(); i++ ) + out[i] = (int) rint( (*this)[i] ); + + return( out ); +} + +// Type conversions: implicit DOUBLEMASK to VImage +VDMask::operator VImage() throw( VError ) +{ + VImage out; + + if( im_mask2vips( mask().dptr, out.image() ) ) + verror(); + + return( out ); +} + +// ... and INTMASK to VImage +VIMask::operator VImage() { return( VImage( VDMask( *this ) ) ); } + +// Embed DOUBLEMASK in VDMask +void VDMask::embed( DOUBLEMASK *i ) throw( VError ) +{ + if( ref->pmask ) + verror( "embed: VDMask not empty" ); + ref->pmask = new _private_detail::VPDMask( i ); +} + +/* Functions for P*Mask - layer over im_*_*mask() functions. + */ + +// Create empty imask +_private_detail::VPIMask::VPIMask( int xsize, int ysize ) throw( VError ) +{ + if( !(data.iptr = im_create_imask( "VPIMask::VPIMask", xsize, ysize )) ) + verror(); + type = _private_detail::VPMask::INT; +} + +// Init from data +_private_detail::VPIMask::VPIMask( int xsize, int ysize, + int scale, int offset, va_list ap ) + throw( VError ) +{ + if( !(data.iptr = im_create_imask( "VPIMask::VPIMask", xsize, ysize )) ) + verror(); + type = _private_detail::VPMask::INT; + + data.iptr->scale = scale; + data.iptr->offset = offset; + for( int i = 0; i < xsize * ysize; i++ ) + data.iptr->coeff[i] = va_arg( ap, int ); +} + +// Create from filename +_private_detail::VPIMask::VPIMask( const char *name ) throw( VError ) +{ + if( !(data.iptr = im_read_imask( (char *) name )) ) + verror(); + type = _private_detail::VPMask::INT; +} + +// Create from existing INTMASK +_private_detail::VPIMask::VPIMask( INTMASK *imask ) +{ + data.iptr = imask; + type = _private_detail::VPMask::INT; +} + +// Create empty +_private_detail::VPIMask::VPIMask() +{ + data.iptr = 0; + type = _private_detail::VPMask::UNASSIGNED; +} + +_private_detail::VPIMask::~VPIMask() +{ + if( data.iptr ) { + im_free_imask( data.iptr ); + data.iptr = 0; + type = _private_detail::VPMask::UNASSIGNED; + } +} + +// Duplicate -- we are a VPIMask, return a new VPIMask which is a copy of us. +// Return as a VPMask tho'. +_private_detail::VPMask *_private_detail::VPIMask::dup() const throw( VError ) +{ + _private_detail::VPIMask *out = new _private_detail::VPIMask(); + + INTMASK *msk; + if( !(msk = im_dup_imask( data.iptr, "VPIMask::dup" )) ) { + delete out; + verror(); + } + out->embed( msk ); + + return( out ); +} + +// Insert INTMASK pointer +void _private_detail::VPIMask::embed( INTMASK *msk ) throw( VError ) +{ + if( type != _private_detail::VPMask::UNASSIGNED ) + verror( "VPIMask::embed: VPIMask not empty" ); + + data.iptr = msk; + type = _private_detail::VPMask::INT; +} + +int _private_detail::VPIMask::xsize() const throw( VError ) +{ + if( !data.iptr ) + verror( "xsize: mask not set" ); + + return( data.iptr->xsize ); +} + +int _private_detail::VPIMask::ysize() const throw( VError ) +{ + if( !data.iptr ) + verror( "ysize: mask not set" ); + + return( data.iptr->ysize ); +} + +int _private_detail::VPIMask::scale() const throw( VError ) +{ + if( !data.iptr ) + verror( "scale: mask not set" ); + + return( data.iptr->scale ); +} + +int _private_detail::VPIMask::offset() const throw( VError ) +{ + if( !data.iptr ) + verror( "offset: mask not set" ); + + return( data.iptr->offset ); +} + +const char *_private_detail::VPIMask::filename() const throw( VError ) +{ + if( !data.iptr ) + verror( "filename: mask not set" ); + + return( data.iptr->filename ); +} + +void _private_detail::VPIMask::ostream_print( std::ostream &file ) const + throw( VError ) +{ + if( !data.iptr ) + verror( "internal error #7447234" ); + + int i, j; + int *p = data.iptr->coeff; + + file << this->xsize() << "\t" << this->ysize() << "\t"; + file << this->scale() << "\t" << this->offset() << "\n"; + + for( i = 0; i < this->ysize(); i++ ) { + for( j = 0; j < this->xsize(); j++ ) + file << *p++ << "\t"; + + file << "\n"; + } +} + +// Extract start of int array +int *_private_detail::VPIMask::array() const +{ + return( data.iptr->coeff ); +} + +// Create empty dmask +_private_detail::VPDMask::VPDMask( int xsize, int ysize ) throw( VError ) +{ + if( !(data.dptr = im_create_dmask( "VPDMask::VPDMask", xsize, ysize )) ) + verror(); + type = _private_detail::VPMask::DOUBLE; +} + +// Create from args +_private_detail::VPDMask::VPDMask( int xsize, int ysize, + double scale, double offset, va_list ap ) throw( VError ) +{ + if( !(data.dptr = im_create_dmask( "VPDMask::VPDMask", xsize, ysize )) ) + verror(); + type = _private_detail::VPMask::DOUBLE; + + data.dptr->scale = scale; + data.dptr->offset = offset; + for( int i = 0; i < xsize * ysize; i++ ) + data.dptr->coeff[i] = va_arg( ap, double ); +} + +// Create from filename +_private_detail::VPDMask::VPDMask( const char *name ) throw( VError ) +{ + if( !(data.dptr = im_read_dmask( (char *) name )) ) + verror(); + type = _private_detail::VPMask::DOUBLE; +} + +// Create empty +_private_detail::VPDMask::VPDMask() +{ + data.dptr = 0; + type = _private_detail::VPMask::UNASSIGNED; +} + +// Create from existing DOUBLEMASK +_private_detail::VPDMask::VPDMask( DOUBLEMASK *dmask ) +{ + data.dptr = dmask; + type = _private_detail::VPMask::DOUBLE; +} + +_private_detail::VPDMask::~VPDMask() +{ + if( data.dptr ) { + im_free_dmask( data.dptr ); + data.dptr = 0; + type = _private_detail::VPMask::UNASSIGNED; + } +} + +// Duplicate -- we are a VPIMask, return a new VPIMask which is a copy of us. +// Return as a VPMask tho'. +_private_detail::VPMask *_private_detail::VPDMask::dup() const throw( VError ) +{ + _private_detail::VPDMask *out = new _private_detail::VPDMask(); + + DOUBLEMASK *msk; + if( !(msk = im_dup_dmask( data.dptr, "VPDMask::dup" )) ) { + delete out; + verror(); + } + out->embed( msk ); + + return( out ); +} + +// Insert DOUBLEMASK pointer +void _private_detail::VPDMask::embed( DOUBLEMASK *msk ) throw( VError ) +{ + if( type != _private_detail::VPMask::UNASSIGNED ) + verror( "VPDMask::embed: VPDMask not empty" ); + + data.dptr = msk; + type = _private_detail::VPMask::DOUBLE; +} + +int _private_detail::VPDMask::xsize() const throw( VError ) +{ + if( !data.dptr ) + verror( "xsize: mask not set" ); + + return( data.dptr->xsize ); +} + +int _private_detail::VPDMask::ysize() const throw( VError ) +{ + if( !data.dptr ) + verror( "ysize: mask not set" ); + + return( data.dptr->ysize ); +} + +double _private_detail::VPDMask::scale() const throw( VError ) +{ + if( !data.dptr ) + verror( "scale: mask not set" ); + + return( data.dptr->scale ); +} + +double _private_detail::VPDMask::offset() const throw( VError ) +{ + if( !data.dptr ) + verror( "offset: mask not set" ); + + return( data.dptr->offset ); +} + +const char *_private_detail::VPDMask::filename() const throw( VError ) +{ + if( !data.dptr ) + verror( "filename: mask not set" ); + + return( data.dptr->filename ); +} + +void _private_detail::VPDMask::ostream_print( std::ostream &file ) const + throw( VError ) +{ + if( !data.dptr ) + verror( "internal error #7447234" ); + + int i, j; + double *p = data.dptr->coeff; + + file << this->xsize() << "\t" << this->ysize() << "\t"; + file << this->scale() << "\t" << this->offset() << "\n"; + + for( i = 0; i < this->ysize(); i++ ) { + for( j = 0; j < this->xsize(); j++ ) + file << *p++ << "\t"; + + file << "\n"; + } +} + +// Extract data pointer +double *_private_detail::VPDMask::array() const +{ + return( data.dptr->coeff ); +} + +// Build functions +VIMask VIMask::gauss( double sig, double minamp ) throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_gauss_imask( "VIMask::gauss", sig, minamp )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::gauss( double sig, double minamp ) throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_gauss_dmask( "VDMask::gauss", sig, minamp )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VIMask VIMask::log( double sig, double minamp ) throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_log_imask( "VIMask::log", sig, minamp )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::log( double sig, double minamp ) throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_log_dmask( "VDMask::log", sig, minamp )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +// Manipulation functions +VIMask VIMask::rotate45() throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_rotate_imask45( mask().iptr, "VIMask::rotate45" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VIMask VIMask::rotate90() throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_rotate_imask90( mask().iptr, "VIMask::rotate90" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::rotate45() throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_rotate_dmask45( mask().dptr, "VDMask::rotate45" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::rotate90() throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_rotate_dmask90( mask().dptr, "VDMask::rotate90" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::trn() throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_mattrn( mask().dptr, "VDMask::trn" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::inv() throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_matinv( mask().dptr, "VDMask::inv" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::mul( VDMask m ) throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_matmul( mask().dptr, m.mask().dptr, "VDMask::mul" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VDMask VDMask::cat( VDMask m ) throw( VError ) +{ + VDMask out; + DOUBLEMASK *msk; + + if( !(msk = im_matcat( mask().dptr, m.mask().dptr, "VDMask::cat" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +VIMask VDMask::scalei() throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_scale_dmask( mask().dptr, "VDMask::scalei" )) ) + verror(); + out.embed( msk ); + + return( out ); +} + +// Arithmetic on a VIMask ... just cast and use VDMask +VDMask VIMask::trn() throw( VError ) + { return( ((VDMask)*this).trn() ); } +VDMask VIMask::inv() throw( VError ) + { return( ((VDMask)*this).inv() ); } +VDMask VIMask::cat( VDMask a ) throw( VError ) + { return( ((VDMask)*this).cat( a ) ); } +VDMask VIMask::mul( VDMask a ) throw( VError ) + { return( ((VDMask)*this).mul( a ) ); } + +// Overload [] to get linear array subscript. +// Our caller may write to the result, so make sure we have a private +// copy. +// Involves function call, slow anyway, so do range checking +int &VIMask::operator[]( int x ) throw( VError ) +{ + if( ref->nrefs != 1 ) + make_private(); + + if( x > size() ) + verror( "VIMask::operator[]: subscript out of range" ); + + return( ((_private_detail::VPIMask *)ref->pmask)->array()[x] ); +} + +double &VDMask::operator[]( int x ) throw( VError ) +{ + if( ref->nrefs != 1 ) + make_private(); + + if( x > size() ) + verror( "VDMask::operator[]: subscript out of range" ); + + return( ((_private_detail::VPDMask *)ref->pmask)->array()[x] ); +} + +VIPS_NAMESPACE_END diff --git a/libsrcCC/vipsc++.cc b/libsrcCC/vipsc++.cc new file mode 100644 index 00000000..41439253 --- /dev/null +++ b/libsrcCC/vipsc++.cc @@ -0,0 +1,5367 @@ +// this file automatically generated from +// VIPS library 7.12.2-Tue Jul 17 23:36:09 BST 2007 +// im_abs: absolute value +VImage VImage::abs() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_abs" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_acostra: acos of image (result in degrees) +VImage VImage::acos() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_acostra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_add: add two images +VImage VImage::add( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_add" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_asintra: asin of image (result in degrees) +VImage VImage::asin() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_asintra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_atantra: atan of image (result in degrees) +VImage VImage::atan() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_atantra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_avg: average value of image +double VImage::avg() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_avg" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_point_bilinear: interpolate value at single point, linearly +double VImage::point_bilinear( double x, double y, int band ) throw( VError ) +{ + VImage in = *this; + double val; + + Vargv _vec( "im_point_bilinear" ); + + _vec.data(0) = in.image(); + *((double*) _vec.data(1)) = x; + *((double*) _vec.data(2)) = y; + *((int*) _vec.data(3)) = band; + _vec.call(); + val = *((double*)_vec.data(4)); + + return( val ); +} + +// im_bandmean: average image bands +VImage VImage::bandmean() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_bandmean" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_ceil: round to smallest integal value not less than +VImage VImage::ceil() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_ceil" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_cmulnorm: multiply two complex images, normalising output +VImage VImage::cmulnorm( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_cmulnorm" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_costra: cos of image (angles in degrees) +VImage VImage::cos() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_costra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_deviate: standard deviation of image +double VImage::deviate() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_deviate" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_divide: divide two images +VImage VImage::divide( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_divide" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_exp10tra: 10^pel of image +VImage VImage::exp10() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_exp10tra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_expntra: x^pel of image +VImage VImage::expn( double x ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_expntra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = x; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_expntra_vec: [x,y,z]^pel of image +VImage VImage::expn( std::vector v ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_expntra_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = v.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[v.size()]; + for( unsigned int i = 0; i < v.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = v[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_exptra: e^pel of image +VImage VImage::exp() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_exptra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_fav4: average of 4 images +VImage VImage::fav4( VImage in2, VImage in3, VImage in4 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_fav4" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = in3.image(); + _vec.data(3) = in4.image(); + _vec.data(4) = out.image(); + _vec.call(); + + return( out ); +} + +// im_floor: round to largest integal value not greater than +VImage VImage::floor() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_floor" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_gadd: calculate a*in1 + b*in2 + c = outfile +VImage VImage::gadd( double a, double b, VImage in2, double c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_gadd" ); + + *((double*) _vec.data(0)) = a; + _vec.data(1) = in1.image(); + *((double*) _vec.data(2)) = b; + _vec.data(3) = in2.image(); + *((double*) _vec.data(4)) = c; + _vec.data(5) = out.image(); + _vec.call(); + + return( out ); +} + +// im_invert: photographic negative +VImage VImage::invert() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_invert" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lintra: calculate a*in + b = outfile +VImage VImage::lin( double a, double b ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lintra" ); + + *((double*) _vec.data(0)) = a; + _vec.data(1) = in.image(); + *((double*) _vec.data(2)) = b; + _vec.data(3) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_linreg: pixelwise linear regression +VImage VImage::linreg( std::vector ins, std::vector xs ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_linreg" ); + + ((im_imagevec_object*) _vec.data(0))->n = ins.size(); + ((im_imagevec_object*) _vec.data(0))->vec = new IMAGE *[ins.size()]; + for( unsigned int i = 0; i < ins.size(); i++ ) + ((im_imagevec_object*) _vec.data(0))->vec[i] = ins[i].image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = xs.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[xs.size()]; + for( unsigned int i = 0; i < xs.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = xs[i]; + _vec.call(); + for( unsigned int i = 0; i < ins.size(); i++ ) + out._ref->addref( ins[i]._ref ); + + return( out ); +} + +// im_lintra_vec: calculate a*in + b -> out, a and b vectors +VImage VImage::lin( std::vector a, std::vector b ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lintra_vec" ); + + ((im_doublevec_object*) _vec.data(0))->n = a.size(); + ((im_doublevec_object*) _vec.data(0))->vec = new double[a.size()]; + for( unsigned int i = 0; i < a.size(); i++ ) + ((im_doublevec_object*) _vec.data(0))->vec[i] = a[i]; + _vec.data(1) = in.image(); + ((im_doublevec_object*) _vec.data(2))->n = b.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[b.size()]; + for( unsigned int i = 0; i < b.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = b[i]; + _vec.data(3) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_litecor: calculate max(white)*factor*(in/white), if clip == 1 +VImage VImage::litecor( VImage white, int clip, double factor ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_litecor" ); + + _vec.data(0) = in.image(); + _vec.data(1) = white.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = clip; + *((double*) _vec.data(4)) = factor; + _vec.call(); + + return( out ); +} + +// im_log10tra: log10 of image +VImage VImage::log10() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_log10tra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_logtra: ln of image +VImage VImage::log() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_logtra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_max: maximum value of image +double VImage::max() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_max" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_maxpos: position of maximum value of image +std::complex VImage::maxpos() throw( VError ) +{ + VImage in = *this; + std::complex position; + + Vargv _vec( "im_maxpos" ); + + _vec.data(0) = in.image(); + _vec.call(); + position = *((std::complex*)_vec.data(1)); + + return( position ); +} + +// im_maxpos_avg: position of maximum value of image, averaging in case of draw +double VImage::maxpos_avg( double& y, double& out ) throw( VError ) +{ + VImage in = *this; + double x; + + Vargv _vec( "im_maxpos_avg" ); + + _vec.data(0) = in.image(); + _vec.call(); + x = *((double*)_vec.data(1)); + y = *((double*)_vec.data(2)); + out = *((double*)_vec.data(3)); + + return( x ); +} + +// im_measure: measure averages of a grid of patches +VDMask VImage::measure( int x, int y, int w, int h, int h_patches, int v_patches ) throw( VError ) +{ + VImage in = *this; + VDMask mask; + + Vargv _vec( "im_measure" ); + + _vec.data(0) = in.image(); + ((im_mask_object*) _vec.data(1))->name = (char*)"noname"; + *((int*) _vec.data(2)) = x; + *((int*) _vec.data(3)) = y; + *((int*) _vec.data(4)) = w; + *((int*) _vec.data(5)) = h; + *((int*) _vec.data(6)) = h_patches; + *((int*) _vec.data(7)) = v_patches; + _vec.call(); + mask.embed( (DOUBLEMASK *)((im_mask_object*)_vec.data(1))->mask ); + + return( mask ); +} + +// im_min: minimum value of image +double VImage::min() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_min" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_minpos: position of minimum value of image +std::complex VImage::minpos() throw( VError ) +{ + VImage in = *this; + std::complex position; + + Vargv _vec( "im_minpos" ); + + _vec.data(0) = in.image(); + _vec.call(); + position = *((std::complex*)_vec.data(1)); + + return( position ); +} + +// im_multiply: multiply two images +VImage VImage::multiply( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_multiply" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_powtra: pel^x ofbuildimage +VImage VImage::pow( double x ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_powtra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = x; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_powtra_vec: pel^[x,y,z] of image +VImage VImage::pow( std::vector v ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_powtra_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = v.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[v.size()]; + for( unsigned int i = 0; i < v.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = v[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_remainder: remainder after integer division +VImage VImage::remainder( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_remainder" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_remainderconst: remainder after integer division by a constant +VImage VImage::remainder( double x ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_remainderconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = x; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_remainderconst_vec: remainder after integer division by a vector of constants +VImage VImage::remainder( std::vector x ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_remainderconst_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = x.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[x.size()]; + for( unsigned int i = 0; i < x.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = x[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rint: round to nearest integal value +VImage VImage::rint() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rint" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_sign: unit vector in direction of value +VImage VImage::sign() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_sign" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_sintra: sin of image (angles in degrees) +VImage VImage::sin() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_sintra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_stats: many image statistics in one pass +VDMask VImage::stats() throw( VError ) +{ + VImage in = *this; + VDMask statistics; + + Vargv _vec( "im_stats" ); + + _vec.data(0) = in.image(); + ((im_mask_object*) _vec.data(1))->name = (char*)"noname"; + _vec.call(); + statistics.embed( (DOUBLEMASK *)((im_mask_object*)_vec.data(1))->mask ); + + return( statistics ); +} + +// im_subtract: subtract two images +VImage VImage::subtract( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_subtract" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_tantra: tan of image (angles in degrees) +VImage VImage::tan() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_tantra" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_andimage: bitwise and of two images +VImage VImage::andimage( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_andimage" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_andimageconst: bitwise and of an image with a constant +VImage VImage::andimage( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_andimageconst" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_andimage_vec: bitwise and of an image with a vector constant +VImage VImage::andimage( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_andimage_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_orimage: bitwise or of two images +VImage VImage::orimage( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_orimage" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_orimageconst: bitwise or of an image with a constant +VImage VImage::orimage( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_orimageconst" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_orimage_vec: bitwise or of an image with a vector constant +VImage VImage::orimage( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_orimage_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_eorimage: bitwise eor of two images +VImage VImage::eorimage( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_eorimage" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_eorimageconst: bitwise eor of an image with a constant +VImage VImage::eorimage( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_eorimageconst" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_eorimage_vec: bitwise eor of an image with a vector constant +VImage VImage::eorimage( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_eorimage_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_shiftleft: shift integer image n bits to left +VImage VImage::shiftleft( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_shiftleft" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_shiftright: shift integer image n bits to right +VImage VImage::shiftright( int c ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_shiftright" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in1._ref ); + + return( out ); +} + +// im_LCh2Lab: convert LCh to Lab +VImage VImage::LCh2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LCh2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LCh2UCS: convert LCh to UCS +VImage VImage::LCh2UCS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LCh2UCS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2LCh: convert Lab to LCh +VImage VImage::Lab2LCh() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2LCh" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2LabQ: convert Lab to LabQ +VImage VImage::Lab2LabQ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2LabQ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2LabS: convert Lab to LabS +VImage VImage::Lab2LabS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2LabS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2UCS: convert Lab to UCS +VImage VImage::Lab2UCS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2UCS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2XYZ: convert D65 Lab to XYZ +VImage VImage::Lab2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2XYZ_temp: convert Lab to XYZ, with a specified colour temperature +VImage VImage::Lab2XYZ_temp( double X0, double Y0, double Z0 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2XYZ_temp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = X0; + *((double*) _vec.data(3)) = Y0; + *((double*) _vec.data(4)) = Z0; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Lab2disp: convert Lab to displayable +VImage VImage::Lab2disp( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Lab2disp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabQ2LabS: convert LabQ to LabS +VImage VImage::LabQ2LabS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabQ2LabS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabQ2Lab: convert LabQ to Lab +VImage VImage::LabQ2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabQ2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabQ2XYZ: convert LabQ to XYZ +VImage VImage::LabQ2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabQ2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabQ2disp: convert LabQ to displayable +VImage VImage::LabQ2disp( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabQ2disp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabS2LabQ: convert LabS to LabQ +VImage VImage::LabS2LabQ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabS2LabQ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_LabS2Lab: convert LabS to Lab +VImage VImage::LabS2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_LabS2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_UCS2LCh: convert UCS to LCh +VImage VImage::UCS2LCh() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_UCS2LCh" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_UCS2Lab: convert UCS to Lab +VImage VImage::UCS2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_UCS2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_UCS2XYZ: convert UCS to XYZ +VImage VImage::UCS2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_UCS2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2Lab: convert D65 XYZ to Lab +VImage VImage::XYZ2Lab() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2Lab_temp: convert XYZ to Lab, with a specified colour temperature +VImage VImage::XYZ2Lab_temp( double X0, double Y0, double Z0 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2Lab_temp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = X0; + *((double*) _vec.data(3)) = Y0; + *((double*) _vec.data(4)) = Z0; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2UCS: convert XYZ to UCS +VImage VImage::XYZ2UCS() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2UCS" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2Yxy: convert XYZ to Yxy +VImage VImage::XYZ2Yxy() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2Yxy" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2disp: convert XYZ to displayble +VImage VImage::XYZ2disp( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2disp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_XYZ2sRGB: convert XYZ to sRGB +VImage VImage::XYZ2sRGB() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_XYZ2sRGB" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_Yxy2XYZ: convert Yxy to XYZ +VImage VImage::Yxy2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_Yxy2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_dE00_fromLab: calculate delta-E CIE2000 for two Lab images +VImage VImage::dE00_fromLab( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dE00_fromLab" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dECMC_fromLab: calculate delta-E CMC(1:1) for two Lab images +VImage VImage::dECMC_fromLab( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dECMC_fromLab" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dECMC_fromdisp: calculate delta-E CMC(1:1) for two displayable images +VImage VImage::dECMC_fromdisp( VImage in2, VDisplay disp ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dECMC_fromdisp" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.data(3) = disp.disp(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dE_fromLab: calculate delta-E for two Lab images +VImage VImage::dE_fromLab( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dE_fromLab" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dE_fromXYZ: calculate delta-E for two XYZ images +VImage VImage::dE_fromXYZ( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dE_fromXYZ" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_dE_fromdisp: calculate delta-E for two displayable images +VImage VImage::dE_fromdisp( VImage in2, VDisplay disp ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_dE_fromdisp" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.data(3) = disp.disp(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_disp2Lab: convert displayable to Lab +VImage VImage::disp2Lab( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_disp2Lab" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_disp2XYZ: convert displayable to XYZ +VImage VImage::disp2XYZ( VDisplay disp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_disp2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = disp.disp(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_ac2rc: convert LAB from AC to RC using an ICC profile +VImage VImage::icc_ac2rc( char* profile ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_ac2rc" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) profile; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_export: convert a float LAB to an 8-bit device image with an ICC profile +VImage VImage::icc_export( char* output_profile, int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_export" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) output_profile; + *((int*) _vec.data(3)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_export_depth: convert a float LAB to device space with an ICC profile +VImage VImage::icc_export_depth( int depth, char* output_profile, int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_export_depth" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = depth; + _vec.data(3) = (im_object) output_profile; + *((int*) _vec.data(4)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_import: convert a device image to float LAB with an ICC profile +VImage VImage::icc_import( char* input_profile, int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_import" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) input_profile; + *((int*) _vec.data(3)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_import_embedded: convert a device image to float LAB using the embedded profile +VImage VImage::icc_import_embedded( int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_import_embedded" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_icc_transform: convert between two device images with a pair of ICC profiles +VImage VImage::icc_transform( char* input_profile, char* output_profile, int intent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_icc_transform" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) input_profile; + _vec.data(3) = (im_object) output_profile; + *((int*) _vec.data(4)) = intent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lab_morph: morph colourspace of a LAB image +VImage VImage::lab_morph( VDMask greyscale, double L_offset, double L_scale, double a_scale, double b_scale ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lab_morph" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = greyscale.mask().dptr; + *((double*) _vec.data(3)) = L_offset; + *((double*) _vec.data(4)) = L_scale; + *((double*) _vec.data(5)) = a_scale; + *((double*) _vec.data(6)) = b_scale; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_sRGB2XYZ: convert sRGB to XYZ +VImage VImage::sRGB2XYZ() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_sRGB2XYZ" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_bandjoin: bandwise join of two images +VImage VImage::bandjoin( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_bandjoin" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_black: generate black image +VImage VImage::black( int x_size, int y_size, int bands ) throw( VError ) +{ + VImage output; + + Vargv _vec( "im_black" ); + + _vec.data(0) = output.image(); + *((int*) _vec.data(1)) = x_size; + *((int*) _vec.data(2)) = y_size; + *((int*) _vec.data(3)) = bands; + _vec.call(); + + return( output ); +} + +// im_c2amph: convert real and imaginary to phase and amplitude +VImage VImage::c2amph() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2amph" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_c2imag: extract imaginary part of complex image +VImage VImage::c2imag() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2imag" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_c2ps: find power spectrum of complex image +VImage VImage::c2ps() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2ps" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_c2real: extract real part of complex image +VImage VImage::c2real() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2real" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_c2rect: convert phase and amplitude to real and imaginary +VImage VImage::c2rect() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_c2rect" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2c: convert to signed 8-bit integer +VImage VImage::clip2c() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2c" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2cm: convert to complex +VImage VImage::clip2cm() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2cm" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2d: convert to double-precision float +VImage VImage::clip2d() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2d" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2dcm: convert to double complex +VImage VImage::clip2dcm() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2dcm" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2f: convert to single-precision float +VImage VImage::clip2f() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2f" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2fmt: convert image format to ofmt +VImage VImage::clip2fmt( int ofmt ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2fmt" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = ofmt; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2i: convert to signed 32-bit integer +VImage VImage::clip2i() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2i" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2s: convert to signed 16-bit integer +VImage VImage::clip2s() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2s" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2ui: convert to unsigned 32-bit integer +VImage VImage::clip2ui() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2ui" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip2us: convert to unsigned 16-bit integer +VImage VImage::clip2us() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip2us" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_clip: convert to unsigned 8-bit integer +VImage VImage::clip() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_clip" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_copy: copy image +VImage VImage::copy() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_copy" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_copy_morph: copy image, setting pixel layout +VImage VImage::copy_morph( int Bands, int BandFmt, int Coding ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_copy_morph" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = Bands; + *((int*) _vec.data(3)) = BandFmt; + *((int*) _vec.data(4)) = Coding; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_copy_swap: copy image, swapping byte order +VImage VImage::copy_swap() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_copy_swap" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_copy_set: copy image, setting informational fields +VImage VImage::copy_set( int Type, double Xres, double Yres, int Xoffset, int Yoffset ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_copy_set" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = Type; + *((double*) _vec.data(3)) = Xres; + *((double*) _vec.data(4)) = Yres; + *((int*) _vec.data(5)) = Xoffset; + *((int*) _vec.data(6)) = Yoffset; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_csv2vips: read a file in csv format +VImage VImage::csv2vips( char* filename ) throw( VError ) +{ + VImage im; + + Vargv _vec( "im_csv2vips" ); + + _vec.data(0) = (im_object) filename; + _vec.data(1) = im.image(); + _vec.call(); + + return( im ); +} + +// im_extract_area: extract area +VImage VImage::extract_area( int left, int top, int width, int height ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract_area" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = left; + *((int*) _vec.data(3)) = top; + *((int*) _vec.data(4)) = width; + *((int*) _vec.data(5)) = height; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_extract_areabands: extract area and bands +VImage VImage::extract_areabands( int left, int top, int width, int height, int band, int nbands ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract_areabands" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = left; + *((int*) _vec.data(3)) = top; + *((int*) _vec.data(4)) = width; + *((int*) _vec.data(5)) = height; + *((int*) _vec.data(6)) = band; + *((int*) _vec.data(7)) = nbands; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_extract_band: extract band +VImage VImage::extract_band( int band ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract_band" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = band; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_extract_bands: extract several bands +VImage VImage::extract_bands( int band, int nbands ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract_bands" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = band; + *((int*) _vec.data(3)) = nbands; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_extract: extract area/band +VImage VImage::extract( int left, int top, int width, int height, int band ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_extract" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = left; + *((int*) _vec.data(3)) = top; + *((int*) _vec.data(4)) = width; + *((int*) _vec.data(5)) = height; + *((int*) _vec.data(6)) = band; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_falsecolour: turn luminance changes into chrominance changes +VImage VImage::falsecolour() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_falsecolour" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_fliphor: flip image left-right +VImage VImage::fliphor() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_fliphor" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_flipver: flip image top-bottom +VImage VImage::flipver() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_flipver" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_gbandjoin: bandwise join of many images +VImage VImage::gbandjoin( std::vector in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_gbandjoin" ); + + ((im_imagevec_object*) _vec.data(0))->n = in.size(); + ((im_imagevec_object*) _vec.data(0))->vec = new IMAGE *[in.size()]; + for( unsigned int i = 0; i < in.size(); i++ ) + ((im_imagevec_object*) _vec.data(0))->vec[i] = in[i].image(); + _vec.data(1) = out.image(); + _vec.call(); + for( unsigned int i = 0; i < in.size(); i++ ) + out._ref->addref( in[i]._ref ); + + return( out ); +} + +// im_grid: chop a tall thin image into a grid of images +VImage VImage::grid( int tile_height, int across, int down ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_grid" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = tile_height; + *((int*) _vec.data(3)) = across; + *((int*) _vec.data(4)) = down; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_insert: insert sub-image into main image at position +VImage VImage::insert( VImage sub, int x, int y ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_insert" ); + + _vec.data(0) = in.image(); + _vec.data(1) = sub.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = x; + *((int*) _vec.data(4)) = y; + _vec.call(); + out._ref->addref( in._ref ); + out._ref->addref( sub._ref ); + + return( out ); +} + +// im_insert_noexpand: insert sub-image into main image at position, no expansion +VImage VImage::insert_noexpand( VImage sub, int x, int y ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_insert_noexpand" ); + + _vec.data(0) = in.image(); + _vec.data(1) = sub.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = x; + *((int*) _vec.data(4)) = y; + _vec.call(); + out._ref->addref( in._ref ); + out._ref->addref( sub._ref ); + + return( out ); +} + +// im_jpeg2vips: convert from jpeg +VImage VImage::jpeg2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_jpeg2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_lrjoin: join two images left-right +VImage VImage::lrjoin( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_lrjoin" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_magick2vips: load file with libMagick +VImage VImage::magick2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_magick2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_mask2vips: convert DOUBLEMASK to VIPS image +VImage VImage::mask2vips( VDMask input ) throw( VError ) +{ + VImage output; + + Vargv _vec( "im_mask2vips" ); + + ((im_mask_object*) _vec.data(0))->mask = input.mask().dptr; + _vec.data(1) = output.image(); + _vec.call(); + + return( output ); +} + +// im_msb: convert to uchar by discarding bits +VImage VImage::msb() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_msb" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_msb_band: convert to single band uchar by discarding bits +VImage VImage::msb_band( int band ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_msb_band" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = band; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_png2vips: convert PNG file to VIPS image +VImage VImage::png2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_png2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_exr2vips: convert an OpenEXR file to VIPS +VImage VImage::exr2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_exr2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_ppm2vips: read a file in pbm/pgm/ppm format +VImage VImage::ppm2vips( char* filename ) throw( VError ) +{ + VImage im; + + Vargv _vec( "im_ppm2vips" ); + + _vec.data(0) = (im_object) filename; + _vec.data(1) = im.image(); + _vec.call(); + + return( im ); +} + +// im_analyze2vips: read a file in analyze format +VImage VImage::analyze2vips( char* filename ) throw( VError ) +{ + VImage im; + + Vargv _vec( "im_analyze2vips" ); + + _vec.data(0) = (im_object) filename; + _vec.data(1) = im.image(); + _vec.call(); + + return( im ); +} + +// im_recomb: linear recombination with mask +VImage VImage::recomb( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_recomb" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_replicate: replicate an image horizontally and vertically +VImage VImage::replicate( int across, int down ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_replicate" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = across; + *((int*) _vec.data(3)) = down; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_ri2c: join two non-complex images to form complex +VImage VImage::ri2c( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_ri2c" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_rot180: rotate image 180 degrees +VImage VImage::rot180() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rot180" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rot270: rotate image 270 degrees clockwise +VImage VImage::rot270() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rot270" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rot90: rotate image 90 degrees clockwise +VImage VImage::rot90() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rot90" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_scale: scale image linearly to fit range 0-255 +VImage VImage::scale() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_scale" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_scaleps: logarithmic scale of image to fit range 0-255 +VImage VImage::scaleps() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_scaleps" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_rightshift_size: decrease size by a power-of-two factor +VImage VImage::rightshift_size( int xshift, int yshift, int band_fmt ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rightshift_size" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = xshift; + *((int*) _vec.data(3)) = yshift; + *((int*) _vec.data(4)) = band_fmt; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_slice: slice an image using two thresholds +VImage VImage::slice( double thresh1, double thresh2 ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_slice" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((double*) _vec.data(2)) = thresh1; + *((double*) _vec.data(3)) = thresh2; + _vec.call(); + + return( output ); +} + +// im_subsample: subsample image by integer factors +VImage VImage::subsample( int xshrink, int yshrink ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_subsample" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = xshrink; + *((int*) _vec.data(3)) = yshrink; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_system: run command on image +char* VImage::system( char* command ) throw( VError ) +{ + VImage im = *this; + char* output; + + Vargv _vec( "im_system" ); + + _vec.data(0) = im.image(); + _vec.data(1) = (im_object) command; + _vec.call(); + output = (char*) _vec.data(2); + + return( output ); +} + +// im_tbjoin: join two images top-bottom +VImage VImage::tbjoin( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_tbjoin" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_text: generate text image +VImage VImage::text( char* text, char* font, int width, int alignment, int dpi ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_text" ); + + _vec.data(0) = out.image(); + _vec.data(1) = (im_object) text; + _vec.data(2) = (im_object) font; + *((int*) _vec.data(3)) = width; + *((int*) _vec.data(4)) = alignment; + *((int*) _vec.data(5)) = dpi; + _vec.call(); + + return( out ); +} + +// im_thresh: slice an image at a threshold +VImage VImage::thresh( double threshold ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_thresh" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((double*) _vec.data(2)) = threshold; + _vec.call(); + + return( output ); +} + +// im_tiff2vips: convert TIFF file to VIPS image +VImage VImage::tiff2vips( char* in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_tiff2vips" ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_vips2csv: write an image in csv format +void VImage::vips2csv( char* filename ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2csv" ); + + _vec.data(0) = in.image(); + _vec.data(1) = (im_object) filename; + _vec.call(); +} + +// im_vips2jpeg: convert to jpeg +void VImage::vips2jpeg( char* out ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2jpeg" ); + + _vec.data(0) = in.image(); + _vec.data(1) = (im_object) out; + _vec.call(); +} + +// im_vips2mask: convert VIPS image to DOUBLEMASK +VDMask VImage::vips2mask() throw( VError ) +{ + VImage input = *this; + VDMask output; + + Vargv _vec( "im_vips2mask" ); + + _vec.data(0) = input.image(); + ((im_mask_object*) _vec.data(1))->name = (char*)"noname"; + _vec.call(); + output.embed( (DOUBLEMASK *)((im_mask_object*)_vec.data(1))->mask ); + + return( output ); +} + +// im_vips2mimejpeg: convert to jpeg as mime type on stdout +void VImage::vips2mimejpeg( int qfac ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2mimejpeg" ); + + _vec.data(0) = in.image(); + *((int*) _vec.data(1)) = qfac; + _vec.call(); +} + +// im_vips2png: convert VIPS image to PNG file +void VImage::vips2png( char* out ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2png" ); + + _vec.data(0) = in.image(); + _vec.data(1) = (im_object) out; + _vec.call(); +} + +// im_vips2ppm: write a file in pbm/pgm/ppm format +void VImage::vips2ppm( char* filename ) throw( VError ) +{ + VImage im = *this; + Vargv _vec( "im_vips2ppm" ); + + _vec.data(0) = im.image(); + _vec.data(1) = (im_object) filename; + _vec.call(); +} + +// im_vips2tiff: convert VIPS image to TIFF file +void VImage::vips2tiff( char* out ) throw( VError ) +{ + VImage in = *this; + Vargv _vec( "im_vips2tiff" ); + + _vec.data(0) = in.image(); + _vec.data(1) = (im_object) out; + _vec.call(); +} + +// im_zoom: simple zoom of an image by integer factors +VImage VImage::zoom( int xfac, int yfac ) throw( VError ) +{ + VImage input = *this; + VImage output; + + Vargv _vec( "im_zoom" ); + + _vec.data(0) = input.image(); + _vec.data(1) = output.image(); + *((int*) _vec.data(2)) = xfac; + *((int*) _vec.data(3)) = yfac; + _vec.call(); + output._ref->addref( input._ref ); + + return( output ); +} + +// im_addgnoise: add gaussian noise with mean 0 and std. dev. sigma +VImage VImage::addgnoise( double sigma ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_addgnoise" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = sigma; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_compass: convolve with 8-way rotating integer mask +VImage VImage::compass( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_compass" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_contrast_surface: find high-contrast points in an image +VImage VImage::contrast_surface( int half_win_size, int spacing ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_contrast_surface" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = half_win_size; + *((int*) _vec.data(3)) = spacing; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_contrast_surface_raw: find high-contrast points in an image +VImage VImage::contrast_surface_raw( int half_win_size, int spacing ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_contrast_surface_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = half_win_size; + *((int*) _vec.data(3)) = spacing; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_conv: convolve +VImage VImage::conv( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_conv" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_conv_raw: convolve, no border +VImage VImage::conv_raw( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_conv_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convf: convolve, with DOUBLEMASK +VImage VImage::convf( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convf" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convf_raw: convolve, with DOUBLEMASK, no border +VImage VImage::convf_raw( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convf_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsep: seperable convolution +VImage VImage::convsep( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsep" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsep_raw: seperable convolution, no border +VImage VImage::convsep_raw( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsep_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsepf: seperable convolution, with DOUBLEMASK +VImage VImage::convsepf( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsepf" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsepf_raw: seperable convolution, with DOUBLEMASK, no border +VImage VImage::convsepf_raw( VDMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsepf_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().dptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_convsub: convolve uchar to uchar, sub-sampling by xskip, yskip +VImage VImage::convsub( VIMask matrix, int xskip, int yskip ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_convsub" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + *((int*) _vec.data(3)) = xskip; + *((int*) _vec.data(4)) = yskip; + _vec.call(); + + return( out ); +} + +// im_embed: embed in within a set of borders +VImage VImage::embed( int type, int x, int y, int w, int h ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_embed" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = type; + *((int*) _vec.data(3)) = x; + *((int*) _vec.data(4)) = y; + *((int*) _vec.data(5)) = w; + *((int*) _vec.data(6)) = h; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_fastcor: fast correlate in2 within in1 +VImage VImage::fastcor( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_fastcor" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_fastcor_raw: fast correlate in2 within in1, no border +VImage VImage::fastcor_raw( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_fastcor_raw" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_gaussnoise: generate image of gaussian noise with specified statistics +VImage VImage::gaussnoise( int xsize, int ysize, double mean, double sigma ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_gaussnoise" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + *((double*) _vec.data(3)) = mean; + *((double*) _vec.data(4)) = sigma; + _vec.call(); + + return( out ); +} + +// im_grad_x: x component of gradient of image +VImage VImage::grad_x() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_grad_x" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_grad_y: y component of gradient of image +VImage VImage::grad_y() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_grad_y" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_gradcor: non-normalised correlation of gradient of in2 within in1 +VImage VImage::gradcor( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_gradcor" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_gradcor_raw: non-normalised correlation of gradient of in2 within in1, no padding +VImage VImage::gradcor_raw( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_gradcor_raw" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_gradient: convolve with 2-way rotating mask +VImage VImage::gradient( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_gradient" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rank_image: point-wise pixel rank +VImage VImage::rank_image( std::vector in, int index ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_rank_image" ); + + ((im_imagevec_object*) _vec.data(0))->n = in.size(); + ((im_imagevec_object*) _vec.data(0))->vec = new IMAGE *[in.size()]; + for( unsigned int i = 0; i < in.size(); i++ ) + ((im_imagevec_object*) _vec.data(0))->vec[i] = in[i].image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = index; + _vec.call(); + for( unsigned int i = 0; i < in.size(); i++ ) + out._ref->addref( in[i]._ref ); + + return( out ); +} + +// im_lindetect: convolve with 4-way rotating mask +VImage VImage::lindetect( VIMask matrix ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lindetect" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = matrix.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_maxvalue: point-wise maximum value +VImage VImage::maxvalue( std::vector in ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_maxvalue" ); + + ((im_imagevec_object*) _vec.data(0))->n = in.size(); + ((im_imagevec_object*) _vec.data(0))->vec = new IMAGE *[in.size()]; + for( unsigned int i = 0; i < in.size(); i++ ) + ((im_imagevec_object*) _vec.data(0))->vec[i] = in[i].image(); + _vec.data(1) = out.image(); + _vec.call(); + for( unsigned int i = 0; i < in.size(); i++ ) + out._ref->addref( in[i]._ref ); + + return( out ); +} + +// im_mpercent: find threshold above which there are percent values +int VImage::mpercent( double percent ) throw( VError ) +{ + VImage in = *this; + int thresh; + + Vargv _vec( "im_mpercent" ); + + _vec.data(0) = in.image(); + *((double*) _vec.data(1)) = percent; + _vec.call(); + thresh = *((int*)_vec.data(2)); + + return( thresh ); +} + +// im_rank: rank filter nth element of xsize/ysize window +VImage VImage::rank( int xsize, int ysize, int n ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rank" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = xsize; + *((int*) _vec.data(3)) = ysize; + *((int*) _vec.data(4)) = n; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_rank_raw: rank filter nth element of xsize/ysize window, no border +VImage VImage::rank_raw( int xsize, int ysize, int n ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rank_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = xsize; + *((int*) _vec.data(3)) = ysize; + *((int*) _vec.data(4)) = n; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_resize_linear: resize to X by Y pixels with linear interpolation +VImage VImage::resize_linear( int X, int Y ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_resize_linear" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = X; + *((int*) _vec.data(3)) = Y; + _vec.call(); + + return( out ); +} + +// im_sharpen: sharpen high frequencies of L channel of LabQ +VImage VImage::sharpen( int mask_size, double x1, double y2, double y3, double m1, double m2 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_sharpen" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = mask_size; + *((double*) _vec.data(3)) = x1; + *((double*) _vec.data(4)) = y2; + *((double*) _vec.data(5)) = y3; + *((double*) _vec.data(6)) = m1; + *((double*) _vec.data(7)) = m2; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_shrink: shrink image by xfac, yfac times +VImage VImage::shrink( double xfac, double yfac ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_shrink" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = xfac; + *((double*) _vec.data(3)) = yfac; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_spcor: normalised correlation of in2 within in1 +VImage VImage::spcor( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_spcor" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_spcor_raw: normalised correlation of in2 within in1, no black padding +VImage VImage::spcor_raw( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_spcor_raw" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_spcor2: normalised correlation of in2 within in1 +VImage VImage::spcor2( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_spcor2" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_spcor2_raw: normalised correlation of in2 within in1, no black padding +VImage VImage::spcor2_raw( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_spcor2_raw" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_stretch3: stretch 3%, sub-pixel displace by xdisp/ydisp +VImage VImage::stretch3( double xdisp, double ydisp ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_stretch3" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = xdisp; + *((double*) _vec.data(3)) = ydisp; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_zerox: find +ve or -ve zero crossings in image +VImage VImage::zerox( int flag ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_zerox" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = flag; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_create_fmask: create frequency domain filter mask +VImage VImage::create_fmask( int width, int height, int type, double p1, double p2, double p3, double p4, double p5 ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_create_fmask" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = width; + *((int*) _vec.data(2)) = height; + *((int*) _vec.data(3)) = type; + *((double*) _vec.data(4)) = p1; + *((double*) _vec.data(5)) = p2; + *((double*) _vec.data(6)) = p3; + *((double*) _vec.data(7)) = p4; + *((double*) _vec.data(8)) = p5; + _vec.call(); + + return( out ); +} + +// im_disp_ps: make displayable power spectrum +VImage VImage::disp_ps() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_disp_ps" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_flt_image_freq: frequency domain filter image +VImage VImage::flt_image_freq( int type, double p1, double p2, double p3, double p4, double p5 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_flt_image_freq" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = type; + *((double*) _vec.data(3)) = p1; + *((double*) _vec.data(4)) = p2; + *((double*) _vec.data(5)) = p3; + *((double*) _vec.data(6)) = p4; + *((double*) _vec.data(7)) = p5; + _vec.call(); + + return( out ); +} + +// im_fractsurf: generate a fractal surface of given dimension +VImage VImage::fractsurf( int size, double dimension ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_fractsurf" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = size; + *((double*) _vec.data(2)) = dimension; + _vec.call(); + + return( out ); +} + +// im_freqflt: frequency-domain filter of in with mask +VImage VImage::freqflt( VImage mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_freqflt" ); + + _vec.data(0) = in.image(); + _vec.data(1) = mask.image(); + _vec.data(2) = out.image(); + _vec.call(); + + return( out ); +} + +// im_fwfft: forward fast-fourier transform +VImage VImage::fwfft() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_fwfft" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_rotquad: rotate image quadrants to move origin to centre +VImage VImage::rotquad() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rotquad" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_invfft: inverse fast-fourier transform +VImage VImage::invfft() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_invfft" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_invfftr: real part of inverse fast-fourier transform +VImage VImage::invfftr() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_invfftr" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// im_gammacorrect: gamma-correct image +VImage VImage::gammacorrect( double exponent ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_gammacorrect" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = exponent; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_heq: histogram-equalise image +VImage VImage::heq( int band_number ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_heq" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = band_number; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_hist: find and graph histogram of image +VImage VImage::hist( int band_number ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_hist" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = band_number; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histcum: turn histogram to cumulative histogram +VImage VImage::histcum() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histcum" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histeq: form histogram equalistion LUT +VImage VImage::histeq() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histeq" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histgr: find histogram of image +VImage VImage::histgr( int band_number ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histgr" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = band_number; + _vec.call(); + + return( out ); +} + +// im_histnD: find 1D, 2D or 3D histogram of image +VImage VImage::histnD( int bins ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histnD" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = bins; + _vec.call(); + + return( out ); +} + +// im_histnorm: form normalised histogram +VImage VImage::histnorm() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histnorm" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histplot: plot graph of histogram +VImage VImage::histplot() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histplot" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_histspec: find histogram which will make pdf of in match ref +VImage VImage::histspec( VImage ref ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_histspec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = ref.image(); + _vec.data(2) = out.image(); + _vec.call(); + + return( out ); +} + +// im_hsp: match stats of in to stats of ref +VImage VImage::hsp( VImage ref ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_hsp" ); + + _vec.data(0) = in.image(); + _vec.data(1) = ref.image(); + _vec.data(2) = out.image(); + _vec.call(); + + return( out ); +} + +// im_identity: generate identity histogram +VImage VImage::identity( int nbands ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_identity" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = nbands; + _vec.call(); + + return( out ); +} + +// im_identity_ushort: generate ushort identity histogram +VImage VImage::identity_ushort( int nbands, int size ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_identity_ushort" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = nbands; + *((int*) _vec.data(2)) = size; + _vec.call(); + + return( out ); +} + +// im_ismonotonic: test LUT for monotonicity +int VImage::ismonotonic() throw( VError ) +{ + VImage lut = *this; + int mono; + + Vargv _vec( "im_ismonotonic" ); + + _vec.data(0) = lut.image(); + _vec.call(); + mono = *((int*)_vec.data(1)); + + return( mono ); +} + +// im_lhisteq: local histogram equalisation +VImage VImage::lhisteq( int width, int height ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lhisteq" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = width; + *((int*) _vec.data(3)) = height; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lhisteq_raw: local histogram equalisation, no border +VImage VImage::lhisteq_raw( int width, int height ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lhisteq_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = width; + *((int*) _vec.data(3)) = height; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_invertlut: generate correction table from set of measures +VImage VImage::invertlut( VDMask measures, int lut_size ) throw( VError ) +{ + VImage lut; + + Vargv _vec( "im_invertlut" ); + + ((im_mask_object*) _vec.data(0))->mask = measures.mask().dptr; + _vec.data(1) = lut.image(); + *((int*) _vec.data(2)) = lut_size; + _vec.call(); + + return( lut ); +} + +// im_buildlut: generate LUT table from set of x/y positions +VImage VImage::buildlut( VDMask xyes ) throw( VError ) +{ + VImage lut; + + Vargv _vec( "im_buildlut" ); + + ((im_mask_object*) _vec.data(0))->mask = xyes.mask().dptr; + _vec.data(1) = lut.image(); + _vec.call(); + + return( lut ); +} + +// im_maplut: map image through LUT +VImage VImage::maplut( VImage lut ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_maplut" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = lut.image(); + _vec.call(); + out._ref->addref( in._ref ); + out._ref->addref( lut._ref ); + + return( out ); +} + +// im_project: find horizontal and vertical projections of an image +VImage VImage::project( VImage& vout ) throw( VError ) +{ + VImage in = *this; + VImage hout; + + Vargv _vec( "im_project" ); + + _vec.data(0) = in.image(); + _vec.data(1) = hout.image(); + _vec.data(2) = vout.image(); + _vec.call(); + + return( hout ); +} + +// im_stdif: statistical differencing +VImage VImage::stdif( double a, double m0, double b, double s0, int xw, int yw ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_stdif" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = m0; + *((double*) _vec.data(4)) = b; + *((double*) _vec.data(5)) = s0; + *((int*) _vec.data(6)) = xw; + *((int*) _vec.data(7)) = yw; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_stdif_raw: statistical differencing, no border +VImage VImage::stdif_raw( double a, double m0, double b, double s0, int xw, int yw ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_stdif_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = m0; + *((double*) _vec.data(4)) = b; + *((double*) _vec.data(5)) = s0; + *((int*) _vec.data(6)) = xw; + *((int*) _vec.data(7)) = yw; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_tone_analyse: analyse in and create LUT for tone adjustment +VImage VImage::tone_analyse( double Ps, double Pm, double Ph, double S, double M, double H ) throw( VError ) +{ + VImage in = *this; + VImage hist; + + Vargv _vec( "im_tone_analyse" ); + + _vec.data(0) = in.image(); + _vec.data(1) = hist.image(); + *((double*) _vec.data(2)) = Ps; + *((double*) _vec.data(3)) = Pm; + *((double*) _vec.data(4)) = Ph; + *((double*) _vec.data(5)) = S; + *((double*) _vec.data(6)) = M; + *((double*) _vec.data(7)) = H; + _vec.call(); + + return( hist ); +} + +// im_tone_build: create LUT for tone adjustment of LabS images +VImage VImage::tone_build( double Lb, double Lw, double Ps, double Pm, double Ph, double S, double M, double H ) throw( VError ) +{ + VImage hist; + + Vargv _vec( "im_tone_build" ); + + _vec.data(0) = hist.image(); + *((double*) _vec.data(1)) = Lb; + *((double*) _vec.data(2)) = Lw; + *((double*) _vec.data(3)) = Ps; + *((double*) _vec.data(4)) = Pm; + *((double*) _vec.data(5)) = Ph; + *((double*) _vec.data(6)) = S; + *((double*) _vec.data(7)) = M; + *((double*) _vec.data(8)) = H; + _vec.call(); + + return( hist ); +} + +// im_tone_build_range: create LUT for tone adjustment +VImage VImage::tone_build_range( int in_max, int out_max, double Lb, double Lw, double Ps, double Pm, double Ph, double S, double M, double H ) throw( VError ) +{ + VImage hist; + + Vargv _vec( "im_tone_build_range" ); + + _vec.data(0) = hist.image(); + *((int*) _vec.data(1)) = in_max; + *((int*) _vec.data(2)) = out_max; + *((double*) _vec.data(3)) = Lb; + *((double*) _vec.data(4)) = Lw; + *((double*) _vec.data(5)) = Ps; + *((double*) _vec.data(6)) = Pm; + *((double*) _vec.data(7)) = Ph; + *((double*) _vec.data(8)) = S; + *((double*) _vec.data(9)) = M; + *((double*) _vec.data(10)) = H; + _vec.call(); + + return( hist ); +} + +// im_tone_map: map L channel of LabS or LabQ image through LUT +VImage VImage::tone_map( VImage lut ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_tone_map" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = lut.image(); + _vec.call(); + out._ref->addref( in._ref ); + out._ref->addref( lut._ref ); + + return( out ); +} + +// im_circle: plot circle on image +void VImage::circle( int cx, int cy, int radius, int intensity ) throw( VError ) +{ + VImage image = *this; + Vargv _vec( "im_circle" ); + + _vec.data(0) = image.image(); + *((int*) _vec.data(1)) = cx; + *((int*) _vec.data(2)) = cy; + *((int*) _vec.data(3)) = radius; + *((int*) _vec.data(4)) = intensity; + _vec.call(); +} + +// im_flood_blob_copy: flood with ink from start_x, start_y while pixel == start pixel +VImage VImage::flood_blob_copy( int start_x, int start_y, std::vector ink ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_flood_blob_copy" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = start_x; + *((int*) _vec.data(3)) = start_y; + ((im_doublevec_object*) _vec.data(4))->n = ink.size(); + ((im_doublevec_object*) _vec.data(4))->vec = new double[ink.size()]; + for( unsigned int i = 0; i < ink.size(); i++ ) + ((im_doublevec_object*) _vec.data(4))->vec[i] = ink[i]; + _vec.call(); + + return( out ); +} + +// im_insertplace: draw image sub inside image main at position (x,y) +void VImage::insertplace( VImage sub, int x, int y ) throw( VError ) +{ + VImage main = *this; + Vargv _vec( "im_insertplace" ); + + _vec.data(0) = main.image(); + _vec.data(1) = sub.image(); + *((int*) _vec.data(2)) = x; + *((int*) _vec.data(3)) = y; + _vec.call(); +} + +// im_line: draw line between points (x1,y1) and (x2,y2) +void VImage::line( int x1, int y1, int x2, int y2, int pelval ) throw( VError ) +{ + VImage im = *this; + Vargv _vec( "im_line" ); + + _vec.data(0) = im.image(); + *((int*) _vec.data(1)) = x1; + *((int*) _vec.data(2)) = y1; + *((int*) _vec.data(3)) = x2; + *((int*) _vec.data(4)) = y2; + *((int*) _vec.data(5)) = pelval; + _vec.call(); +} + +// im_lineset: draw line between points (x1,y1) and (x2,y2) +VImage VImage::lineset( VImage mask, VImage ink, std::vector x1, std::vector y1, std::vector x2, std::vector y2 ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lineset" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = mask.image(); + _vec.data(3) = ink.image(); + ((im_intvec_object*) _vec.data(4))->n = x1.size(); + ((im_intvec_object*) _vec.data(4))->vec = new int[x1.size()]; + for( unsigned int i = 0; i < x1.size(); i++ ) + ((im_intvec_object*) _vec.data(4))->vec[i] = x1[i]; + ((im_intvec_object*) _vec.data(5))->n = y1.size(); + ((im_intvec_object*) _vec.data(5))->vec = new int[y1.size()]; + for( unsigned int i = 0; i < y1.size(); i++ ) + ((im_intvec_object*) _vec.data(5))->vec[i] = y1[i]; + ((im_intvec_object*) _vec.data(6))->n = x2.size(); + ((im_intvec_object*) _vec.data(6))->vec = new int[x2.size()]; + for( unsigned int i = 0; i < x2.size(); i++ ) + ((im_intvec_object*) _vec.data(6))->vec[i] = x2[i]; + ((im_intvec_object*) _vec.data(7))->n = y2.size(); + ((im_intvec_object*) _vec.data(7))->vec = new int[y2.size()]; + for( unsigned int i = 0; i < y2.size(); i++ ) + ((im_intvec_object*) _vec.data(7))->vec[i] = y2[i]; + _vec.call(); + + return( out ); +} + +// im_binfile: open a headerless binary file +VImage VImage::binfile( char* filename, int width, int height, int bands, int offset ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_binfile" ); + + _vec.data(0) = (im_object) filename; + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = width; + *((int*) _vec.data(3)) = height; + *((int*) _vec.data(4)) = bands; + *((int*) _vec.data(5)) = offset; + _vec.call(); + + return( out ); +} + +// im_cache: cache results of an operation +VImage VImage::cache( int tile_width, int tile_height, int max_tiles ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_cache" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = tile_width; + *((int*) _vec.data(3)) = tile_height; + *((int*) _vec.data(4)) = max_tiles; + _vec.call(); + + return( out ); +} + +// im_header_get_type: return field type +int VImage::header_get_type( char* field ) throw( VError ) +{ + VImage image = *this; + int gtype; + + Vargv _vec( "im_header_get_type" ); + + _vec.data(0) = (im_object) field; + _vec.data(1) = image.image(); + _vec.call(); + gtype = *((int*)_vec.data(2)); + + return( gtype ); +} + +// im_header_int: extract int fields from header +int VImage::header_int( char* field ) throw( VError ) +{ + VImage image = *this; + int value; + + Vargv _vec( "im_header_int" ); + + _vec.data(0) = (im_object) field; + _vec.data(1) = image.image(); + _vec.call(); + value = *((int*)_vec.data(2)); + + return( value ); +} + +// im_header_double: extract double fields from header +double VImage::header_double( char* field ) throw( VError ) +{ + VImage image = *this; + double value; + + Vargv _vec( "im_header_double" ); + + _vec.data(0) = (im_object) field; + _vec.data(1) = image.image(); + _vec.call(); + value = *((double*)_vec.data(2)); + + return( value ); +} + +// im_header_string: extract string fields from header +char* VImage::header_string( char* field ) throw( VError ) +{ + VImage image = *this; + char* value; + + Vargv _vec( "im_header_string" ); + + _vec.data(0) = (im_object) field; + _vec.data(1) = image.image(); + _vec.call(); + value = (char*) _vec.data(2); + + return( value ); +} + +// im_cntlines: count horizontal or vertical lines +double VImage::cntlines( int direction ) throw( VError ) +{ + VImage in = *this; + double nlines; + + Vargv _vec( "im_cntlines" ); + + _vec.data(0) = in.image(); + *((int*) _vec.data(2)) = direction; + _vec.call(); + nlines = *((double*)_vec.data(1)); + + return( nlines ); +} + +// im_dilate: dilate image with mask, adding a black border +VImage VImage::dilate( VIMask mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_dilate" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = mask.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_dilate_raw: dilate image with mask +VImage VImage::dilate_raw( VIMask mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_dilate_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = mask.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_erode: erode image with mask, adding a black border +VImage VImage::erode( VIMask mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_erode" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = mask.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_erode_raw: erode image with mask +VImage VImage::erode_raw( VIMask mask ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_erode_raw" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_mask_object*) _vec.data(2))->mask = mask.mask().iptr; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_profile: find first horizontal/vertical edge +VImage VImage::profile( int direction ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_profile" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = direction; + _vec.call(); + + return( out ); +} + +// im_affine: affine transform +VImage VImage::affine( double a, double b, double c, double d, double dx, double dy, int x, int y, int w, int h ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_affine" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = b; + *((double*) _vec.data(4)) = c; + *((double*) _vec.data(5)) = d; + *((double*) _vec.data(6)) = dx; + *((double*) _vec.data(7)) = dy; + *((int*) _vec.data(8)) = x; + *((int*) _vec.data(9)) = y; + *((int*) _vec.data(10)) = w; + *((int*) _vec.data(11)) = h; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_correl: search area around sec for match for area around ref +double VImage::correl( VImage sec, int xref, int yref, int xsec, int ysec, int hwindowsize, int hsearchsize, int& x, int& y ) throw( VError ) +{ + VImage ref = *this; + double correlation; + + Vargv _vec( "im_correl" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + *((int*) _vec.data(2)) = xref; + *((int*) _vec.data(3)) = yref; + *((int*) _vec.data(4)) = xsec; + *((int*) _vec.data(5)) = ysec; + *((int*) _vec.data(6)) = hwindowsize; + *((int*) _vec.data(7)) = hsearchsize; + _vec.call(); + correlation = *((double*)_vec.data(8)); + x = *((int*)_vec.data(9)); + y = *((int*)_vec.data(10)); + + return( correlation ); +} + +// im__find_lroverlap: search for left-right overlap of ref and sec +int VImage::_find_lroverlap( VImage sec, int bandno, int xr, int yr, int xs, int ys, int halfcorrelation, int halfarea, int& dy0, double& scale1, double& angle1, double& dx1, double& dy1 ) throw( VError ) +{ + VImage ref = *this; + int dx0; + + Vargv _vec( "im__find_lroverlap" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + *((int*) _vec.data(2)) = bandno; + *((int*) _vec.data(3)) = xr; + *((int*) _vec.data(4)) = yr; + *((int*) _vec.data(5)) = xs; + *((int*) _vec.data(6)) = ys; + *((int*) _vec.data(7)) = halfcorrelation; + *((int*) _vec.data(8)) = halfarea; + _vec.call(); + dx0 = *((int*)_vec.data(9)); + dy0 = *((int*)_vec.data(10)); + scale1 = *((double*)_vec.data(11)); + angle1 = *((double*)_vec.data(12)); + dx1 = *((double*)_vec.data(13)); + dy1 = *((double*)_vec.data(14)); + + return( dx0 ); +} + +// im__find_tboverlap: search for top-bottom overlap of ref and sec +int VImage::_find_tboverlap( VImage sec, int bandno, int xr, int yr, int xs, int ys, int halfcorrelation, int halfarea, int& dy0, double& scale1, double& angle1, double& dx1, double& dy1 ) throw( VError ) +{ + VImage ref = *this; + int dx0; + + Vargv _vec( "im__find_tboverlap" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + *((int*) _vec.data(2)) = bandno; + *((int*) _vec.data(3)) = xr; + *((int*) _vec.data(4)) = yr; + *((int*) _vec.data(5)) = xs; + *((int*) _vec.data(6)) = ys; + *((int*) _vec.data(7)) = halfcorrelation; + *((int*) _vec.data(8)) = halfarea; + _vec.call(); + dx0 = *((int*)_vec.data(9)); + dy0 = *((int*)_vec.data(10)); + scale1 = *((double*)_vec.data(11)); + angle1 = *((double*)_vec.data(12)); + dx1 = *((double*)_vec.data(13)); + dy1 = *((double*)_vec.data(14)); + + return( dx0 ); +} + +// im_global_balance: automatically rebuild mosaic with balancing +VImage VImage::global_balance( double gamma ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_global_balance" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = gamma; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_global_balancef: automatically rebuild mosaic with balancing, float output +VImage VImage::global_balancef( double gamma ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_global_balancef" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = gamma; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lrmerge: left-right merge of in1 and in2 +VImage VImage::lrmerge( VImage sec, int dx, int dy, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_lrmerge" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = dx; + *((int*) _vec.data(4)) = dy; + *((int*) _vec.data(5)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_lrmerge1: first-order left-right merge of ref and sec +VImage VImage::lrmerge1( VImage sec, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_lrmerge1" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = xr1; + *((int*) _vec.data(4)) = yr1; + *((int*) _vec.data(5)) = xs1; + *((int*) _vec.data(6)) = ys1; + *((int*) _vec.data(7)) = xr2; + *((int*) _vec.data(8)) = yr2; + *((int*) _vec.data(9)) = xs2; + *((int*) _vec.data(10)) = ys2; + *((int*) _vec.data(11)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_lrmosaic: left-right mosaic of ref and sec +VImage VImage::lrmosaic( VImage sec, int bandno, int xr, int yr, int xs, int ys, int halfcorrelation, int halfarea, int balancetype, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_lrmosaic" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = bandno; + *((int*) _vec.data(4)) = xr; + *((int*) _vec.data(5)) = yr; + *((int*) _vec.data(6)) = xs; + *((int*) _vec.data(7)) = ys; + *((int*) _vec.data(8)) = halfcorrelation; + *((int*) _vec.data(9)) = halfarea; + *((int*) _vec.data(10)) = balancetype; + *((int*) _vec.data(11)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_lrmosaic1: first-order left-right mosaic of ref and sec +VImage VImage::lrmosaic1( VImage sec, int bandno, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int halfcorrelation, int halfarea, int balancetype, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_lrmosaic1" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = bandno; + *((int*) _vec.data(4)) = xr1; + *((int*) _vec.data(5)) = yr1; + *((int*) _vec.data(6)) = xs1; + *((int*) _vec.data(7)) = ys1; + *((int*) _vec.data(8)) = xr2; + *((int*) _vec.data(9)) = yr2; + *((int*) _vec.data(10)) = xs2; + *((int*) _vec.data(11)) = ys2; + *((int*) _vec.data(12)) = halfcorrelation; + *((int*) _vec.data(13)) = halfarea; + *((int*) _vec.data(14)) = balancetype; + *((int*) _vec.data(15)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_match_linear: resample ref so that tie-points match +VImage VImage::match_linear( VImage sec, int xref1, int yref1, int xsec1, int ysec1, int xref2, int yref2, int xsec2, int ysec2 ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_match_linear" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = xref1; + *((int*) _vec.data(4)) = yref1; + *((int*) _vec.data(5)) = xsec1; + *((int*) _vec.data(6)) = ysec1; + *((int*) _vec.data(7)) = xref2; + *((int*) _vec.data(8)) = yref2; + *((int*) _vec.data(9)) = xsec2; + *((int*) _vec.data(10)) = ysec2; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_match_linear_search: search sec, then resample so that tie-points match +VImage VImage::match_linear_search( VImage sec, int xref1, int yref1, int xsec1, int ysec1, int xref2, int yref2, int xsec2, int ysec2, int hwindowsize, int hsearchsize ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_match_linear_search" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = xref1; + *((int*) _vec.data(4)) = yref1; + *((int*) _vec.data(5)) = xsec1; + *((int*) _vec.data(6)) = ysec1; + *((int*) _vec.data(7)) = xref2; + *((int*) _vec.data(8)) = yref2; + *((int*) _vec.data(9)) = xsec2; + *((int*) _vec.data(10)) = ysec2; + *((int*) _vec.data(11)) = hwindowsize; + *((int*) _vec.data(12)) = hsearchsize; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_remosaic: automatically rebuild mosaic with new files +VImage VImage::remosaic( char* old_str, char* new_str ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_remosaic" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.data(2) = (im_object) old_str; + _vec.data(3) = (im_object) new_str; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_similarity_area: output area xywh of similarity transformation +VImage VImage::similarity_area( double a, double b, double dx, double dy, int x, int y, int w, int h ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_similarity_area" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = b; + *((double*) _vec.data(4)) = dx; + *((double*) _vec.data(5)) = dy; + *((int*) _vec.data(6)) = x; + *((int*) _vec.data(7)) = y; + *((int*) _vec.data(8)) = w; + *((int*) _vec.data(9)) = h; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_similarity: similarity transformation +VImage VImage::similarity( double a, double b, double dx, double dy ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_similarity" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = a; + *((double*) _vec.data(3)) = b; + *((double*) _vec.data(4)) = dx; + *((double*) _vec.data(5)) = dy; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_tbmerge: top-bottom merge of in1 and in2 +VImage VImage::tbmerge( VImage sec, int dx, int dy, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_tbmerge" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = dx; + *((int*) _vec.data(4)) = dy; + *((int*) _vec.data(5)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_tbmerge1: first-order top-bottom merge of in1 and in2 +VImage VImage::tbmerge1( VImage sec, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_tbmerge1" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = xr1; + *((int*) _vec.data(4)) = yr1; + *((int*) _vec.data(5)) = xs1; + *((int*) _vec.data(6)) = ys1; + *((int*) _vec.data(7)) = xr2; + *((int*) _vec.data(8)) = yr2; + *((int*) _vec.data(9)) = xs2; + *((int*) _vec.data(10)) = ys2; + *((int*) _vec.data(11)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_tbmosaic: top-bottom mosaic of in1 and in2 +VImage VImage::tbmosaic( VImage sec, int bandno, int xr, int yr, int xs, int ys, int halfcorrelation, int halfarea, int balancetype, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_tbmosaic" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = bandno; + *((int*) _vec.data(4)) = xr; + *((int*) _vec.data(5)) = yr; + *((int*) _vec.data(6)) = xs; + *((int*) _vec.data(7)) = ys; + *((int*) _vec.data(8)) = halfcorrelation; + *((int*) _vec.data(9)) = halfarea; + *((int*) _vec.data(10)) = balancetype; + *((int*) _vec.data(11)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_tbmosaic1: first-order top-bottom mosaic of ref and sec +VImage VImage::tbmosaic1( VImage sec, int bandno, int xr1, int yr1, int xs1, int ys1, int xr2, int yr2, int xs2, int ys2, int halfcorrelation, int halfarea, int balancetype, int mwidth ) throw( VError ) +{ + VImage ref = *this; + VImage out; + + Vargv _vec( "im_tbmosaic1" ); + + _vec.data(0) = ref.image(); + _vec.data(1) = sec.image(); + _vec.data(2) = out.image(); + *((int*) _vec.data(3)) = bandno; + *((int*) _vec.data(4)) = xr1; + *((int*) _vec.data(5)) = yr1; + *((int*) _vec.data(6)) = xs1; + *((int*) _vec.data(7)) = ys1; + *((int*) _vec.data(8)) = xr2; + *((int*) _vec.data(9)) = yr2; + *((int*) _vec.data(10)) = xs2; + *((int*) _vec.data(11)) = ys2; + *((int*) _vec.data(12)) = halfcorrelation; + *((int*) _vec.data(13)) = halfarea; + *((int*) _vec.data(14)) = balancetype; + *((int*) _vec.data(15)) = mwidth; + _vec.call(); + out._ref->addref( ref._ref ); + out._ref->addref( sec._ref ); + + return( out ); +} + +// im_benchmark: do something complicated for testing +VImage VImage::benchmark() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_benchmark" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_benchmark2: do something complicated for testing +double VImage::benchmark2() throw( VError ) +{ + VImage in = *this; + double value; + + Vargv _vec( "im_benchmark2" ); + + _vec.data(0) = in.image(); + _vec.call(); + value = *((double*)_vec.data(1)); + + return( value ); +} + +// im_benchmarkn: do something complicated for testing +VImage VImage::benchmarkn( int n ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_benchmarkn" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = n; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_eye: generate IM_BANDFMT_UCHAR [0,255] frequency/amplitude image +VImage VImage::eye( int xsize, int ysize, double factor ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_eye" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + *((double*) _vec.data(3)) = factor; + _vec.call(); + + return( out ); +} + +// im_grey: generate IM_BANDFMT_UCHAR [0,255] grey scale image +VImage VImage::grey( int xsize, int ysize ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_grey" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + _vec.call(); + + return( out ); +} + +// im_feye: generate IM_BANDFMT_FLOAT [-1,1] frequency/amplitude image +VImage VImage::feye( int xsize, int ysize, double factor ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_feye" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + *((double*) _vec.data(3)) = factor; + _vec.call(); + + return( out ); +} + +// im_fgrey: generate IM_BANDFMT_FLOAT [0,1] grey scale image +VImage VImage::fgrey( int xsize, int ysize ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_fgrey" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + _vec.call(); + + return( out ); +} + +// im_fzone: generate IM_BANDFMT_FLOAT [-1,1] zone plate image +VImage VImage::fzone( int size ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_fzone" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = size; + _vec.call(); + + return( out ); +} + +// im_make_xy: generate image with pixel value equal to coordinate +VImage VImage::make_xy( int xsize, int ysize ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_make_xy" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = xsize; + *((int*) _vec.data(2)) = ysize; + _vec.call(); + + return( out ); +} + +// im_zone: generate IM_BANDFMT_UCHAR [0,255] zone plate image +VImage VImage::zone( int size ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_zone" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = size; + _vec.call(); + + return( out ); +} + +// im_blend: use cond image to blend between images in1 and in2 +VImage VImage::blend( VImage in1, VImage in2 ) throw( VError ) +{ + VImage cond = *this; + VImage out; + + Vargv _vec( "im_blend" ); + + _vec.data(0) = cond.image(); + _vec.data(1) = in1.image(); + _vec.data(2) = in2.image(); + _vec.data(3) = out.image(); + _vec.call(); + out._ref->addref( cond._ref ); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_equal: two images equal in value +VImage VImage::equal( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_equal" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_equal_vec: image equals doublevec +VImage VImage::equal( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_equal_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_equalconst: image equals const +VImage VImage::equal( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_equalconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_ifthenelse: use cond image to choose pels from image in1 or in2 +VImage VImage::ifthenelse( VImage in1, VImage in2 ) throw( VError ) +{ + VImage cond = *this; + VImage out; + + Vargv _vec( "im_ifthenelse" ); + + _vec.data(0) = cond.image(); + _vec.data(1) = in1.image(); + _vec.data(2) = in2.image(); + _vec.data(3) = out.image(); + _vec.call(); + out._ref->addref( cond._ref ); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_less: in1 less than in2 in value +VImage VImage::less( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_less" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_less_vec: in less than doublevec +VImage VImage::less( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_less_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lessconst: in less than const +VImage VImage::less( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lessconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lesseq: in1 less than or equal to in2 in value +VImage VImage::lesseq( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_lesseq" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_lesseq_vec: in less than or equal to doublevec +VImage VImage::lesseq( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lesseq_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_lesseqconst: in less than or equal to const +VImage VImage::lesseq( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_lesseqconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_more: in1 more than in2 in value +VImage VImage::more( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_more" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_more_vec: in more than doublevec +VImage VImage::more( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_more_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_moreconst: in more than const +VImage VImage::more( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_moreconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_moreeq: in1 more than or equal to in2 in value +VImage VImage::moreeq( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_moreeq" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_moreeq_vec: in more than or equal to doublevec +VImage VImage::moreeq( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_moreeq_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_moreeqconst: in more than or equal to const +VImage VImage::moreeq( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_moreeqconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_notequal: two images not equal in value +VImage VImage::notequal( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_notequal" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + out._ref->addref( in1._ref ); + out._ref->addref( in2._ref ); + + return( out ); +} + +// im_notequal_vec: image does not equal doublevec +VImage VImage::notequal( std::vector vec ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_notequal_vec" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + ((im_doublevec_object*) _vec.data(2))->n = vec.size(); + ((im_doublevec_object*) _vec.data(2))->vec = new double[vec.size()]; + for( unsigned int i = 0; i < vec.size(); i++ ) + ((im_doublevec_object*) _vec.data(2))->vec[i] = vec[i]; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_notequalconst: image does not equal const +VImage VImage::notequal( double c ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_notequalconst" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((double*) _vec.data(2)) = c; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// im_video_test: test video grabber +VImage VImage::video_test( int brightness, int error ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_video_test" ); + + _vec.data(0) = out.image(); + *((int*) _vec.data(1)) = brightness; + *((int*) _vec.data(2)) = error; + _vec.call(); + + return( out ); +} + +// im_video_v4l1: grab a video frame with v4l1 +VImage VImage::video_v4l1( char* device, int channel, int brightness, int colour, int contrast, int hue, int ngrabs ) throw( VError ) +{ + VImage out; + + Vargv _vec( "im_video_v4l1" ); + + _vec.data(0) = out.image(); + _vec.data(1) = (im_object) device; + *((int*) _vec.data(2)) = channel; + *((int*) _vec.data(3)) = brightness; + *((int*) _vec.data(4)) = colour; + *((int*) _vec.data(5)) = contrast; + *((int*) _vec.data(6)) = hue; + *((int*) _vec.data(7)) = ngrabs; + _vec.call(); + + return( out ); +} + diff --git a/po/ChangeLog b/po/ChangeLog new file mode 100644 index 00000000..dbf75fcd --- /dev/null +++ b/po/ChangeLog @@ -0,0 +1 @@ +started 17 dec 03 diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 00000000..ad372f35 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,318 @@ +src/mosaicing/find_mosaic.c +src/mosaicing/mergeup.c +src/other/spatres.c +src/other/squares.c +src/other/simcontr.c +src/other/glds_features.c +src/other/glds.c +src/other/sines.c +src/other/cooc.c +src/other/cooc_features.c +src/iofuncs/binfile.c +src/iofuncs/vips.c +src/iofuncs/debugim.c +src/iofuncs/header.c +src/iofuncs/edvips.c +src/iofuncs/printlines.c +contrib/vdump/vdump.c +contrib/vips2dj/vips2ah.c +contrib/vips2dj/vips2dj.c +contrib/mitsub/mitsub.c +libsrc/histograms_lut/im_invertlut.c +libsrc/histograms_lut/hist_dispatch.c +libsrc/histograms_lut/im_project.c +libsrc/histograms_lut/im_hist.c +libsrc/histograms_lut/im_histspec.c +libsrc/histograms_lut/im_stdif.c +libsrc/histograms_lut/im_maplut.c +libsrc/histograms_lut/im_histgr.c +libsrc/histograms_lut/im_identity.c +libsrc/histograms_lut/im_histnD.c +libsrc/histograms_lut/tone.c +libsrc/histograms_lut/im_histplot.c +libsrc/histograms_lut/im_gammacorrect.c +libsrc/histograms_lut/im_heq.c +libsrc/histograms_lut/im_histeq.c +libsrc/histograms_lut/im_lhisteq.c +libsrc/histograms_lut/im_buildlut.c +libsrc/histograms_lut/im_hsp.c +libsrc/mosaicing/im_tbmerge.c +libsrc/mosaicing/im_affine.c +libsrc/mosaicing/im_lrmosaic.c +libsrc/mosaicing/im_chkpair.c +libsrc/mosaicing/global_balance.c +libsrc/mosaicing/im_tbmosaic.c +libsrc/mosaicing/im_improve.c +libsrc/mosaicing/im_avgdxdy.c +libsrc/mosaicing/mosaic1.c +libsrc/mosaicing/im_remosaic.c +libsrc/mosaicing/mosaicing_dispatch.c +libsrc/mosaicing/im_lrcalcon.c +libsrc/mosaicing/im_lrmerge.c +libsrc/mosaicing/im_initialize.c +libsrc/mosaicing/similarity.c +libsrc/mosaicing/im_tbcalcon.c +libsrc/mosaicing/match.c +libsrc/mosaicing/im_clinear.c +libsrc/matrix/im_mattrn.c +libsrc/matrix/im_matcat.c +libsrc/matrix/matalloc.c +libsrc/matrix/matrix_dispatch.c +libsrc/matrix/im_matinv.c +libsrc/matrix/im_matmul.c +libsrc/convolution/im_contrast_surface.c +libsrc/convolution/im_zerox.c +libsrc/convolution/im_rank_image.c +libsrc/convolution/im_compass.c +libsrc/convolution/rw_mask.c +libsrc/convolution/im_resize_linear.c +libsrc/convolution/im_sharpen.c +libsrc/convolution/im_spcor.c +libsrc/convolution/im_shrink.c +libsrc/convolution/im_gaussnoise.c +libsrc/convolution/rotmask.c +libsrc/convolution/im_gradcor.c +libsrc/convolution/im_rank.c +libsrc/convolution/im_mpercent.c +libsrc/convolution/im_convsepf.c +libsrc/convolution/convol_dispatch.c +libsrc/convolution/im_embed.c +libsrc/convolution/im_stretch3.c +libsrc/convolution/im_convsub.c +libsrc/convolution/im_convsep.c +libsrc/convolution/im_convf.c +libsrc/convolution/im_gaussmasks.c +libsrc/convolution/im_fastcor.c +libsrc/convolution/im_conv.c +libsrc/convolution/im_logmasks.c +libsrc/convolution/im_addgnoise.c +libsrc/acquire/im_clamp.c +libsrc/other/im_simcontr.c +libsrc/other/im_zone.c +libsrc/other/im_spatres.c +libsrc/other/cooc_funcs.c +libsrc/other/im_benchmark.c +libsrc/other/other_dispatch.c +libsrc/other/glds_funcs.c +libsrc/other/im_meanstd.c +libsrc/other/im_dif_std.c +libsrc/other/im_make_xy.c +libsrc/other/im_eye.c +libsrc/other/im_sines.c +libsrc/other/im_grey.c +libsrc/iofuncs/im_setbox.c +libsrc/iofuncs/rect.c +libsrc/iofuncs/im_open.c +libsrc/iofuncs/im_writeline.c +libsrc/iofuncs/util.c +libsrc/iofuncs/im_iocheck.c +libsrc/iofuncs/im_mapfile.c +libsrc/iofuncs/package.c +libsrc/iofuncs/vbuf.c +libsrc/iofuncs/window.c +libsrc/iofuncs/im_wrapmany.c +libsrc/iofuncs/im_setbuf.c +libsrc/iofuncs/meta.c +libsrc/iofuncs/im_desc_hd.c +libsrc/iofuncs/im_prepare.c +libsrc/iofuncs/im_openin.c +libsrc/iofuncs/im_init_world.c +libsrc/iofuncs/im_readhist.c +libsrc/iofuncs/debug.c +libsrc/iofuncs/callback.c +libsrc/iofuncs/im_iterate.c +libsrc/iofuncs/im_cp_desc.c +libsrc/iofuncs/im_unmapfile.c +libsrc/iofuncs/im_close.c +libsrc/iofuncs/im_image.c +libsrc/iofuncs/im_init.c +libsrc/iofuncs/memory.c +libsrc/iofuncs/buffer.c +libsrc/iofuncs/im_render.c +libsrc/iofuncs/time.c +libsrc/iofuncs/im_openout.c +libsrc/iofuncs/im_generate.c +libsrc/iofuncs/im_histlin.c +libsrc/iofuncs/im_header.c +libsrc/iofuncs/threadgroup.c +libsrc/iofuncs/im_guess_prefix.c +libsrc/iofuncs/im_demand_hint.c +libsrc/iofuncs/error.c +libsrc/iofuncs/im_makerw.c +libsrc/iofuncs/im_printdesc.c +libsrc/iofuncs/im_piocheck.c +libsrc/iofuncs/base64.c +libsrc/iofuncs/predicate.c +libsrc/iofuncs/semaphore.c +libsrc/iofuncs/im_binfile.c +libsrc/iofuncs/im_debugim.c +libsrc/iofuncs/im_printlines.c +libsrc/iofuncs/im_partial.c +libsrc/iofuncs/im_setupout.c +libsrc/iofuncs/im_updatehist.c +libsrc/iofuncs/im_bits_of_fmt.c +libsrc/iofuncs/im_initdesc.c +libsrc/iofuncs/error_exit.c +libsrc/iofuncs/dispatch_types.c +libsrc/iofuncs/region.c +libsrc/iofuncs/im_wrapone.c +libsrc/morphology/im_profile.c +libsrc/morphology/morph_dispatch.c +libsrc/morphology/im_dilate.c +libsrc/morphology/im_cntlines.c +libsrc/morphology/im_erode.c +libsrc/video/video_dispatch.c +libsrc/video/im_video_v4l1.c +libsrc/video/im_video_test.c +libsrc/dummy.c +libsrc/relational/im_ifthenelse.c +libsrc/relational/relational_dispatch.c +libsrc/relational/relational.c +libsrc/relational/im_blend.c +libsrc/arithmetic/im_cmulnorm.c +libsrc/arithmetic/im_litecor.c +libsrc/arithmetic/im_logtra.c +libsrc/arithmetic/im_gadd.c +libsrc/arithmetic/im_sign.c +libsrc/arithmetic/im_minpos.c +libsrc/arithmetic/im_maxpos_avg.c +libsrc/arithmetic/im_subtract.c +libsrc/arithmetic/im_max.c +libsrc/arithmetic/im_remainder.c +libsrc/arithmetic/im_gfadd.c +libsrc/arithmetic/im_multiply.c +libsrc/arithmetic/im_linreg.c +libsrc/arithmetic/im_gaddim.c +libsrc/arithmetic/im_abs.c +libsrc/arithmetic/im_ceil.c +libsrc/arithmetic/im_avg.c +libsrc/arithmetic/im_bandmean.c +libsrc/arithmetic/im_add.c +libsrc/arithmetic/im_min.c +libsrc/arithmetic/im_deviate.c +libsrc/arithmetic/im_stats.c +libsrc/arithmetic/im_maxpos_vec.c +libsrc/arithmetic/im_lintra.c +libsrc/arithmetic/im_measure.c +libsrc/arithmetic/im_log10tra.c +libsrc/arithmetic/im_powtra.c +libsrc/arithmetic/im_costra.c +libsrc/arithmetic/im_maxpos.c +libsrc/arithmetic/im_invert.c +libsrc/arithmetic/im_rint.c +libsrc/arithmetic/im_sintra.c +libsrc/arithmetic/arith_dispatch.c +libsrc/arithmetic/im_point_bilinear.c +libsrc/arithmetic/im_expntra.c +libsrc/arithmetic/im_tantra.c +libsrc/arithmetic/im_divide.c +libsrc/arithmetic/im_fav4.c +libsrc/arithmetic/im_floor.c +libsrc/boolean/boolean.c +libsrc/boolean/bool_dispatch.c +libsrc/inplace/im_flood.c +libsrc/inplace/inplace_dispatch.c +libsrc/inplace/im_paintrect.c +libsrc/inplace/smudge_area.c +libsrc/inplace/im_insertplace.c +libsrc/inplace/im_plotmask.c +libsrc/inplace/plot_point.c +libsrc/inplace/im_circle.c +libsrc/inplace/line_draw.c +libsrc/inplace/im_line.c +libsrc/colour/im_dE_fromLab.c +libsrc/colour/im_Lab2LCh.c +libsrc/colour/im_dECMC_fromLab.c +libsrc/colour/im_XYZ2Yxy.c +libsrc/colour/derived.c +libsrc/colour/im_disp2XYZ.c +libsrc/colour/im_LabQ2Lab.c +libsrc/colour/im_Lab2LabQ.c +libsrc/colour/im_XYZ2disp.c +libsrc/colour/im_lab_morph.c +libsrc/colour/im_dE00_fromLab.c +libsrc/colour/im_Lab2XYZ.c +libsrc/colour/colour_dispatch.c +libsrc/colour/colour.c +libsrc/colour/im_LabQ2disp.c +libsrc/colour/im_Yxy2XYZ.c +libsrc/colour/im_XYZ2Lab.c +libsrc/colour/im_LCh2Lab.c +libsrc/colour/im_LabS2LabQ.c +libsrc/colour/im_LabQ2LabS.c +libsrc/colour/im_UCS2LCh.c +libsrc/colour/im_Lab2LabS.c +libsrc/colour/im_icc_transform.c +libsrc/colour/im_LCh2UCS.c +libsrc/colour/im_LabS2Lab.c +libsrc/conversion/im_recomb.c +libsrc/conversion/im_tbjoin.c +libsrc/conversion/im_flipver.c +libsrc/conversion/im_magick2vips.c +libsrc/conversion/im_c2rect.c +libsrc/conversion/im_rot90.c +libsrc/conversion/im_vips2csv.c +libsrc/conversion/im_c2ps.c +libsrc/conversion/im_rightshift_size.c +libsrc/conversion/im_csv2vips.c +libsrc/conversion/im_insert.c +libsrc/conversion/im_c2imag.c +libsrc/conversion/im_falsecolour.c +libsrc/conversion/im_text.c +libsrc/conversion/conver_dispatch.c +libsrc/conversion/im_bandjoin.c +libsrc/conversion/im_analyze2vips.c +libsrc/conversion/im_vips2tiff.c +libsrc/conversion/im_slice.c +libsrc/conversion/im_rot180.c +libsrc/conversion/im_scale.c +libsrc/conversion/im_black.c +libsrc/conversion/im_ppm2vips.c +libsrc/conversion/im_c2real.c +libsrc/conversion/im_scaleps.c +libsrc/conversion/im_msb.c +libsrc/conversion/im_ri2c.c +libsrc/conversion/im_print.c +libsrc/conversion/im_system.c +libsrc/conversion/im_c2amph.c +libsrc/conversion/im_bernd.c +libsrc/conversion/im_lrjoin.c +libsrc/conversion/im_tile_cache.c +libsrc/conversion/im_jpeg2vips.c +libsrc/conversion/im_copy.c +libsrc/conversion/im_extract.c +libsrc/conversion/im_thresh.c +libsrc/conversion/im_zoom.c +libsrc/conversion/im_png2vips.c +libsrc/conversion/im_clip.c +libsrc/conversion/im_vips2mask.c +libsrc/conversion/im_vips2ppm.c +libsrc/conversion/im_replicate.c +libsrc/conversion/im_rot270.c +libsrc/conversion/im_grid.c +libsrc/conversion/im_vips2png.c +libsrc/conversion/im_fliphor.c +libsrc/conversion/im_gbandjoin.c +libsrc/conversion/im_vips2jpeg.c +libsrc/conversion/im_subsample.c +libsrc/conversion/im_tiff2vips.c +libsrc/conversion/im_exr2vips.c +libsrc/conversion/im_mask2vips.c +libsrc/conversion/im_raw2vips.c +libsrc/freq_filt/im_freq_mask.c +libsrc/freq_filt/im_invfft.c +libsrc/freq_filt/freq_dispatch.c +libsrc/freq_filt/im_disp_ps.c +libsrc/freq_filt/fft_sp.c +libsrc/freq_filt/fmaskcir.c +libsrc/freq_filt/im_fwfft.c +libsrc/freq_filt/im_fractsurf.c +libsrc/freq_filt/im_freqflt.c +libsrc/freq_filt/im_invfftr.c +libsrc/freq_filt/im_rotquad.c +libsrc/freq_filt/fmask4th.c +libsrcCC/VMask.cc +libsrcCC/vipsc++.cc +libsrcCC/VError.cc +libsrcCC/VImage.cc +libsrcCC/VDisplay.cc diff --git a/po/POTFILES.skip b/po/POTFILES.skip new file mode 100644 index 00000000..e69de29b diff --git a/po/README b/po/README new file mode 100644 index 00000000..b60a7b2a --- /dev/null +++ b/po/README @@ -0,0 +1,51 @@ +translators +----------- + +see this page for a howto: + + http://developer.gnome.org/doc/tutorials/gnome-i18n/translator.html + +Things like + + msgid "/File/_Save Image As ..." + +are menu items. You only need to translate the last part (following the final +"/"). The underscore character marks the accelerator (the underlined character +in the menu item). So you could put: + + msgstr "Sevy i_mago os ..." + +and it would display as "Sevy imago os ...", with the "m" underlined. + +tips +---- + +intltool-update --pot + + make a new vips.pot translation template from the sources + +:%s/msgstr ""/msgstr "Malkovich"/ +:%s/msgstr\[0\] ""/msgstr[0] "Malkovich"/ +:%s/msgstr\[1\] ""/msgstr[1] "Malkovich"/ +:g/#, c-format/d + +add header + +# test translation file +# +msgid "" +msgstr "" +"Project-Id-Version: vips 7.9.3\n" +"POT-Creation-Date: 2003-11-04 12:18+0000\n" +"PO-Revision-Date: 2003-11-04 12:30+0000\n" +"Last-Translator: john \n" +"Language-Team: dk \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + + edits to make vips.pot into test.po + +msgfmt -cv -o /dev/null test.po + + check translation for errors diff --git a/po/en_GB.gmo b/po/en_GB.gmo new file mode 100644 index 0000000000000000000000000000000000000000..9bf7ae926fe92b8086c9e6c328b7d4a0562bd27b GIT binary patch literal 362 zcmYL^Jx{|h5Qd9j%E-*%fvsGy+lHp*Lsd&bm0D3%ffbp=xGv!wIZg#Xgulmc;j)ld zdUV4*x{u$}lb;^th&Ut8iDTk|Xp<70niHz6^Pi1(dz}YZ7Vx5dZ5>L6wi>W;Jc)rw^hCEL3l#(7iO5T;clwYj1hi R9u}Cp!lIEZeX}9z{Q\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: libsrc/convolution/im_addgnoise.c:78 +msgid "too many bands" +msgstr "" + +#: libsrc/convolution/im_sharpen.c:247 +msgid "input not 3-band short" +msgstr "" + +#: libsrc/convolution/im_sharpen.c:258 +msgid "parameters out of range" +msgstr "" + +#: libsrc/colour/im_icc_transform.c:200 libsrc/colour/im_icc_transform.c:210 +#, c-format +msgid "unable to open profile \"%s\"" +msgstr "" + +#: libsrc/matrix/im_invmat.c:69 +msgid "singular matrix in ludcmp" +msgstr "" + +#: libsrc/matrix/im_invmat.c:206 +msgid "singular matrix" +msgstr "" + +#: libsrc/acquire/im_clamp.c:69 +msgid "bad input format" +msgstr "" + +#: libsrc/acquire/im_clamp.c:74 +msgid "bad black format" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:89 libsrc/freq_filt/im_invfftr.c:171 +#: libsrc/freq_filt/im_invfft.c:94 libsrc/freq_filt/im_invfft.c:142 +#: libsrc/freq_filt/im_fwfft.c:112 libsrc/freq_filt/im_fwfft.c:217 +#: libsrc/freq_filt/im_fwfft.c:308 libsrc/freq_filt/im_fwfft.c:420 +msgid "one band uncoded only" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:126 libsrc/freq_filt/im_invfftr.c:209 +#: libsrc/freq_filt/im_invfft.c:106 libsrc/freq_filt/im_invfft.c:156 +#: libsrc/freq_filt/im_fwfft.c:123 libsrc/freq_filt/im_fwfft.c:228 +#: libsrc/freq_filt/im_fwfft.c:321 libsrc/freq_filt/im_fwfft.c:433 +msgid "unable to create transform plan" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:252 libsrc/freq_filt/im_invfft.c:199 +msgid "one band complex uncoded only" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:256 libsrc/freq_filt/im_invfft.c:203 +#: libsrc/freq_filt/im_fwfft.c:515 +msgid "sides must be power of 2" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:276 libsrc/freq_filt/im_invfft.c:223 +#: libsrc/freq_filt/im_fwfft.c:535 +msgid "fft_sp failed" +msgstr "" + +#: libsrc/freq_filt/im_fwfft.c:511 +msgid "one band non-complex uncoded only" +msgstr "" + +#: libsrc/conversion/im_extract.c:162 +msgid "bad extract area" +msgstr "" + +#: libsrc/conversion/im_extract.c:167 libsrc/conversion/im_extract.c:252 +msgid "band selection out of range" +msgstr "" + +#: libsrc/conversion/im_extract.c:172 +msgid "unknown image coding type" +msgstr "" + +#: libsrc/conversion/im_extract.c:177 +msgid "can't extract band from IM_CODING_LABQ" +msgstr "" + +#: libsrc/conversion/im_extract.c:256 libsrc/arithmetic/im_powtra.c:184 +#: libsrc/arithmetic/im_deviate.c:171 libsrc/arithmetic/im_multiply.c:186 +#: libsrc/arithmetic/im_ceil.c:92 libsrc/arithmetic/im_maxpos.c:94 +#: libsrc/arithmetic/im_costra.c:117 libsrc/arithmetic/im_costra.c:202 +#: libsrc/arithmetic/im_tantra.c:117 libsrc/arithmetic/im_tantra.c:203 +#: libsrc/arithmetic/im_measure.c:189 libsrc/arithmetic/im_sign.c:146 +#: libsrc/arithmetic/im_expntra.c:184 libsrc/arithmetic/im_logtra.c:123 +#: libsrc/arithmetic/im_abs.c:147 libsrc/arithmetic/im_add.c:254 +#: libsrc/arithmetic/im_avg.c:166 libsrc/arithmetic/im_max.c:232 +#: libsrc/arithmetic/im_min.c:231 libsrc/arithmetic/im_lintra.c:281 +#: libsrc/arithmetic/im_invert.c:117 libsrc/arithmetic/im_stats.c:238 +#: libsrc/arithmetic/im_floor.c:92 libsrc/arithmetic/im_remainder.c:103 +#: libsrc/arithmetic/im_remainder.c:212 libsrc/arithmetic/im_subtract.c:171 +#: libsrc/arithmetic/im_sintra.c:117 libsrc/arithmetic/im_sintra.c:202 +#: libsrc/arithmetic/im_log10tra.c:123 libsrc/arithmetic/im_divide.c:150 +msgid "not uncoded" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:54 libsrc/conversion/im_magick2vips.c:61 +msgid "libMagick support disabled" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:190 +#, c-format +msgid "unsupported colorspace %d" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:214 +#, c-format +msgid "unsupported bit depth %d" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:409 +msgid "unable to read pixels" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:431 +#, c-format +msgid "" +"unable to read file \"%s\"\n" +"libMagick error: %s %s" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:456 +#, c-format +msgid "" +"unable to ping file \"%s\"\n" +"libMagick error: %s %s" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:467 +msgid "bad image size" +msgstr "" + +#: libsrc/conversion/im_copy.c:138 libsrc/conversion/im_copy.c:275 +msgid "in must be uncoded" +msgstr "" + +#: libsrc/conversion/im_copy.c:142 +msgid "Coding must be NONE or LABQ" +msgstr "" + +#: libsrc/conversion/im_copy.c:146 +#, c-format +msgid "BandFmt must be in range [0,%d]" +msgstr "" + +#: libsrc/conversion/im_copy.c:169 +msgid "sizeof( pixel ) has changed" +msgstr "" + +#: libsrc/conversion/im_copy.c:312 +msgid "unsupported image type" +msgstr "" + +#: libsrc/conversion/im_ppm2vips.c:250 +msgid "internal error" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:131 libsrc/conversion/im_tiff2vips.c:144 +#: libsrc/conversion/im_vips2tiff.c:127 +msgid "TIFF support disabled" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:284 libsrc/conversion/im_tiff2vips.c:307 +#: libsrc/conversion/im_tiff2vips.c:326 +#, c-format +msgid "required field %d missing" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:288 +#, c-format +msgid "required field %d=%d, not %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:632 +msgid "read error" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1029 +#, fuzzy +msgid "bad colormap" +msgstr "False colour" + +#: libsrc/conversion/im_tiff2vips.c:1085 libsrc/conversion/im_tiff2vips.c:1135 +msgid "3 or 4 bands RGB TIFF only" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1284 +msgid "unknown resolution unit" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1360 +#, c-format +msgid "unsupported sample format %d for lab image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1370 +#, c-format +msgid "unsupported depth %d for LAB image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1417 +#, c-format +msgid "unsupported sample format %d for greyscale image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1426 +#, c-format +msgid "unsupported depth %d for greyscale image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1466 +#, c-format +msgid "unsupported sample format %d for rgb image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1475 +#, c-format +msgid "unsupported depth %d for RGB image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1489 +#, c-format +msgid "unknown photometric interpretation %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1550 +#, c-format +msgid "bad page number %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1577 libsrc/conversion/im_vips2tiff.c:265 +#, c-format +msgid "unable to open \"%s\" for input" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1627 libsrc/conversion/im_tiff2vips.c:1661 +#, c-format +msgid "TIFF file does not contain page %d" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:228 +#, c-format +msgid "TIFF error in \"%s\": " +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:245 +#, c-format +msgid "unable to open \"%s\" for output" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:381 +#, c-format +msgid "file \"%s\" too long" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:393 +#, c-format +msgid "error reading from file \"%s\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:416 +#, c-format +msgid "unable to read profile \"%s\"" +msgstr "" + +#. Out of space! +#. +#: libsrc/conversion/im_vips2tiff.c:688 +msgid "layer buffer exhausted -- try making TIFF output tiles smaller" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:919 +msgid "TIFF write tile failed" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:994 +msgid "internal error #9876345" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1109 +msgid "TIFF write failed" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1238 +msgid "bad JPEG quality parameter" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1244 +#, c-format +msgid "" +"unknown compression mode \"%s\"\n" +"should be one of \"none\", \"packbits\", \"ccittfax4\", \"lzw\", \"deflate\" " +"or \"jpeg\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1258 +msgid "bad tile sizes" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1265 +#, c-format +msgid "bad tile size %dx%d" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1273 +msgid "tile size not a multiple of 16" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1282 +#, c-format +msgid "" +"unknown layout mode \"%s\"\n" +"should be one of \"tile\" or \"strip\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1294 +#, c-format +msgid "" +"unknown multi-res mode \"%s\"\n" +"should be one of \"flat\" or \"pyramid\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1306 +#, c-format +msgid "" +"unknown format \"%s\"\n" +"should be one of \"onebit\" or \"manybit\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1318 +#, c-format +msgid "" +"unknown resolution unit \"%s\"\n" +"should be one of \"res_cm\" or \"res_inch\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1327 +msgid "bad resolution values" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1342 +#, c-format +msgid "unknown extra options \"%s\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1346 +msgid "can't have strip pyramid -- enabling tiling" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1356 +msgid "can only pyramid LABQ and non-complex images" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1372 +msgid "can't have 1-bit JPEG -- disabling JPEG" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1529 +msgid "unknown coding type" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1537 +msgid "unsigned 8-bit int, 16-bit int, and 32-bit float only" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1543 +msgid "1 to 4 bands only" +msgstr "" + +#: libsrc/arithmetic/im_powtra.c:188 libsrc/arithmetic/im_tantra.c:121 +#: libsrc/arithmetic/im_tantra.c:207 libsrc/arithmetic/im_expntra.c:188 +#: libsrc/arithmetic/im_logtra.c:127 libsrc/arithmetic/im_sintra.c:121 +#: libsrc/arithmetic/im_sintra.c:206 libsrc/arithmetic/im_log10tra.c:127 +msgid "not non-complex" +msgstr "" + +#: libsrc/arithmetic/im_powtra.c:193 libsrc/arithmetic/im_expntra.c:193 +#: libsrc/arithmetic/im_lintra.c:290 libsrc/arithmetic/im_remainder.c:217 +#, c-format +msgid "not 1 or %d elements in vector" +msgstr "" + +#: libsrc/arithmetic/im_deviate.c:175 libsrc/arithmetic/im_costra.c:121 +#: libsrc/arithmetic/im_costra.c:206 libsrc/arithmetic/im_measure.c:193 +#: libsrc/arithmetic/im_avg.c:162 libsrc/arithmetic/im_stats.c:234 +msgid "bad input type" +msgstr "" + +#: libsrc/arithmetic/im_multiply.c:177 libsrc/arithmetic/im_remainder.c:94 +msgid "not same size" +msgstr "" + +#: libsrc/arithmetic/im_multiply.c:182 libsrc/arithmetic/im_add.c:250 +#: libsrc/arithmetic/im_remainder.c:99 libsrc/arithmetic/im_subtract.c:167 +#: libsrc/arithmetic/im_divide.c:146 +msgid "not same number of bands" +msgstr "" + +#: libsrc/arithmetic/im_minpos.c:80 +msgid "input must be uncoded" +msgstr "" + +#: libsrc/arithmetic/im_gadd.c:91 libsrc/arithmetic/im_gadd.c:107 +msgid "Unable to accept image1" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:79 +msgid "absolute value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:98 +msgid "add two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:123 +msgid "average value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:142 +msgid "multiply two complex images, normalising output" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:167 +msgid "standard deviation of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:186 +msgid "10^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:205 +msgid "e^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:234 +msgid "x^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:263 +msgid "[x,y,z]^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:299 +msgid "average of 4 images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:318 +msgid "divide two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:352 +msgid "calculate a*in1 + b*in2 + c = outfile" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:371 +msgid "photographic negative" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:402 +msgid "calculate a*in + b = outfile" +msgstr "" + +#: libsrc/arithmetic/arith_dispatch.c:427 +msgid "vectors not same length" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:438 +msgid "calculate a*in + b -> out, a and b vectors" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:470 +msgid "calculate max(white)*factor*(in/white), if clip == 1" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:489 +msgid "log10 of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:508 +msgid "ln of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:527 +msgid "tan of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:546 +msgid "atan of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:565 +msgid "cos of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:584 +msgid "acos of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:603 +msgid "round to smallest integal value not less than" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:622 +msgid "round to largest integal value not greater than" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:641 +msgid "round to nearest integal value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:660 +msgid "sin of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:679 +msgid "unit vector in direction of value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:698 +msgid "asin of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:723 +msgid "maximum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:758 +msgid "position of maximum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:818 +msgid "measure averages of a grid of patches" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:843 +msgid "minimum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:871 +msgid "position of minimum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:890 +msgid "remainder after integer division" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:919 +msgid "remainder after integer division by a constant" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:948 +msgid "remainder after integer division by a vector of constants" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:968 +msgid "multiply two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:989 +msgid "pel^x ofbuildimage" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1010 +msgid "pel^[x,y,z] of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1041 +msgid "many image statistics in one pass" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1060 +msgid "subtract two images" +msgstr "" + +#: libsrc/arithmetic/im_measure.c:106 +#, c-format +msgid "patch %d is out of range" +msgstr "" + +#: libsrc/arithmetic/im_measure.c:148 +#, c-format +msgid "patch %d, band %d: avg = %g, sdev = %g" +msgstr "" + +#: libsrc/arithmetic/im_abs.c:183 +msgid "unknown input type" +msgstr "" + +#: libsrc/arithmetic/im_add.c:190 +#, c-format +msgid "not one band or %d bands" +msgstr "" + +#: libsrc/arithmetic/im_add.c:194 +msgid "bad bands" +msgstr "" + +#: libsrc/arithmetic/im_invert.c:121 +msgid "not UCHAR" +msgstr "" + +#: libsrc/arithmetic/im_remainder.c:239 +msgid "division by zero" +msgstr "" + +#: libsrc/relational/im_blend.c:338 +msgid "images not uncoded" +msgstr "" + +#: libsrc/relational/im_blend.c:344 libsrc/relational/im_ifthenelse.c:183 +msgid "size and format of then and else must match" +msgstr "" + +#: libsrc/relational/im_blend.c:349 libsrc/relational/im_ifthenelse.c:188 +msgid "conditional image must be uchar" +msgstr "" + +#: libsrc/relational/im_blend.c:354 libsrc/relational/im_ifthenelse.c:193 +msgid "conditional image must be one band or same as then and else images" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:167 +msgid "then image must be uncoded or labpack" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:172 +msgid "else image must be uncoded or labpack" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:177 +msgid "condition image must be uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:112 +#, c-format +msgid "%d overflows detected" +msgstr "" + +#. Switch for input types. Has to be uint type! +#. +#: libsrc/histograms_lut/im_maplut.c:473 +msgid "bad input file" +msgstr "" + +#. Switch for LUT types. One function for non-complex images, a +#. * variant for complex ones. Another pair as well, in case the input is not +#. * uchar. +#. +#: libsrc/histograms_lut/im_maplut.c:504 +msgid "bad lut file" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:554 +msgid "lut is not uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:558 +msgid "lut seems very large!" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:570 +msgid "input is not uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:574 +msgid "input is not some unsigned integer type" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:579 +msgid "" +"lut should have 1 band, or same number of bands as input, or any number of " +"bands if input has 1 band" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:586 +msgid "input is uchar and lut does not have 256 elements" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:212 +msgid " uncoded images only" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:218 +msgid " unsigned 8 or 16 bit images only" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:225 +#, c-format +msgid " bins out of range [1,%d]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:233 +msgid "bad in_max, out_max parameters" +msgstr "" + +#: libsrc/histograms_lut/tone.c:237 +msgid "bad Lb, Lw parameters" +msgstr "" + +#: libsrc/histograms_lut/tone.c:241 +msgid "Ps not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:245 +msgid "Pm not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:249 +msgid "Ph not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:253 +msgid "S not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:257 +msgid "M not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:261 +msgid "H not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:342 libsrc/histograms_lut/tone.c:390 +msgid "not 1 by n or n by 1 image" +msgstr "" + +#: libsrc/histograms_lut/tone.c:396 +msgid "not 1024-point IM_BANDFMT_SHORT lut" +msgstr "" + +#: libsrc/histograms_lut/tone.c:413 libsrc/histograms_lut/tone.c:487 +msgid "input not LabS or LabQ" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:81 +msgid "inputs differ in format" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:85 +msgid "input should be uncoded or IM_CODING_LABQ" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:104 +msgid "small not inside big" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:134 libsrc/iofuncs/im_mapfile.c:301 +msgid "unable to CreateFileMapping" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:150 libsrc/iofuncs/im_mapfile.c:313 +msgid "unable to MapViewOfFile" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:177 +msgid "unable to mmap" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:178 +#, c-format +msgid "" +"map failed (%s), running very low on system resources, expect a crash soon" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:195 libsrc/iofuncs/im_mapfile.c:307 +msgid "unable to UnmapViewOfFile" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:201 +msgid "unable to munmap file" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:224 libsrc/iofuncs/im_mapfile.c:265 +msgid "unable to get file status" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:229 +msgid "file is less than 64 bytes" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:233 +msgid "not a regular file" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:270 +msgid "unable to read data" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:333 +#, c-format +msgid "unable to mmap: \"%s\" - %s" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:343 +#, c-format +msgid "unable to mmap \"%s\" to same address" +msgstr "" + +#: libsrc/iofuncs/im_binfile.c:88 libsrc/iofuncs/im_render.c:1079 +msgid "bad parameters" +msgstr "" + +#: libsrc/iofuncs/im_binfile.c:116 +#, c-format +msgid "unable to open %s: file has been truncated" +msgstr "" + +#: libsrc/iofuncs/im_binfile.c:126 +#, c-format +msgid "%s is longer than expected" +msgstr "" + +#: libsrc/iofuncs/error.c:114 +msgid "windows error" +msgstr "" + +#: libsrc/iofuncs/error.c:123 +msgid "unix error" +msgstr "" + +#: libsrc/iofuncs/error.c:149 libsrc/iofuncs/error.c:150 +#: libsrc/iofuncs/error.c:170 libsrc/iofuncs/error.c:171 +#, c-format +msgid "%s: " +msgstr "" + +#: libsrc/iofuncs/error.c:149 +msgid "vips diagnostic" +msgstr "" + +#: libsrc/iofuncs/error.c:170 +msgid "vips warning" +msgstr "" + +#: libsrc/iofuncs/im_readhist.c:163 +msgid "history file too large" +msgstr "" + +#: libsrc/iofuncs/im_readhist.c:174 +msgid "unable to read" +msgstr "" + +#: libsrc/iofuncs/im_header.c:102 +#, c-format +msgid "no such int field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_header.c:124 +#, c-format +msgid "no such double field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_header.c:146 +#, c-format +msgid "no such string field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:112 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:118 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:124 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:130 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:136 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:142 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_render.c:511 libsrc/iofuncs/im_render.c:666 +#: libsrc/iofuncs/threadgroup.c:330 +msgid "unable to create thread" +msgstr "" + +#: libsrc/iofuncs/package.c:436 +#, c-format +msgid "unable to close plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:438 libsrc/iofuncs/package.c:488 +#: libsrc/iofuncs/package.c:502 +#, c-format +msgid "plugin error: %s" +msgstr "" + +#: libsrc/iofuncs/package.c:462 +msgid "plugins not supported on this platform" +msgstr "" + +#: libsrc/iofuncs/package.c:486 +#, c-format +msgid "unable to open plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:499 +#, c-format +msgid "unable to find symbol \"package_table\" in plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:513 +#, c-format +msgid "corrupted package table in plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:636 libsrc/iofuncs/package.c:692 +#, c-format +msgid "function \"%s\" not found" +msgstr "" + +#: libsrc/iofuncs/package.c:664 +#, c-format +msgid "package \"%s\" not found" +msgstr "" + +#: libsrc/iofuncs/package.c:802 +msgid "too few arguments" +msgstr "" + +#: libsrc/iofuncs/package.c:824 +msgid "too many arguments" +msgstr "" + +#: libsrc/iofuncs/package.c:1031 +msgid "flag not 0,1,2" +msgstr "" + +#: libsrc/iofuncs/im_wrapmany.c:158 +msgid "too many input images" +msgstr "" + +#: libsrc/iofuncs/im_wrapmany.c:176 +msgid "descriptors differ in size" +msgstr "" + +#~ msgid "Edit color \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#~ msgid "Set color" +#~ msgstr "Set colour" + +#~ msgid "Double-click to edit this color, or drag-and-drop between colors" +#~ msgstr "Double-click to edit this colour, or drag-and-drop between colours" + +#, fuzzy +#~ msgid "Header for \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Edit \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Edit matrix \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Set option" +#~ msgstr "Set colour" + +#, fuzzy +#~ msgid "Edit column item \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Edit slider \"%s\"" +#~ msgstr "Edit colour \"%s\"" + +#, fuzzy +#~ msgid "Set Slider" +#~ msgstr "Set colour" + +#, fuzzy +#~ msgid "Set toggle" +#~ msgstr "Set colour" diff --git a/po/malkovich.gmo b/po/malkovich.gmo new file mode 100644 index 0000000000000000000000000000000000000000..87a131943d983bdfee964e33f617e4ab59fbebc5 GIT binary patch literal 380 zcmZ{fu};G<5Qd9j^2p5KfvsGylNM3~6>2F`B_gUSu$#oBhQy9+w}J=Z^>`L8OEU3G zpL~+PJD-0~Pku(2BjgM@M~;yTq)Un{@N$M{=k(b)`=N>e>k?j-^V%9gjc#Ll9nGSI zY*lMrfXr9AgvGw{kXu2?<~fI@Q!=0r4>Bl0W5%Yyu7IVKPsEgu8D|Vd*s6o><_=y0 zoCBLs9*cyH8HPMbA0T(q_*w?*1Z`yfX%D*C(c8Yf@2jY=%^aIdwU?EGT*>C2mRd*F zRdbT9v(@lJ9x<}CCMbiiez&-OP+#G)t)(_MwAe}KRk(Y}A0ZvQZo7BoU}XwhYEucC IzUe?lf7twL-v9sr literal 0 HcmV?d00001 diff --git a/po/malkovich.po b/po/malkovich.po new file mode 100644 index 00000000..b3ac4029 --- /dev/null +++ b/po/malkovich.po @@ -0,0 +1,3259 @@ +# test translation file +# +msgid "" +msgstr "" +"Project-Id-Version: nip2 7.9.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2005-06-08 14:51+0100\n" +"PO-Revision-Date: 2003-11-04 12:30+0000\n" +"Last-Translator: malkovich \n" +"Language-Team: malkovich \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: libsrc/convolution/im_addgnoise.c:78 +#, fuzzy +msgid "too many bands" +msgstr "Malkovich" + +#: libsrc/convolution/im_sharpen.c:247 +msgid "input not 3-band short" +msgstr "" + +#: libsrc/convolution/im_sharpen.c:258 +msgid "parameters out of range" +msgstr "" + +#: libsrc/colour/im_icc_transform.c:200 libsrc/colour/im_icc_transform.c:210 +#, fuzzy, c-format +msgid "unable to open profile \"%s\"" +msgstr "Malkovich" + +#: libsrc/matrix/im_invmat.c:69 +msgid "singular matrix in ludcmp" +msgstr "" + +#: libsrc/matrix/im_invmat.c:206 +#, fuzzy +msgid "singular matrix" +msgstr "Malkovich" + +#: libsrc/acquire/im_clamp.c:69 +msgid "bad input format" +msgstr "" + +#: libsrc/acquire/im_clamp.c:74 +msgid "bad black format" +msgstr "" + +#: libsrc/freq_filt/im_invfftr.c:89 libsrc/freq_filt/im_invfftr.c:171 +#: libsrc/freq_filt/im_invfft.c:94 libsrc/freq_filt/im_invfft.c:142 +#: libsrc/freq_filt/im_fwfft.c:112 libsrc/freq_filt/im_fwfft.c:217 +#: libsrc/freq_filt/im_fwfft.c:308 libsrc/freq_filt/im_fwfft.c:420 +#, fuzzy +msgid "one band uncoded only" +msgstr "Malkovich" + +#: libsrc/freq_filt/im_invfftr.c:126 libsrc/freq_filt/im_invfftr.c:209 +#: libsrc/freq_filt/im_invfft.c:106 libsrc/freq_filt/im_invfft.c:156 +#: libsrc/freq_filt/im_fwfft.c:123 libsrc/freq_filt/im_fwfft.c:228 +#: libsrc/freq_filt/im_fwfft.c:321 libsrc/freq_filt/im_fwfft.c:433 +#, fuzzy +msgid "unable to create transform plan" +msgstr "Malkovich" + +#: libsrc/freq_filt/im_invfftr.c:252 libsrc/freq_filt/im_invfft.c:199 +#, fuzzy +msgid "one band complex uncoded only" +msgstr "Malkovich" + +#: libsrc/freq_filt/im_invfftr.c:256 libsrc/freq_filt/im_invfft.c:203 +#: libsrc/freq_filt/im_fwfft.c:515 +#, fuzzy +msgid "sides must be power of 2" +msgstr "Malkovich" + +#: libsrc/freq_filt/im_invfftr.c:276 libsrc/freq_filt/im_invfft.c:223 +#: libsrc/freq_filt/im_fwfft.c:535 +msgid "fft_sp failed" +msgstr "" + +#: libsrc/freq_filt/im_fwfft.c:511 +msgid "one band non-complex uncoded only" +msgstr "" + +#: libsrc/conversion/im_extract.c:162 +msgid "bad extract area" +msgstr "" + +#: libsrc/conversion/im_extract.c:167 libsrc/conversion/im_extract.c:252 +msgid "band selection out of range" +msgstr "" + +#: libsrc/conversion/im_extract.c:172 +#, fuzzy +msgid "unknown image coding type" +msgstr "Malkovich" + +#: libsrc/conversion/im_extract.c:177 +msgid "can't extract band from IM_CODING_LABQ" +msgstr "" + +#: libsrc/conversion/im_extract.c:256 libsrc/arithmetic/im_powtra.c:184 +#: libsrc/arithmetic/im_deviate.c:171 libsrc/arithmetic/im_multiply.c:186 +#: libsrc/arithmetic/im_ceil.c:92 libsrc/arithmetic/im_maxpos.c:94 +#: libsrc/arithmetic/im_costra.c:117 libsrc/arithmetic/im_costra.c:202 +#: libsrc/arithmetic/im_tantra.c:117 libsrc/arithmetic/im_tantra.c:203 +#: libsrc/arithmetic/im_measure.c:189 libsrc/arithmetic/im_sign.c:146 +#: libsrc/arithmetic/im_expntra.c:184 libsrc/arithmetic/im_logtra.c:123 +#: libsrc/arithmetic/im_abs.c:147 libsrc/arithmetic/im_add.c:254 +#: libsrc/arithmetic/im_avg.c:166 libsrc/arithmetic/im_max.c:232 +#: libsrc/arithmetic/im_min.c:231 libsrc/arithmetic/im_lintra.c:281 +#: libsrc/arithmetic/im_invert.c:117 libsrc/arithmetic/im_stats.c:238 +#: libsrc/arithmetic/im_floor.c:92 libsrc/arithmetic/im_remainder.c:103 +#: libsrc/arithmetic/im_remainder.c:212 libsrc/arithmetic/im_subtract.c:171 +#: libsrc/arithmetic/im_sintra.c:117 libsrc/arithmetic/im_sintra.c:202 +#: libsrc/arithmetic/im_log10tra.c:123 libsrc/arithmetic/im_divide.c:150 +msgid "not uncoded" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:54 libsrc/conversion/im_magick2vips.c:61 +msgid "libMagick support disabled" +msgstr "" + +#: libsrc/conversion/im_magick2vips.c:190 +#, fuzzy, c-format +msgid "unsupported colorspace %d" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:214 +#, fuzzy, c-format +msgid "unsupported bit depth %d" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:409 +#, fuzzy +msgid "unable to read pixels" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:431 +#, fuzzy, c-format +msgid "" +"unable to read file \"%s\"\n" +"libMagick error: %s %s" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:456 +#, fuzzy, c-format +msgid "" +"unable to ping file \"%s\"\n" +"libMagick error: %s %s" +msgstr "Malkovich" + +#: libsrc/conversion/im_magick2vips.c:467 +#, fuzzy +msgid "bad image size" +msgstr "Malkovich" + +#: libsrc/conversion/im_copy.c:138 libsrc/conversion/im_copy.c:275 +msgid "in must be uncoded" +msgstr "" + +#: libsrc/conversion/im_copy.c:142 +msgid "Coding must be NONE or LABQ" +msgstr "" + +#: libsrc/conversion/im_copy.c:146 +#, c-format +msgid "BandFmt must be in range [0,%d]" +msgstr "" + +#: libsrc/conversion/im_copy.c:169 +msgid "sizeof( pixel ) has changed" +msgstr "" + +#: libsrc/conversion/im_copy.c:312 +#, fuzzy +msgid "unsupported image type" +msgstr "Malkovich" + +#: libsrc/conversion/im_ppm2vips.c:250 +#, fuzzy +msgid "internal error" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:131 libsrc/conversion/im_tiff2vips.c:144 +#: libsrc/conversion/im_vips2tiff.c:127 +msgid "TIFF support disabled" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:284 libsrc/conversion/im_tiff2vips.c:307 +#: libsrc/conversion/im_tiff2vips.c:326 +#, c-format +msgid "required field %d missing" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:288 +#, c-format +msgid "required field %d=%d, not %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:632 +#, fuzzy +msgid "read error" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1029 +#, fuzzy +msgid "bad colormap" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1085 libsrc/conversion/im_tiff2vips.c:1135 +msgid "3 or 4 bands RGB TIFF only" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1284 +#, fuzzy +msgid "unknown resolution unit" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1360 +#, fuzzy, c-format +msgid "unsupported sample format %d for lab image" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1370 +#, fuzzy, c-format +msgid "unsupported depth %d for LAB image" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1417 +#, c-format +msgid "unsupported sample format %d for greyscale image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1426 +#, fuzzy, c-format +msgid "unsupported depth %d for greyscale image" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1466 +#, c-format +msgid "unsupported sample format %d for rgb image" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1475 +#, fuzzy, c-format +msgid "unsupported depth %d for RGB image" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1489 +#, c-format +msgid "unknown photometric interpretation %d" +msgstr "" + +#: libsrc/conversion/im_tiff2vips.c:1550 +#, fuzzy, c-format +msgid "bad page number %d" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1577 libsrc/conversion/im_vips2tiff.c:265 +#, fuzzy, c-format +msgid "unable to open \"%s\" for input" +msgstr "Malkovich" + +#: libsrc/conversion/im_tiff2vips.c:1627 libsrc/conversion/im_tiff2vips.c:1661 +#, c-format +msgid "TIFF file does not contain page %d" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:228 +#, fuzzy, c-format +msgid "TIFF error in \"%s\": " +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:245 +#, fuzzy, c-format +msgid "unable to open \"%s\" for output" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:381 +#, fuzzy, c-format +msgid "file \"%s\" too long" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:393 +#, fuzzy, c-format +msgid "error reading from file \"%s\"" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:416 +#, fuzzy, c-format +msgid "unable to read profile \"%s\"" +msgstr "Malkovich" + +#. Out of space! +#. +#: libsrc/conversion/im_vips2tiff.c:688 +msgid "layer buffer exhausted -- try making TIFF output tiles smaller" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:919 +msgid "TIFF write tile failed" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:994 +#, fuzzy +msgid "internal error #9876345" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1109 +#, fuzzy +msgid "TIFF write failed" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1238 +#, fuzzy +msgid "bad JPEG quality parameter" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1244 +#, c-format +msgid "" +"unknown compression mode \"%s\"\n" +"should be one of \"none\", \"packbits\", \"ccittfax4\", \"lzw\", \"deflate\" " +"or \"jpeg\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1258 +#, fuzzy +msgid "bad tile sizes" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1265 +#, c-format +msgid "bad tile size %dx%d" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1273 +msgid "tile size not a multiple of 16" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1282 +#, c-format +msgid "" +"unknown layout mode \"%s\"\n" +"should be one of \"tile\" or \"strip\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1294 +#, c-format +msgid "" +"unknown multi-res mode \"%s\"\n" +"should be one of \"flat\" or \"pyramid\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1306 +#, c-format +msgid "" +"unknown format \"%s\"\n" +"should be one of \"onebit\" or \"manybit\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1318 +#, c-format +msgid "" +"unknown resolution unit \"%s\"\n" +"should be one of \"res_cm\" or \"res_inch\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1327 +#, fuzzy +msgid "bad resolution values" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1342 +#, c-format +msgid "unknown extra options \"%s\"" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1346 +msgid "can't have strip pyramid -- enabling tiling" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1356 +msgid "can only pyramid LABQ and non-complex images" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1372 +msgid "can't have 1-bit JPEG -- disabling JPEG" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1529 +#, fuzzy +msgid "unknown coding type" +msgstr "Malkovich" + +#: libsrc/conversion/im_vips2tiff.c:1537 +msgid "unsigned 8-bit int, 16-bit int, and 32-bit float only" +msgstr "" + +#: libsrc/conversion/im_vips2tiff.c:1543 +msgid "1 to 4 bands only" +msgstr "" + +#: libsrc/arithmetic/im_powtra.c:188 libsrc/arithmetic/im_tantra.c:121 +#: libsrc/arithmetic/im_tantra.c:207 libsrc/arithmetic/im_expntra.c:188 +#: libsrc/arithmetic/im_logtra.c:127 libsrc/arithmetic/im_sintra.c:121 +#: libsrc/arithmetic/im_sintra.c:206 libsrc/arithmetic/im_log10tra.c:127 +#, fuzzy +msgid "not non-complex" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_powtra.c:193 libsrc/arithmetic/im_expntra.c:193 +#: libsrc/arithmetic/im_lintra.c:290 libsrc/arithmetic/im_remainder.c:217 +#, c-format +msgid "not 1 or %d elements in vector" +msgstr "" + +#: libsrc/arithmetic/im_deviate.c:175 libsrc/arithmetic/im_costra.c:121 +#: libsrc/arithmetic/im_costra.c:206 libsrc/arithmetic/im_measure.c:193 +#: libsrc/arithmetic/im_avg.c:162 libsrc/arithmetic/im_stats.c:234 +msgid "bad input type" +msgstr "" + +#: libsrc/arithmetic/im_multiply.c:177 libsrc/arithmetic/im_remainder.c:94 +msgid "not same size" +msgstr "" + +#: libsrc/arithmetic/im_multiply.c:182 libsrc/arithmetic/im_add.c:250 +#: libsrc/arithmetic/im_remainder.c:99 libsrc/arithmetic/im_subtract.c:167 +#: libsrc/arithmetic/im_divide.c:146 +#, fuzzy +msgid "not same number of bands" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_minpos.c:80 +msgid "input must be uncoded" +msgstr "" + +#: libsrc/arithmetic/im_gadd.c:91 libsrc/arithmetic/im_gadd.c:107 +#, fuzzy +msgid "Unable to accept image1" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:79 +#, fuzzy +msgid "absolute value" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:98 +#, fuzzy +msgid "add two images" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:123 +msgid "average value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:142 +msgid "multiply two complex images, normalising output" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:167 +msgid "standard deviation of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:186 +#, fuzzy +msgid "10^pel of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:205 +#, fuzzy +msgid "e^pel of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:234 +#, fuzzy +msgid "x^pel of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:263 +msgid "[x,y,z]^pel of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:299 +msgid "average of 4 images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:318 +msgid "divide two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:352 +msgid "calculate a*in1 + b*in2 + c = outfile" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:371 +msgid "photographic negative" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:402 +msgid "calculate a*in + b = outfile" +msgstr "" + +#: libsrc/arithmetic/arith_dispatch.c:427 +msgid "vectors not same length" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:438 +msgid "calculate a*in + b -> out, a and b vectors" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:470 +msgid "calculate max(white)*factor*(in/white), if clip == 1" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:489 +#, fuzzy +msgid "log10 of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:508 +#, fuzzy +msgid "ln of image" +msgstr "Malkovich" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:527 +msgid "tan of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:546 +msgid "atan of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:565 +msgid "cos of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:584 +msgid "acos of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:603 +msgid "round to smallest integal value not less than" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:622 +msgid "round to largest integal value not greater than" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:641 +msgid "round to nearest integal value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:660 +msgid "sin of image (angles in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:679 +msgid "unit vector in direction of value" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:698 +msgid "asin of image (result in degrees)" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:723 +msgid "maximum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:758 +msgid "position of maximum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:818 +msgid "measure averages of a grid of patches" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:843 +msgid "minimum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:871 +msgid "position of minimum value of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:890 +msgid "remainder after integer division" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:919 +msgid "remainder after integer division by a constant" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:948 +msgid "remainder after integer division by a vector of constants" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:968 +msgid "multiply two images" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:989 +msgid "pel^x ofbuildimage" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1010 +msgid "pel^[x,y,z] of image" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1041 +msgid "many image statistics in one pass" +msgstr "" + +#. Name +#: libsrc/arithmetic/arith_dispatch.c:1060 +msgid "subtract two images" +msgstr "" + +#: libsrc/arithmetic/im_measure.c:106 +#, c-format +msgid "patch %d is out of range" +msgstr "" + +#: libsrc/arithmetic/im_measure.c:148 +#, c-format +msgid "patch %d, band %d: avg = %g, sdev = %g" +msgstr "" + +#: libsrc/arithmetic/im_abs.c:183 +#, fuzzy +msgid "unknown input type" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_add.c:190 +#, fuzzy, c-format +msgid "not one band or %d bands" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_add.c:194 +#, fuzzy +msgid "bad bands" +msgstr "Malkovich" + +#: libsrc/arithmetic/im_invert.c:121 +msgid "not UCHAR" +msgstr "" + +#: libsrc/arithmetic/im_remainder.c:239 +#, fuzzy +msgid "division by zero" +msgstr "Malkovich" + +#: libsrc/relational/im_blend.c:338 +#, fuzzy +msgid "images not uncoded" +msgstr "Malkovich" + +#: libsrc/relational/im_blend.c:344 libsrc/relational/im_ifthenelse.c:183 +msgid "size and format of then and else must match" +msgstr "" + +#: libsrc/relational/im_blend.c:349 libsrc/relational/im_ifthenelse.c:188 +msgid "conditional image must be uchar" +msgstr "" + +#: libsrc/relational/im_blend.c:354 libsrc/relational/im_ifthenelse.c:193 +msgid "conditional image must be one band or same as then and else images" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:167 +msgid "then image must be uncoded or labpack" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:172 +msgid "else image must be uncoded or labpack" +msgstr "" + +#: libsrc/relational/im_ifthenelse.c:177 +msgid "condition image must be uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:112 +#, c-format +msgid "%d overflows detected" +msgstr "" + +#. Switch for input types. Has to be uint type! +#. +#: libsrc/histograms_lut/im_maplut.c:473 +#, fuzzy +msgid "bad input file" +msgstr "Malkovich" + +#. Switch for LUT types. One function for non-complex images, a +#. * variant for complex ones. Another pair as well, in case the input is not +#. * uchar. +#. +#: libsrc/histograms_lut/im_maplut.c:504 +#, fuzzy +msgid "bad lut file" +msgstr "Malkovich" + +#: libsrc/histograms_lut/im_maplut.c:554 +msgid "lut is not uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:558 +msgid "lut seems very large!" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:570 +msgid "input is not uncoded" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:574 +#, fuzzy +msgid "input is not some unsigned integer type" +msgstr "Malkovich" + +#: libsrc/histograms_lut/im_maplut.c:579 +msgid "" +"lut should have 1 band, or same number of bands as input, or any number of " +"bands if input has 1 band" +msgstr "" + +#: libsrc/histograms_lut/im_maplut.c:586 +msgid "input is uchar and lut does not have 256 elements" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:212 +#, fuzzy +msgid " uncoded images only" +msgstr "Malkovich" + +#: libsrc/histograms_lut/im_histnD.c:218 +msgid " unsigned 8 or 16 bit images only" +msgstr "" + +#: libsrc/histograms_lut/im_histnD.c:225 +#, c-format +msgid " bins out of range [1,%d]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:233 +msgid "bad in_max, out_max parameters" +msgstr "" + +#: libsrc/histograms_lut/tone.c:237 +#, fuzzy +msgid "bad Lb, Lw parameters" +msgstr "Malkovich" + +#: libsrc/histograms_lut/tone.c:241 +msgid "Ps not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:245 +msgid "Pm not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:249 +msgid "Ph not in range [0.0,1.0]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:253 +msgid "S not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:257 +msgid "M not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:261 +msgid "H not in range [-30,+30]" +msgstr "" + +#: libsrc/histograms_lut/tone.c:342 libsrc/histograms_lut/tone.c:390 +msgid "not 1 by n or n by 1 image" +msgstr "" + +#: libsrc/histograms_lut/tone.c:396 +msgid "not 1024-point IM_BANDFMT_SHORT lut" +msgstr "" + +#: libsrc/histograms_lut/tone.c:413 libsrc/histograms_lut/tone.c:487 +msgid "input not LabS or LabQ" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:81 +msgid "inputs differ in format" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:85 +msgid "input should be uncoded or IM_CODING_LABQ" +msgstr "" + +#: libsrc/inplace/im_insertplace.c:104 +msgid "small not inside big" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:134 libsrc/iofuncs/im_mapfile.c:301 +#, fuzzy +msgid "unable to CreateFileMapping" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:150 libsrc/iofuncs/im_mapfile.c:313 +#, fuzzy +msgid "unable to MapViewOfFile" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:177 +#, fuzzy +msgid "unable to mmap" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:178 +#, c-format +msgid "" +"map failed (%s), running very low on system resources, expect a crash soon" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:195 libsrc/iofuncs/im_mapfile.c:307 +#, fuzzy +msgid "unable to UnmapViewOfFile" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:201 +#, fuzzy +msgid "unable to munmap file" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:224 libsrc/iofuncs/im_mapfile.c:265 +#, fuzzy +msgid "unable to get file status" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:229 +msgid "file is less than 64 bytes" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:233 +msgid "not a regular file" +msgstr "" + +#: libsrc/iofuncs/im_mapfile.c:270 +#, fuzzy +msgid "unable to read data" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:333 +#, fuzzy, c-format +msgid "unable to mmap: \"%s\" - %s" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_mapfile.c:343 +#, fuzzy, c-format +msgid "unable to mmap \"%s\" to same address" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_binfile.c:88 libsrc/iofuncs/im_render.c:1079 +#, fuzzy +msgid "bad parameters" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_binfile.c:116 +#, fuzzy, c-format +msgid "unable to open %s: file has been truncated" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_binfile.c:126 +#, c-format +msgid "%s is longer than expected" +msgstr "" + +#: libsrc/iofuncs/error.c:114 +#, fuzzy +msgid "windows error" +msgstr "Malkovich" + +#: libsrc/iofuncs/error.c:123 +#, fuzzy +msgid "unix error" +msgstr "Malkovich" + +#: libsrc/iofuncs/error.c:149 libsrc/iofuncs/error.c:150 +#: libsrc/iofuncs/error.c:170 libsrc/iofuncs/error.c:171 +#, fuzzy, c-format +msgid "%s: " +msgstr "Malkovich" + +#: libsrc/iofuncs/error.c:149 +msgid "vips diagnostic" +msgstr "" + +#: libsrc/iofuncs/error.c:170 +msgid "vips warning" +msgstr "" + +#: libsrc/iofuncs/im_readhist.c:163 +msgid "history file too large" +msgstr "" + +#: libsrc/iofuncs/im_readhist.c:174 +#, fuzzy +msgid "unable to read" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_header.c:102 +#, c-format +msgid "no such int field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_header.c:124 +#, c-format +msgid "no such double field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_header.c:146 +#, c-format +msgid "no such string field \"%s\"" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:112 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:118 +#, fuzzy +msgid "" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_printdesc.c:124 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:130 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:136 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_printdesc.c:142 +msgid "" +msgstr "" + +#: libsrc/iofuncs/im_render.c:511 libsrc/iofuncs/im_render.c:666 +#: libsrc/iofuncs/threadgroup.c:330 +#, fuzzy +msgid "unable to create thread" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:436 +#, fuzzy, c-format +msgid "unable to close plugin \"%s\"" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:438 libsrc/iofuncs/package.c:488 +#: libsrc/iofuncs/package.c:502 +#, fuzzy, c-format +msgid "plugin error: %s" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:462 +msgid "plugins not supported on this platform" +msgstr "" + +#: libsrc/iofuncs/package.c:486 +#, fuzzy, c-format +msgid "unable to open plugin \"%s\"" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:499 +#, c-format +msgid "unable to find symbol \"package_table\" in plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:513 +#, c-format +msgid "corrupted package table in plugin \"%s\"" +msgstr "" + +#: libsrc/iofuncs/package.c:636 libsrc/iofuncs/package.c:692 +#, fuzzy, c-format +msgid "function \"%s\" not found" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:664 +#, fuzzy, c-format +msgid "package \"%s\" not found" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:802 +#, fuzzy +msgid "too few arguments" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:824 +#, fuzzy +msgid "too many arguments" +msgstr "Malkovich" + +#: libsrc/iofuncs/package.c:1031 +msgid "flag not 0,1,2" +msgstr "" + +#: libsrc/iofuncs/im_wrapmany.c:158 +#, fuzzy +msgid "too many input images" +msgstr "Malkovich" + +#: libsrc/iofuncs/im_wrapmany.c:176 +msgid "descriptors differ in size" +msgstr "" + +#~ msgid "Stack overflow." +#~ msgstr "Malkovich" + +#~ msgid "Spine stack overflow, runaway recursion?" +#~ msgstr "Malkovich" + +#~ msgid "Frame stack overflow, expression too complex." +#~ msgstr "Malkovich" + +#~ msgid "Stack underflow." +#~ msgstr "Malkovich" + +#~ msgid "Frame stack underflow, you've found a bug!" +#~ msgstr "Malkovich" + +#~ msgid "Spine stack underflow, you've found a bug!" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "" +#~ "Error in binary \"%s\".\n" +#~ "left = %s\n" +#~ "right = %s" +#~ msgstr "Malkovich" + +#~ msgid "Member not found." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "" +#~ "Member \"%s\" not found in object \"%s\".\n" +#~ "object = %s\n" +#~ "tag = %s\n" +#~ "Reference attempted in \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Bad argument." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "" +#~ "Error in unary \"%s\".\n" +#~ "argument = %s" +#~ msgstr "Malkovich" + +#~ msgid "Symbol on left hand side of '.' is not scope" +#~ msgstr "Malkovich" + +#~ msgid "Right hand side of '.' is not tag." +#~ msgstr "Malkovich" + +#~ msgid "Bad left hand side." +#~ msgstr "Malkovich" + +#~ msgid "Unimplemented." +#~ msgstr "Malkovich" + +#~ msgid "invoking method:" +#~ msgstr "Malkovich" + +#~ msgid "Close _without Saving" +#~ msgstr "Malkovich" + +#~ msgid "translator_credits" +#~ msgstr "Malkovich" + +#~ msgid "About %s." +#~ msgstr "Malkovich" + +#~ msgid "%s: (c) 2003 The National Gallery" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s comes with ABSOLUTELY NO WARRANTY. This is free software\n" +#~ "and you are welcome to redistribute it under certain conditions,\n" +#~ "see http://www.gnu.org." +#~ msgstr "Malkovich" + +#~ msgid "Homepage:" +#~ msgstr "Malkovich" + +#~ msgid "Linked to VIPS library version:" +#~ msgstr "Malkovich" + +#~ msgid "Temp files in:" +#~ msgstr "Malkovich" + +#~ msgid "Help page not found." +#~ msgstr "Malkovich" + +#~ msgid "No indexed help page found for tag \"%s\"" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Search for" +#~ msgstr "Malkovich" + +#~ msgid "Case sensitive" +#~ msgstr "Malkovich" + +#~ msgid "Regular expression" +#~ msgstr "Malkovich" + +#~ msgid "Search from start" +#~ msgstr "Malkovich" + +#~ msgid "Unable to open location." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to open URL \"%s\"\n" +#~ "windows error code = %d" +#~ msgstr "Malkovich" + +#~ msgid "Browser window opened." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Opened window for URL:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "This may take a few seconds." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Attempted to launch browser with command:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "You can change this command in Preferences." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Opened window for URL:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "You may need to switch desktops to see the new window." +#~ msgstr "Malkovich" + +#~ msgid "Cancelling" +#~ msgstr "Malkovich" + +#~ msgid "Computing image." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Calculating all the pixels in an image. Press the\n" +#~ "Cancel button to stop computation early." +#~ msgstr "Malkovich" + +#~ msgid "Calculating" +#~ msgstr "Malkovich" + +#~ msgid "About %d seconds left" +#~ msgstr "Malkovich" + +#~ msgid "Thumbnails" +#~ msgstr "Malkovich" + +#~ msgid "Image files found in: \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Searching ..." +#~ msgstr "Malkovich" + +#~ msgid "Search incomplete!" +#~ msgstr "Malkovich" + +#~ msgid "Not implemented." +#~ msgstr "Malkovich" + +#~ msgid "Complex math ops not implemented." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Macro error." +#~ msgstr "Malkovich" + +#~ msgid "Builtin \"%s\" takes %d argument." +#~ msgid_plural "Builtin \"%s\" takes %d arguments." +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "" +#~ "Argument %d to builtin \"%s\" should be \"%s\"\n" +#~ "you passed:\n" +#~ " %s" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Object %s\n" +#~ "is not a class." +#~ msgstr "Malkovich" + +#~ msgid "Member \"%s\" not found in class \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Too many arguments to superclass constructor \"%s\".\n" +#~ "No more than %d arguments are possible." +#~ msgstr "Malkovich" + +#~ msgid "Superclass constructor \"%s\" expects %d arguments, not %d." +#~ msgstr "Malkovich" + +#~ msgid "Bad superclass." +#~ msgstr "Malkovich" + +#~ msgid "Superclass constructor \"%s\" should have no secret arguments." +#~ msgstr "Malkovich" + +#~ msgid "First element in superclass of \"%s\" must be class or constructor." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Too many arguments to class constructor \"%s\".\n" +#~ "No more than %d arguments are possible." +#~ msgstr "Malkovich" + +#~ msgid "Class not found." +#~ msgstr "Malkovich" + +#~ msgid "Class \"%s\" not found." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Member \"%s\" of class \"%s\" should be of type \"%s\", instead it's:" +#~ msgstr "Malkovich" + +#~ msgid "_%s() not implemented for class \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Edit color \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Set color" +#~ msgstr "Malkovich" + +#~ msgid "Bad value." +#~ msgstr "Malkovich" + +#~ msgid "Double-click to edit this color, or drag-and-drop between colors" +#~ msgstr "Malkovich" + +#~ msgid "Name clash." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't create column \"%s\".\n" +#~ "A column with that name already exists." +#~ msgstr "Malkovich" + +#~ msgid "Empty column." +#~ msgstr "Malkovich" + +#~ msgid "There are no objects in the current column." +#~ msgstr "Malkovich" + +#~ msgid "Too few items." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "This column only has %d items, but %d items\n" +#~ "are needed by operation \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Save Column \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Name" +#~ msgstr "Malkovich" + +#~ msgid "Toolkit" +#~ msgstr "Malkovich" + +#~ msgid "Filename" +#~ msgstr "Malkovich" + +#~ msgid "Set menu item text here" +#~ msgstr "Malkovich" + +#~ msgid "Add to this toolkit" +#~ msgstr "Malkovich" + +#~ msgid "Store column in this file" +#~ msgstr "Malkovich" + +#~ msgid "New menu item from column \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Menuize" +#~ msgstr "Malkovich" + +#~ msgid "Edit caption, press enter to accept changes, press escape to cancel" +#~ msgstr "Malkovich" + +#~ msgid "Enter expressions here" +#~ msgstr "Malkovich" + +#~ msgid "Close the columnview" +#~ msgstr "Malkovich" + +#~ msgid "Open the columnview" +#~ msgstr "Malkovich" + +#~ msgid "Column menu" +#~ msgstr "Malkovich" + +#~ msgid "Edit caption ..." +#~ msgstr "Malkovich" + +#~ msgid "Select all" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Make column into menu item ..." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Left-drag to move, left-double-click to set title, right-click for menu" +#~ msgstr "Malkovich" + +#~ msgid "Delete the column" +#~ msgstr "Malkovich" + +#~ msgid "Too many shared nodes in graph." +#~ msgstr "Malkovich" + +#~ msgid "Disable optimisation, or raise MAX_RELOC" +#~ msgstr "Malkovich" + +#~ msgid "Member \"%s\" of class \"%s\" should have no arguments." +#~ msgstr "Malkovich" + +#~ msgid "Unable to find image range." +#~ msgstr "Malkovich" + +#~ msgid "Find image range failed." +#~ msgstr "Malkovich" + +#~ msgid "Convert menu" +#~ msgstr "Malkovich" + +#~ msgid "Scale" +#~ msgstr "Malkovich" + +#~ msgid "Interpret" +#~ msgstr "Malkovich" + +#~ msgid "Reset" +#~ msgstr "Malkovich" + +#~ msgid "value" +#~ msgstr "Malkovich" + +#~ msgid "zombie" +#~ msgstr "Malkovich" + +#~ msgid "workspace" +#~ msgstr "Malkovich" + +#~ msgid "workspace group" +#~ msgstr "Malkovich" + +#~ msgid "root symbol" +#~ msgstr "Malkovich" + +#~ msgid "external symbol" +#~ msgstr "Malkovich" + +#~ msgid "built-in symbol" +#~ msgstr "Malkovich" + +#~ msgid "Escape to cancel edit, press Return to accept edit and recalculate" +#~ msgstr "Malkovich" + +#~ msgid "top level" +#~ msgstr "Malkovich" + +#~ msgid "class" +#~ msgstr "Malkovich" + +#~ msgid "instance" +#~ msgstr "Malkovich" + +#~ msgid "definition" +#~ msgstr "Malkovich" + +#~ msgid "parameter \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "member" +#~ msgstr "Malkovich" + +#~ msgid "function" +#~ msgstr "Malkovich" + +#~ msgid "of" +#~ msgstr "Malkovich" + +#~ msgid "XML library error." +#~ msgstr "Malkovich" + +#~ msgid "model_save_filename: xmlNewDoc() failed" +#~ msgstr "Malkovich" + +#~ msgid "model_save_filename: xmlNewDocNode() failed" +#~ msgstr "Malkovich" + +#~ msgid "Save failed." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Save of %s \"%s\" to file \"%s\" failed.\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "filemodel_save_all: no save method" +#~ msgstr "Malkovich" + +#~ msgid "Load failed." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't load XML file \"%s\",\n" +#~ "it's not a %s save file" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't load XML file \"%s\",\n" +#~ "Unable to extract version information from namespace." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't load XML file \"%s\",\n" +#~ "The file does not contain a %s." +#~ msgstr "Malkovich" + +#~ msgid "Save %s %s" +#~ msgstr "Malkovich" + +#~ msgid "Save successful." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s \"%s\" successfully saved\n" +#~ "to file \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Object has been modified." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s \"%s\" has been modified since you loaded it\n" +#~ "from file \"%s\".\n" +#~ "Do you want to save your changes?" +#~ msgstr "Malkovich" + +#~ msgid "%s \"%s\" has been modified. Do you want to save your changes?" +#~ msgstr "Malkovich" + +#~ msgid "Select file" +#~ msgstr "Malkovich" + +#~ msgid "Choose" +#~ msgstr "Malkovich" + +#~ msgid "TIFF image files (*.tif, *.tiff)" +#~ msgstr "Malkovich" + +#~ msgid "JPEG image files (*.jpg, *.jpeg, *.jpe)" +#~ msgstr "Malkovich" + +#~ msgid "PNG image files (*.png)" +#~ msgstr "Malkovich" + +#~ msgid "VIPS image files (*.v)" +#~ msgstr "Malkovich" + +#~ msgid "PPM image files (*.ppm, *.pgm, *.pbm)" +#~ msgstr "Malkovich" + +#~ msgid "Workspace files (*.ws)" +#~ msgstr "Malkovich" + +#~ msgid "Recombination matrix files (*.rec)" +#~ msgstr "Malkovich" + +#~ msgid "Morphology matrix files (*.mor)" +#~ msgstr "Malkovich" + +#~ msgid "Convolution matrix files (*.con)" +#~ msgstr "Malkovich" + +#~ msgid "Matrix files (*.mat)" +#~ msgstr "Malkovich" + +#~ msgid "Definition files (*.def)" +#~ msgstr "Malkovich" + +#~ msgid "ICC profiles (*.icc, *.icm)" +#~ msgstr "Malkovich" + +#~ msgid "All files (*)" +#~ msgstr "Malkovich" + +#~ msgid "Unable to determine space free in \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "free in \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Increment filename" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "After Save, add 1 to the last number in the file name" +#~ msgstr "Malkovich" + +#~ msgid "Show thumbnails" +#~ msgstr "Malkovich" + +#~ msgid "Show thumbnails for files in this directory" +#~ msgstr "Malkovich" + +#~ msgid "Overwrite" +#~ msgstr "Malkovich" + +#~ msgid "File \"%s\" exists. OK to overwrite?" +#~ msgstr "Malkovich" + +#~ msgid "circular" +#~ msgstr "Malkovich" + +#~ msgid "circular to label %d" +#~ msgstr "Malkovich" + +#~ msgid "label %d" +#~ msgstr "Malkovich" + +#~ msgid "unevaluated" +#~ msgstr "Malkovich" + +#~ msgid "class (0x%x)" +#~ msgstr "Malkovich" + +#~ msgid "members" +#~ msgstr "Malkovich" + +#~ msgid "secret" +#~ msgstr "Malkovich" + +#~ msgid "from" +#~ msgstr "Malkovich" + +#~ msgid "NULL pointer" +#~ msgstr "Malkovich" + +#~ msgid "symbol" +#~ msgstr "Malkovich" + +#~ msgid "constructor" +#~ msgstr "Malkovich" + +#~ msgid "symref" +#~ msgstr "Malkovich" + +#~ msgid "compileref" +#~ msgstr "Malkovich" + +#~ msgid "image \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "tag \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "unknown element tag %d" +#~ msgstr "Malkovich" + +#~ msgid "Current:" +#~ msgstr "Malkovich" + +#~ msgid "Directories" +#~ msgstr "Malkovich" + +#~ msgid "Files" +#~ msgstr "Malkovich" + +#~ msgid "Selection:" +#~ msgstr "Malkovich" + +#~ msgid "Create Dir" +#~ msgstr "Malkovich" + +#~ msgid "Delete File" +#~ msgstr "Malkovich" + +#~ msgid "Rename File" +#~ msgstr "Malkovich" + +#~ msgid "Error" +#~ msgstr "Malkovich" + +#~ msgid "Close" +#~ msgstr "Malkovich" + +#~ msgid "Create Directory" +#~ msgstr "Malkovich" + +#~ msgid "Directory name:" +#~ msgstr "Malkovich" + +#~ msgid "Create" +#~ msgstr "Malkovich" + +#~ msgid "Cancel" +#~ msgstr "Malkovich" + +#~ msgid "Delete" +#~ msgstr "Malkovich" + +#~ msgid "Rename" +#~ msgstr "Malkovich" + +#~ msgid "Directory unreadable: " +#~ msgstr "Malkovich" + +#~ msgid "Current: " +#~ msgstr "Malkovich" + +#~ msgid "Bad identifier." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Enter an identifier. Identifiers start with\n" +#~ "a letter, and then contain only letters, numbers,\n" +#~ "apostrophy and underscore." +#~ msgstr "Malkovich" + +#~ msgid "Bad floating point number." +#~ msgstr "Malkovich" + +#~ msgid "\"%s\" is not a floating point number." +#~ msgstr "Malkovich" + +#~ msgid "Bad integer." +#~ msgstr "Malkovich" + +#~ msgid "\"%s\" is not an integer." +#~ msgstr "Malkovich" + +#~ msgid "Bad unsigned integer." +#~ msgstr "Malkovich" + +#~ msgid "Bad positive integer." +#~ msgstr "Malkovich" + +#~ msgid "Left-click to change value" +#~ msgstr "Malkovich" + +#~ msgid "Heap full." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "The main calculation heap has filled. Raise\n" +#~ "the heap size limit in the Preferences panel." +#~ msgstr "Malkovich" + +#~ msgid "Typecheck error." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Expected %s, instead saw:\n" +#~ " %s" +#~ msgstr "Malkovich" + +#~ msgid "on" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "at %d" +#~ msgstr "Malkovich" + +#~ msgid "at (%d, %d)" +#~ msgstr "Malkovich" + +#~ msgid "at (%d, %d), offset (%d, %d)" +#~ msgstr "Malkovich" + +#~ msgid "Pin up" +#~ msgstr "Malkovich" + +#~ msgid "Check this to pin the dialog up" +#~ msgstr "Malkovich" + +#~ msgid "Save Image \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Replace Image \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Double-click to open a viewer on this thumbnail" +#~ msgstr "Malkovich" + +#~ msgid "Unable to open image \"%s\" for read." +#~ msgstr "Malkovich" + +#~ msgid "Unable to write to file." +#~ msgstr "Malkovich" + +#~ msgid "File \"%s\" is already open for read." +#~ msgstr "Malkovich" + +#~ msgid "Error writing image to file \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Unable to paint on image." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to get write permission for file \"%s\".\n" +#~ "Check permission settings." +#~ msgstr "Malkovich" + +#~ msgid "Modify" +#~ msgstr "Malkovich" + +#~ msgid "Modify disc file?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "This image is being shown directly from the disc file:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "If you paint on this file, it will be permanently changed.\n" +#~ "If something goes wrong, you may lose work.\n" +#~ "Are you sure you want to modify this file?" +#~ msgstr "Malkovich" + +#~ msgid "No image value" +#~ msgstr "Malkovich" + +#~ msgid "%dx%d %s pixels, %d band, %s" +#~ msgid_plural "%dx%d %s pixels, %d bands, %s" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "Ruler menu" +#~ msgstr "Malkovich" + +#~ msgid "Header for \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "OK" +#~ msgstr "Malkovich" + +#~ msgid "/_File" +#~ msgstr "Malkovich" + +#~ msgid "/File/_New" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_Point" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_HGuide" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_VGuide" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_Arrow" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_Region" +#~ msgstr "Malkovich" + +#~ msgid "/File/sep3" +#~ msgstr "Malkovich" + +#~ msgid "/File/_Replace Image ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Save Image As ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/sep1" +#~ msgstr "Malkovich" + +#~ msgid "/File/Image _Header" +#~ msgstr "Malkovich" + +#~ msgid "/File/Re_calculate Image" +#~ msgstr "Malkovich" + +#~ msgid "/File/sep2" +#~ msgstr "Malkovich" + +#~ msgid "/File/_Close" +#~ msgstr "Malkovich" + +#~ msgid "/_View" +#~ msgstr "Malkovich" + +#~ msgid "/View/_Toolbar" +#~ msgstr "Malkovich" + +#~ msgid "/View/Toolbar/_Status" +#~ msgstr "Malkovich" + +#~ msgid "/View/Toolbar/_Display control" +#~ msgstr "Malkovich" + +#~ msgid "/View/Toolbar/_Paint" +#~ msgstr "Malkovich" + +#~ msgid "/View/Toolbar/_Rulers" +#~ msgstr "Malkovich" + +#~ msgid "/View/M_ode" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/_Select" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/_Pan" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/Zoom _In" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/Zoom _Out" +#~ msgstr "Malkovich" + +#~ msgid "/View/Mode/P_aint" +#~ msgstr "Malkovich" + +#~ msgid "/View/sep1" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom _In" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom _Out" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom _100%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom to _Fit" +#~ msgstr "Malkovich" + +#~ msgid "/View/_Zoom" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/6%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/12%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/25%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/50%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/100%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/200%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/400%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/800%" +#~ msgstr "Malkovich" + +#~ msgid "/View/Zoom/1600%" +#~ msgstr "Malkovich" + +#~ msgid "/_Help" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Image View" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Paint Bar" +#~ msgstr "Malkovich" + +#~ msgid "Left" +#~ msgstr "Malkovich" + +#~ msgid "Top" +#~ msgstr "Malkovich" + +#~ msgid "Width" +#~ msgstr "Malkovich" + +#~ msgid "Height" +#~ msgstr "Malkovich" + +#~ msgid "Left edge of region" +#~ msgstr "Malkovich" + +#~ msgid "Top edge of region" +#~ msgstr "Malkovich" + +#~ msgid "Width of region" +#~ msgstr "Malkovich" + +#~ msgid "Height of region" +#~ msgstr "Malkovich" + +#~ msgid "Edit \"%s\"" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Set Region" +#~ msgstr "Malkovich" + +#~ msgid "at (%d, %d), size (%d, %d)" +#~ msgstr "Malkovich" + +#~ msgid "%dx%d %s pixels, %d band" +#~ msgid_plural "%dx%d %s pixels, %d bands" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "scope \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "reference to symbol \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Press Escape to cancel edit, press Return to accept edit and recalculate" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Value display\n" +#~ "Left-click to edit expression" +#~ msgstr "Malkovich" + +#~ msgid "line too long" +#~ msgstr "Malkovich" + +#~ msgid "end of line inside string" +#~ msgstr "Malkovich" + +#~ msgid "no end of string" +#~ msgstr "Malkovich" + +#~ msgid "no end of comment!" +#~ msgstr "Malkovich" + +#~ msgid "bad char constant" +#~ msgstr "Malkovich" + +#~ msgid "illegal character \"%c\"" +#~ msgstr "Malkovich" + +#~ msgid "Circular dependency." +#~ msgstr "Malkovich" + +#~ msgid "Circular dependency detected near symbol \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s: usage:\n" +#~ "%s filename1 filename2 ...\n" +#~ "\tstart in GUI mode, loading the named files" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s -script filename arg1 arg2 ...\n" +#~ "\tread in filename as a set of definitions, set list argv to\n" +#~ "\t[\"filename\", \"arg1\", \"arg2\", ...], set argc to length of list;\n" +#~ "\tprint the value of symbol \"main\" to stdout; exit; useful for\n" +#~ "\trunning %s as an interpreter on unix" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s -main arg1 arg2 ...\n" +#~ "\tas -script, but read the definitions from stdin rather than from a\n" +#~ "\tfile; useful for here-is shell scripts" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s -workspace arg1 arg2 ...\n" +#~ "\tas -main, but read a workspace save file instead; run in GUI mode\n" +#~ "\tprint the value of the final row in the save file on exit" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "%s -benchmark\n" +#~ "\tload all start objects and quit; useful for bechmarking the compiler" +#~ msgstr "Malkovich" + +#~ msgid "symbol \"main\" not found" +#~ msgstr "Malkovich" + +#~ msgid "symbol \"main\" has no value" +#~ msgstr "Malkovich" + +#~ msgid "Unknown file type." +#~ msgstr "Malkovich" + +#~ msgid "Unable to load \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Unable to load." +#~ msgstr "Malkovich" + +#~ msgid "Error loading plug-in \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Error loading plug-ins in directory \"%s/lib\"" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Ink dropper" +#~ msgstr "Malkovich" + +#~ msgid "Duplicate" +#~ msgstr "Malkovich" + +#~ msgid "Pen" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Line" +#~ msgstr "Malkovich" + +#~ msgid "Text" +#~ msgstr "Malkovich" + +#~ msgid "Smudge" +#~ msgstr "Malkovich" + +#~ msgid "Flood" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Flood Blob" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Fill Rectangle" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Pan" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Select" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "unable to change max file descriptors\n" +#~ "max file descriptors still set to %d" +#~ msgstr "Malkovich" + +#~ msgid "unable to read max file descriptors" +#~ msgstr "Malkovich" + +#~ msgid "-script needs an argument" +#~ msgstr "Malkovich" + +#~ msgid "Startup error." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Startup error log:\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "Welcome to %s-%s!" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "A new directory has been created in your home directory to hold startup,\n" +#~ "data and temporary files:\n" +#~ "\n" +#~ " %s\n" +#~ "\n" +#~ "If you've used previous versions of %s, you will probably want\n" +#~ "to move any files over from your old work area and remove any old temps." +#~ msgstr "Malkovich" + +#~ msgid "No temp area" +#~ msgstr "Malkovich" + +#~ msgid "%s free" +#~ msgstr "Malkovich" + +#~ msgid "%d cells free" +#~ msgstr "Malkovich" + +#~ msgid "Selected:" +#~ msgstr "Malkovich" + +#~ msgid "%s in \"%s\", %d cells in heap, %d cells free, %d cells maximum" +#~ msgstr "Malkovich" + +#~ msgid "modified" +#~ msgstr "Malkovich" + +#~ msgid "No objects selected." +#~ msgstr "Malkovich" + +#~ msgid "Find in workspace not implemented yet." +#~ msgstr "Malkovich" + +#~ msgid "Find again in workspace not implemented yet." +#~ msgstr "Malkovich" + +#~ msgid "There are no errors (that I can see) in this workspace." +#~ msgstr "Malkovich" + +#~ msgid "Recalculate" +#~ msgstr "Malkovich" + +#~ msgid "Completely recalculate?" +#~ msgstr "Malkovich" + +#~ msgid "Insert image" +#~ msgstr "Malkovich" + +#~ msgid "Merge workspace" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "No recent workspaces" +#~ msgstr "Malkovich" + +#~ msgid "Open workspace" +#~ msgstr "Malkovich" + +#~ msgid "Caption" +#~ msgstr "Malkovich" + +#~ msgid "Set workspace name here" +#~ msgstr "Malkovich" + +#~ msgid "Set workspace caption here" +#~ msgstr "Malkovich" + +#~ msgid "New Workspace" +#~ msgstr "Malkovich" + +#~ msgid "Create Workspace" +#~ msgstr "Malkovich" + +#~ msgid "Set column name here" +#~ msgstr "Malkovich" + +#~ msgid "Set column caption here" +#~ msgstr "Malkovich" + +#~ msgid "New Column" +#~ msgstr "Malkovich" + +#~ msgid "Create Column" +#~ msgstr "Malkovich" + +#~ msgid "Delete selected objects?" +#~ msgstr "Malkovich" + +#~ msgid "Are you sure you want to delete %s?" +#~ msgstr "Malkovich" + +#~ msgid "/File/_New ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Open ..." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "/File/Open _Recent" +#~ msgstr "Malkovich" + +#~ msgid "/File/_Save" +#~ msgstr "Malkovich" + +#~ msgid "/File/_Save As ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Program" +#~ msgstr "Malkovich" + +#~ msgid "/File/Re_cover After Crash ..." +#~ msgstr "Malkovich" + +#~ msgid "/_Edit" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Delete" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Select All" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Duplicate" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/sep2" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Find" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Find _Next" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Jump To Next Error" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/sep4" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Group" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/U_ngroup" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Recalculate" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/sep3" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Preferences" +#~ msgstr "Malkovich" + +#~ msgid "/View/_Statusbar" +#~ msgstr "Malkovich" + +#~ msgid "/View/_Regular" +#~ msgstr "Malkovich" + +#~ msgid "/View/Show _Formula" +#~ msgstr "Malkovich" + +#~ msgid "/View/No _Edits" +#~ msgstr "Malkovich" + +#~ msgid "/_Insert" +#~ msgstr "Malkovich" + +#~ msgid "/Insert/New C_olumn ..." +#~ msgstr "Malkovich" + +#~ msgid "/Insert/sep6" +#~ msgstr "Malkovich" + +#~ msgid "/Insert/Ima_ge From File ..." +#~ msgstr "Malkovich" + +#~ msgid "/Insert/Workspace From File ..." +#~ msgstr "Malkovich" + +#~ msgid "/Insert/Matrix From File ..." +#~ msgstr "Malkovich" + +#~ msgid "/_Toolkits" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_About" +#~ msgstr "Malkovich" + +#~ msgid "/Help/sep7" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_This Window" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Users Guide" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Quick Tour" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Mosaic Tour" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Nerd Tour" +#~ msgstr "Malkovich" + +#~ msgid "/Help/M_enu Reference" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Configuration" +#~ msgstr "Malkovich" + +#~ msgid "/Help/sep6" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Go to VIPS Home Page" +#~ msgstr "Malkovich" + +#~ msgid "Open Workspace" +#~ msgstr "Malkovich" + +#~ msgid "Save Workspace As" +#~ msgstr "Malkovich" + +#~ msgid "Duplicate Workspace" +#~ msgstr "Malkovich" + +#~ msgid "Duplicate Selected Rows" +#~ msgstr "Malkovich" + +#~ msgid "Insert File" +#~ msgstr "Malkovich" + +#~ msgid "Sliders" +#~ msgstr "Malkovich" + +#~ msgid "Toggle buttons" +#~ msgstr "Malkovich" + +#~ msgid "Text, plus scale and offset" +#~ msgstr "Malkovich" + +#~ msgid "Display as" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Edit matrix \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Set matrix" +#~ msgstr "Malkovich" + +#~ msgid "Save Matrix \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Replace Matrix \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Cell (%d, %d):\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid ": cell (%d, %d): %s" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to load from file \"%s\".\n" +#~ "Error log is:\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to load from \"%s\".\n" +#~ "Error log is:\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "model_save: xmlNewChild() failed" +#~ msgstr "Malkovich" + +#~ msgid "XML load error." +#~ msgstr "Malkovich" + +#~ msgid "Can't load node of type \"%s\" into object of type \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Delete?" +#~ msgstr "Malkovich" + +#~ msgid "Are you sure you want to delete %s \"%s\"?" +#~ msgstr "Malkovich" + +#~ msgid "No options." +#~ msgstr "Malkovich" + +#~ msgid "You need at least one option in your option list" +#~ msgstr "Malkovich" + +#~ msgid "Set option caption here" +#~ msgstr "Malkovich" + +#~ msgid "Options" +#~ msgstr "Malkovich" + +#~ msgid "Value" +#~ msgstr "Malkovich" + +#~ msgid "Set option default value here" +#~ msgstr "Malkovich" + +#~ msgid "Edit option" +#~ msgstr "Malkovich" + +#~ msgid "Set option" +#~ msgstr "Malkovich" + +#~ msgid "Orderlist menu" +#~ msgstr "Malkovich" + +#~ msgid "Delete item" +#~ msgstr "Malkovich" + +#~ msgid "Delete selected items" +#~ msgstr "Malkovich" + +#~ msgid "Delete all items" +#~ msgstr "Malkovich" + +#~ msgid "Current options - right button for menu" +#~ msgstr "Malkovich" + +#~ msgid "Enter new option fields here" +#~ msgstr "Malkovich" + +#~ msgid "Paintbox bar menu" +#~ msgstr "Malkovich" + +#~ msgid "Font not found." +#~ msgstr "Malkovich" + +#~ msgid "Font \"%s\" not found on system." +#~ msgstr "Malkovich" + +#~ msgid "Select font" +#~ msgstr "Malkovich" + +#~ msgid "Set font" +#~ msgstr "Malkovich" + +#~ msgid "Clear undo history?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Are you sure you want to clear all undo and redo?\n" +#~ "This will free up memory, but you will no longer be\n" +#~ "able to undo or redo any of the painting you have\n" +#~ "done so far." +#~ msgstr "Malkovich" + +#~ msgid "1 round" +#~ msgstr "Malkovich" + +#~ msgid "2 round" +#~ msgstr "Malkovich" + +#~ msgid "3 round" +#~ msgstr "Malkovich" + +#~ msgid "4 round" +#~ msgstr "Malkovich" + +#~ msgid "5 round" +#~ msgstr "Malkovich" + +#~ msgid "6 round" +#~ msgstr "Malkovich" + +#~ msgid "10 round" +#~ msgstr "Malkovich" + +#~ msgid "2 italic" +#~ msgstr "Malkovich" + +#~ msgid "3 italic" +#~ msgstr "Malkovich" + +#~ msgid "4 italic" +#~ msgstr "Malkovich" + +#~ msgid "5 italic" +#~ msgstr "Malkovich" + +#~ msgid "6 italic" +#~ msgstr "Malkovich" + +#~ msgid "10 italic" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Pan window" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Zoom out" +#~ msgstr "Malkovich" + +#~ msgid "Undo last paint action" +#~ msgstr "Malkovich" + +#~ msgid "Redo last paint action" +#~ msgstr "Malkovich" + +#~ msgid "Clear all undo and redo buffers" +#~ msgstr "Malkovich" + +#~ msgid "Nib" +#~ msgstr "Malkovich" + +#~ msgid "Click to select font" +#~ msgstr "Malkovich" + +#~ msgid "Enter text for text tool" +#~ msgstr "Malkovich" + +#~ msgid "Error in %s: %s" +#~ msgstr "Malkovich" + +#~ msgid "definition is too long!" +#~ msgstr "Malkovich" + +#~ msgid "not top level" +#~ msgstr "Malkovich" + +#~ msgid "not strings" +#~ msgstr "Malkovich" + +#~ msgid "Not found." +#~ msgstr "Malkovich" + +#~ msgid "File \"%s\" not found on path" +#~ msgstr "Malkovich" + +#~ msgid "Revert to Defaults" +#~ msgstr "Malkovich" + +#~ msgid "Revert to installation defaults?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Would you like to reset all preferences to their factory\n" +#~ "settings? This will delete any changes you have ever made\n" +#~ "to your preferences and may take a few seconds." +#~ msgstr "Malkovich" + +#~ msgid "Preferences" +#~ msgstr "Malkovich" + +#~ msgid "Unable to display preferences." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "No preferences workspace was found.\n" +#~ "Preferences probably failed to load when %s started." +#~ msgstr "Malkovich" + +#~ msgid "Program" +#~ msgstr "Malkovich" + +#~ msgid "Edit window" +#~ msgstr "Malkovich" + +#~ msgid "Menu item text" +#~ msgstr "Malkovich" + +#~ msgid "Load column from this file" +#~ msgstr "Malkovich" + +#~ msgid "Edit column item \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Set column item" +#~ msgstr "Malkovich" + +#~ msgid "Unable to save." +#~ msgstr "Malkovich" + +#~ msgid "You can only save toolkits, not tools." +#~ msgstr "Malkovich" + +#~ msgid "You can't save auto-generated toolkits." +#~ msgstr "Malkovich" + +#~ msgid "Toolkit tree menu" +#~ msgstr "Malkovich" + +#~ msgid "Edit ..." +#~ msgstr "Malkovich" + +#~ msgid "Tool changed." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Tool changed by someone else!\n" +#~ "You're going to loose edits unless you're careful." +#~ msgstr "Malkovich" + +#~ msgid "Bad drag." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Sorry, you can only drag tools between toolkits\n" +#~ "You can't reorder toolkits, you can't nest toolkits\n" +#~ "and you can't drag tools to the top level." +#~ msgstr "Malkovich" + +#~ msgid "Sorry, you can't drag to or from from pseudo toolkits." +#~ msgstr "Malkovich" + +#~ msgid "Set toolkit name here" +#~ msgstr "Malkovich" + +#~ msgid "Set toolkit caption here" +#~ msgstr "Malkovich" + +#~ msgid "New toolkit" +#~ msgstr "Malkovich" + +#~ msgid "Nothing selected." +#~ msgstr "Malkovich" + +#~ msgid "No toolkit selected." +#~ msgstr "Malkovich" + +#~ msgid "Display this name" +#~ msgstr "Malkovich" + +#~ msgid "Load definition" +#~ msgstr "Malkovich" + +#~ msgid "Reload" +#~ msgstr "Malkovich" + +#~ msgid "Reload startup objects?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Would you like to reload all startup menus, workspaces\n" +#~ "and plugins now? This may take a few seconds." +#~ msgstr "Malkovich" + +#~ msgid "No tool selected" +#~ msgstr "Malkovich" + +#~ msgid "Bad regular expression." +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "No match found for \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Find in all toolkits" +#~ msgstr "Malkovich" + +#~ msgid "Enter search string here" +#~ msgstr "Malkovich" + +#~ msgid "No match found." +#~ msgstr "Malkovich" + +#~ msgid "No top-level symbol called \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Symbol \"%s\" has no tool inforation." +#~ msgstr "Malkovich" + +#~ msgid "Go to definition of this symbol" +#~ msgstr "Malkovich" + +#~ msgid "Go to definition" +#~ msgstr "Malkovich" + +#~ msgid "Object information." +#~ msgstr "Malkovich" + +#~ msgid "No unresolved symbols found." +#~ msgstr "Malkovich" + +#~ msgid "Link report." +#~ msgstr "Malkovich" + +#~ msgid "No documentation available." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "On-line documentation is only currently\n" +#~ "available for VIPS functions and nip builtins." +#~ msgstr "Malkovich" + +#~ msgid "/File/New/_Tool" +#~ msgstr "Malkovich" + +#~ msgid "/File/New/Tool_kit ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/New/Separator ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/New/Column Item ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/New/Program Window ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Open Toolkit ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Save Toolkit" +#~ msgstr "Malkovich" + +#~ msgid "/File/Save Toolkit _As ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/Pr_ocess Text ..." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Reload Start Stuff ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/C_ut" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Copy" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Paste" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Select All" +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Delete This _Tool ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/Delete This Tool_kit ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Find ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Jump to Definition of ..." +#~ msgstr "Malkovich" + +#~ msgid "/Edit/_Info ..." +#~ msgstr "Malkovich" + +#~ msgid "/_Debug" +#~ msgstr "Malkovich" + +#~ msgid "/Debug/_Trace ..." +#~ msgstr "Malkovich" + +#~ msgid "/Debug/_Link ..." +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Program ..." +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Documentation For This Tool ..." +#~ msgstr "Malkovich" + +#~ msgid "Overflow error." +#~ msgstr "Malkovich" + +#~ msgid "%s too long." +#~ msgstr "Malkovich" + +#~ msgid "Not rectangular." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Matrix of real is not rectangular.\n" +#~ "Found row of length %d, should be %d." +#~ msgstr "Malkovich" + +#~ msgid "List only has %d elements, unable to get element %d." +#~ msgstr "Malkovich" + +#~ msgid "No arguments allowed." +#~ msgstr "Malkovich" + +#~ msgid "Object \"%s\" should have no arguments." +#~ msgstr "Malkovich" + +#~ msgid "C stack overflow. Expression too complex." +#~ msgstr "Malkovich" + +#~ msgid "No value." +#~ msgstr "Malkovich" + +#~ msgid "Symbol \"%s\" has no value" +#~ msgstr "Malkovich" + +#~ msgid "Symbol \"%s\" is not defined." +#~ msgstr "Malkovich" + +#~ msgid "Can't duplicate." +#~ msgstr "Malkovich" + +#~ msgid "You can only duplicate top level regions." +#~ msgstr "Malkovich" + +#~ msgid "Can't delete." +#~ msgstr "Malkovich" + +#~ msgid "You can only delete top level regions." +#~ msgstr "Malkovich" + +#~ msgid "Delete Region?" +#~ msgstr "Malkovich" + +#~ msgid "Are you sure you want to delete Region \"%s\"?" +#~ msgstr "Malkovich" + +#~ msgid "Region menu" +#~ msgstr "Malkovich" + +#~ msgid "blocked on" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Left-click to select, shift-left-click to extend select, double-left-" +#~ "click to edit, right-click for menu, left-drag to move" +#~ msgstr "Malkovich" + +#~ msgid "You can only duplicate top level rows." +#~ msgstr "Malkovich" + +#~ msgid "You can only delete top level rows." +#~ msgstr "Malkovich" + +#~ msgid "Drag between columns not yet implemented." +#~ msgstr "Malkovich" + +#~ msgid "Row menu" +#~ msgstr "Malkovich" + +#~ msgid "Ungroup" +#~ msgstr "Malkovich" + +#~ msgid "Replace from file ..." +#~ msgstr "Malkovich" + +#~ msgid "Click to open or close class" +#~ msgstr "Malkovich" + +#~ msgid "Minimum" +#~ msgstr "Malkovich" + +#~ msgid "Maximum" +#~ msgstr "Malkovich" + +#~ msgid "Lower slider value" +#~ msgstr "Malkovich" + +#~ msgid "Upper slider value" +#~ msgstr "Malkovich" + +#~ msgid "Set slider value here" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Edit slider \"%s\"" +#~ msgstr "Malkovich" + +#, fuzzy +#~ msgid "Set Slider" +#~ msgstr "Malkovich" + +#~ msgid "Status bar menu" +#~ msgstr "Malkovich" + +#~ msgid "Magnification" +#~ msgstr "Malkovich" + +#~ msgid "Attempt to redefine root symbol \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Name \"%s\" repeated in scope." +#~ msgstr "Malkovich" + +#~ msgid "Can't redefine %s \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Set toggle caption here" +#~ msgstr "Malkovich" + +#~ msgid "State" +#~ msgstr "Malkovich" + +#~ msgid "Set toggle state here" +#~ msgstr "Malkovich" + +#~ msgid "Edit toggle" +#~ msgstr "Malkovich" + +#~ msgid "Set toggle" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't create dialog with name \"%s\"\n" +#~ "an object with that name already exists in kit \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "tool \"%s\", toolkit \"%s\", refers to undefined symbol" +#~ msgstr "Malkovich" + +#~ msgid "Trace buffer stack overflow." +#~ msgstr "Malkovich" + +#~ msgid "/File/_Clear" +#~ msgstr "Malkovich" + +#~ msgid "/View/Operators" +#~ msgstr "Malkovich" + +#~ msgid "/View/Builtin Functions" +#~ msgstr "Malkovich" + +#~ msgid "/View/Class Construction" +#~ msgstr "Malkovich" + +#~ msgid "/Help/_Trace ..." +#~ msgstr "Malkovich" + +#~ msgid "Trace" +#~ msgstr "Malkovich" + +#~ msgid "Slider value ... edit!" +#~ msgstr "Malkovich" + +#~ msgid "Left-drag to set number" +#~ msgstr "Malkovich" + +#~ msgid "VIPS library error." +#~ msgstr "Malkovich" + +#~ msgid "Unable to set XML property." +#~ msgstr "Malkovich" + +#~ msgid "Unable to set property \"%s\" to value \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "8-bit signed integer" +#~ msgstr "Malkovich" + +#~ msgid "16-bit unsigned integer" +#~ msgstr "Malkovich" + +#~ msgid "16-bit signed integer" +#~ msgstr "Malkovich" + +#~ msgid "32-bit unsigned integer" +#~ msgstr "Malkovich" + +#~ msgid "32-bit signed integer" +#~ msgstr "Malkovich" + +#~ msgid "32-bit float" +#~ msgstr "Malkovich" + +#~ msgid "64-bit float" +#~ msgstr "Malkovich" + +#~ msgid "128-bit complex" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ msgstr "Malkovich" + +#~ msgid "TIFF image" +#~ msgstr "Malkovich" + +#~ msgid "JPEG image" +#~ msgstr "Malkovich" + +#~ msgid "PNG image" +#~ msgstr "Malkovich" + +#~ msgid "PPM/PGM/PBM image" +#~ msgstr "Malkovich" + +#~ msgid "VIPS image" +#~ msgstr "Malkovich" + +#~ msgid "%s, %s, %s, %dx%d, %d band" +#~ msgid_plural "%s, %s, %s, %dx%d, %d bands" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "Bad filename." +#~ msgstr "Malkovich" + +#~ msgid "Filenames may not contain ':' characters." +#~ msgstr "Malkovich" + +#~ msgid "Filename is too long." +#~ msgstr "Malkovich" + +#~ msgid "Filename contains only blank characters." +#~ msgstr "Malkovich" + +#~ msgid "Unable to open." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to open file \"%s\" for writing.\n" +#~ "%s." +#~ msgstr "Malkovich" + +#~ msgid "Unable to write." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Unable to write to file \"%s\".\n" +#~ "%s." +#~ msgstr "Malkovich" + +#~ msgid "bytes" +#~ msgstr "Malkovich" + +#~ msgid "KB" +#~ msgstr "Malkovich" + +#~ msgid "MB" +#~ msgstr "Malkovich" + +#~ msgid "GB" +#~ msgstr "Malkovich" + +#~ msgid "TB" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "unable to make temporary file \"%s\"\n" +#~ "%s" +#~ msgstr "Malkovich" + +#~ msgid "Out of memory." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Request for %s of RAM triggered memory allocation\n" +#~ "failure." +#~ msgstr "Malkovich" + +#~ msgid "Unknown type." +#~ msgstr "Malkovich" + +#~ msgid "VIPS type \"%s\" not supported" +#~ msgstr "Malkovich" + +#~ msgid "Error calling library function \"%s\" (%s)." +#~ msgstr "Malkovich" + +#~ msgid "VIPS operator \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "%s, from package \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "\"%s\" takes %d argument:" +#~ msgid_plural "\"%s\" takes %d arguments:" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "And produces %d result:" +#~ msgid_plural "And produces %d results" +#~ msgstr[0] "Malkovich" +#~ msgstr[1] "Malkovich" + +#~ msgid "flags:" +#~ msgstr "Malkovich" + +#~ msgid "PIO function" +#~ msgstr "Malkovich" + +#~ msgid "WIO function" +#~ msgstr "Malkovich" + +#~ msgid "coordinate transformer" +#~ msgstr "Malkovich" + +#~ msgid "no coordinate transformation" +#~ msgstr "Malkovich" + +#~ msgid "point-to-point operation" +#~ msgstr "Malkovich" + +#~ msgid "area operation" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Argument %d to \"%s\" is the wrong type.\n" +#~ "You passed:\n" +#~ " %s\n" +#~ "Usage:\n" +#~ " %s" +#~ msgstr "Malkovich" + +#~ msgid "doublevec" +#~ msgstr "Malkovich" + +#~ msgid "imagevec" +#~ msgstr "Malkovich" + +#~ msgid "Bad regular expression \"%s\"." +#~ msgstr "Malkovich" + +#~ msgid "Select exactly one object and try again." +#~ msgstr "Malkovich" + +#~ msgid "More than one object selected." +#~ msgstr "Malkovich" + +#~ msgid "No backup workspaces found." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "You need to enable \"Auto workspace save\" in Preferences\n" +#~ "Before automatic recovery works" +#~ msgstr "Malkovich" + +#~ msgid "No suitable workspace save files found in \"%s\"" +#~ msgstr "Malkovich" + +#~ msgid "Open workspace backup?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Found workspace \"%s\",\n" +#~ "dated %sDo you want to recover this workspace?" +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't create workspace \"%s\".\n" +#~ "A symbol with that name already exists." +#~ msgstr "Malkovich" + +#~ msgid "Default empty workspace" +#~ msgstr "Malkovich" + +#~ msgid "\"%s\" needs %d arguments, there are %d selected." +#~ msgstr "Malkovich" + +#~ msgid "Too many names selected." +#~ msgstr "Malkovich" + +#~ msgid "You can only remove top level rows." +#~ msgstr "Malkovich" + +#~ msgid "Not all selected objects are top level rows." +#~ msgstr "Malkovich" + +#~ msgid "Unable to ungroup." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "You can only ungroup lists (comma-separated lists of things enclosed in " +#~ "square brackets).\n" +#~ "Use Format=>Decompose to break compound objects into lists." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Can't create workspacegroup \"%s\".\n" +#~ "A symbol with that name already exists." +#~ msgstr "Malkovich" + +#~ msgid "No text specified." +#~ msgstr "Malkovich" + +#~ msgid "" +#~ "Enter some text to paint in the entry widget at the top of the window." +#~ msgstr "Malkovich" + +#~ msgid "Edit regions (+CTRL to create)" +#~ msgstr "Malkovich" + +#~ msgid "Pan image (also use middle mouse button)" +#~ msgstr "Malkovich" + +#~ msgid "Zoom in (also 'i' key)" +#~ msgstr "Malkovich" + +#~ msgid "Zoom out (also 'o' key)" +#~ msgstr "Malkovich" + +#~ msgid "Ink:" +#~ msgstr "Malkovich" + +#~ msgid "Find" +#~ msgstr "Malkovich" + +#~ msgid "at %1$d" +#~ msgstr "Malkovich" + +#~ msgid "Tool" +#~ msgstr "Malkovich" + +#~ msgid "Range" +#~ msgstr "Malkovich" diff --git a/po/messages b/po/messages new file mode 100644 index 0000000000000000000000000000000000000000..3e22b1860750543d0d0f09dfc44b6e00c968ae60 GIT binary patch literal 349 zcmZ{f!AiqG5QZao%F(ljJ@i&5?j{A>Rf|w76k042y^rabt|q%-vle|2U$4*N1f@Ck zhoAXjzS*7GpPl|htP}P*d+?txxOS54AvYD*j)~UZy;Bijv4I!zt#MWhYnoJCC6gqJ z*N-{OJYnDl++!duQYl9uCy+^zDlOBYWHAU&v@_iUJ(qz}AV)%_I+K6g659Z|N845y zoY$hp>cj0!u@yImcy~xi;p%C;L|cAhNstrPM@A}>IA2cV)nYa8Pf>}CXU+z){Ofna q>j(M@mraGnUW;OjzNK*coIk+0=eq6Q$iv(gZewhz#rV~PIQj+gc4GYi literal 0 HcmV?d00001 diff --git a/po/missing b/po/missing new file mode 100644 index 00000000..e69de29b diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 00000000..f232c586 --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = \ + vipsCC + +EXTRA_DIST = \ + test diff --git a/python/test/testvipsCC.py b/python/test/testvipsCC.py new file mode 100755 index 00000000..c9ab443d --- /dev/null +++ b/python/test/testvipsCC.py @@ -0,0 +1,37 @@ +#!/usr/bin/python + +import sys + +# just need this for leaktesting +import gc + +from vipsCC import * + +if len (sys.argv) != 3: + print 'usage:', sys.argv[0], 'inputimage outputimage' + print '\tcalculate photographic negative of inputimage' + sys.exit (1) + +try: + a = VImage.VImage (sys.argv[1]) + b = a.invert () + c = b.lin ([1,2,3],[4,5,6]) + c.write (sys.argv[2]) +except VError.VError, e: + e.perror (sys.argv[0]) + +# we can get properties of VImage too +print 'inputimage is', a.Xsize (), 'pixels across' + +print 'starting shutdown ...' +del b +del a +del c +# sometimes have to do several GCs to get them all, not sure why +for i in range(10): + gc.collect () +print 'shutdown!' + +print 'leaked IMAGEs:' +VImage.im__print_all () +print 'done ... hopefully you saw no leaks' diff --git a/python/vipsCC/Makefile.am b/python/vipsCC/Makefile.am new file mode 100644 index 00000000..80e0b39b --- /dev/null +++ b/python/vipsCC/Makefile.am @@ -0,0 +1,46 @@ +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ @PYTHON_INCLUDES@ + +# we install to a directory inside the python area, since we are a module +vipsccdir = $(pyexecdir)/vipsCC + +vipscc_PYTHON = VImage.py VDisplay.py VError.py VMask.py __init__.py + +# need an expanded VImage.h ... SWIG's preprocessor b0rks on includes inside +# class definitions +vimagemodule.cxx: VImage.i + cpp -DSWIG -E $(top_srcdir)/include/vips/VImage.h > VImage.h + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/include -o $@ $< + +vdisplaymodule.cxx: VDisplay.i + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/include -o $@ $< + +verrormodule.cxx: VError.i + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/include -o $@ $< + +vmaskmodule.cxx: VMask.i + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/include -o $@ $< + +vipscc_LTLIBRARIES = vimagemodule.la vdisplaymodule.la verrormodule.la vmaskmodule.la + +vimagemodule_la_LDFLAGS = -module -avoid-version +vimagemodule_la_LIBADD = ../../libsrcCC/libvipsCC.la ../../libsrc/libvips.la $(VIPS_LIBS) +nodist_vimagemodule_la_SOURCES = vimagemodule.cxx + +vdisplaymodule_la_LDFLAGS = -module -avoid-version +vdisplaymodule_la_LIBADD = ../../libsrcCC/libvipsCC.la $(VIPS_LIBS) +nodist_vdisplaymodule_la_SOURCES = vdisplaymodule.cxx + +verrormodule_la_LDFLAGS = -module -avoid-version +verrormodule_la_LIBADD = ../../libsrcCC/libvipsCC.la $(VIPS_LIBS) +nodist_verrormodule_la_SOURCES = verrormodule.cxx + +vmaskmodule_la_LDFLAGS = -module -avoid-version +vmaskmodule_la_LIBADD = ../../libsrcCC/libvipsCC.la $(VIPS_LIBS) +nodist_vmaskmodule_la_SOURCES = vmaskmodule.cxx + +CLEANFILES = \ + vimagemodule.cxx VImage.h \ + verrormodule.cxx vdisplaymodule.cxx vmaskmodule.cxx \ + VImage.py VDisplay.py VError.py VMask.py + +EXTRA_DIST = VImage.i VDisplay.i VError.i VMask.i __init__.py diff --git a/python/vipsCC/VDisplay.i b/python/vipsCC/VDisplay.i new file mode 100644 index 00000000..7c880761 --- /dev/null +++ b/python/vipsCC/VDisplay.i @@ -0,0 +1,15 @@ +/* SWIG interface file for VDisplay. + */ + +%module VDisplay +%{ +#include +%} + +%import "VError.i" + +/* Need to override assignment to get refcounting working. + */ +%rename(__assign__) *::operator=; + +%include vips/VDisplay.h diff --git a/python/vipsCC/VError.i b/python/vipsCC/VError.i new file mode 100644 index 00000000..084eaa62 --- /dev/null +++ b/python/vipsCC/VError.i @@ -0,0 +1,12 @@ +/* SWIG interface file for VError. + */ + +%module VError +%{ +#include +%} + +%include "std_except.i" +%include "std_string.i" + +%include vips/VError.h diff --git a/python/vipsCC/VImage.i b/python/vipsCC/VImage.i new file mode 100644 index 00000000..6066d0a8 --- /dev/null +++ b/python/vipsCC/VImage.i @@ -0,0 +1,186 @@ +/* SWIG interface file for vipsCC7 + */ + +%module VImage +%{ +#include +%} +/* Need to override assignment to get refcounting working. + */ +%rename(__assign__) vips::VImage::operator=; + +%include "std_list.i" +%include "std_complex.i" +%include "std_vector.i" +%include "std_except.i" + +%import "VError.i" +%import "VMask.i" +%import "VDisplay.i" + +namespace std { + %template(IntVector) vector; + %template(DoubleVector) vector; + %template(ImageVector) vector; +} + +/* VImage defines a lot of other operator overloads ... but SWIGs autowrapping + * doesn't work well for them. Do by hand later. + */ + +/* Need the expanded VImage.h in this directory. SWIG b0rks on #includes + * inside class definitions. + */ +%include VImage.h + +/* Helper code for vips_init(). + */ +%{ +#include + +/* Turn on to print args. +#define DEBUG + */ + +/* Command-line args during parse. + */ +typedef struct _Args { + /* The n strings we alloc when we get from Python. + */ + int n; + char **str; + + /* argc/argv as processed by us. + */ + int argc; + char **argv; +} Args; + +#ifdef DEBUG +static void +args_print (Args *args) +{ + int i; + + printf ("args_print: argc = %d\n", args->argc); + // +1 so we print the trailing NULL too + for (i = 0; i < args->argc + 1; i++) + printf( "\t%2d)\t%s\n", i, args->argv[i]); +} +#endif /*DEBUG*/ + +static void +args_free (Args *args) +{ + int i; + + for (i = 0; i < args->n; i++) + IM_FREE (args->str[i]); + args->n = 0; + args->argc = 0; + IM_FREE (args->str); + IM_FREE (args->argv); + IM_FREE (args); +} + +/* Get argv/argc from python. + */ +static Args * +args_new (void) +{ + Args *args; + PyObject *av; + int i; + int n; + + args = g_new (Args, 1); + args->n = 0; + args->str = NULL; + args->argc = 0; + args->argv = NULL; + + if (!(av = PySys_GetObject ((char *) "argv"))) + return (args); + if (!PyList_Check (av)) { + PyErr_Warn (PyExc_Warning, "ignoring sys.argv: " + "it must be a list of strings"); + return (args); + } + + n = PyList_Size (av); + args->str = g_new (char *, n); + for (i = 0; i < n; i++) + args->str[i] = g_strdup + (PyString_AsString (PyList_GetItem (av, i))); + args->n = n; + + /* +1 for NULL termination. + */ + args->argc = n; + args->argv = g_new (char *, n + 1); + for (i = 0; i < n; i++) + args->argv[i] = args->str[i]; + args->argv[i] = NULL; + + return (args); +} + +static void +vips_fatal (const char *msg) +{ + char buf[256]; + + im_snprintf (buf, 256, "%s\n%s", msg, im_error_buffer()); + im_error_clear(); + Py_FatalError (buf); +} + +%} + +%init %{ +{ + Args *args; + + args = args_new (); + +#ifdef DEBUG + printf ("on startup:\n"); + args_print (args); +#endif /*DEBUG*/ + + if (im_init_world (args->argv[0])) { + args_free (args); + vips_fatal ("can't initialise module vips"); + } + + /* Now parse any GOptions. + */ + GError *error = NULL; + GOptionContext *context; + + context = g_option_context_new ("- vips"); + g_option_context_add_group (context, im_get_option_group()); + + if( !g_option_context_parse (context, + &args->argc, &args->argv, &error)) { + g_option_context_free (context); + args_free (args); + im_error( "vipsmodule", "%s", error->message); + g_error_free (error); + vips_fatal ("can't initialise module vips_core"); + } + g_option_context_free (context); + +#ifdef DEBUG + printf ("after parse:\n"); + args_print (args); +#endif /*DEBUG*/ + + // Write (possibly) modified argc/argv back again. + if (args->argv) + PySys_SetArgv (args->argc, args->argv); + + args_free (args); +} +%} + diff --git a/python/vipsCC/VMask.i b/python/vipsCC/VMask.i new file mode 100644 index 00000000..b274f425 --- /dev/null +++ b/python/vipsCC/VMask.i @@ -0,0 +1,34 @@ +/* SWIG interface file for VMask. + */ + +%module VMask +%{ +#include +%} + +%import "VError.i" +%import "VImage.i" + +/* Need to override assignment to get refcounting working. + */ +%rename(__assign__) *::operator=; + +/* [] is array subscript, as you'd expect. + */ +%rename(__index__) vips::VIMask::operator[]; +%rename(__index__) vips::VDMask::operator[]; + +/* () is 2d array subscript, how odd! + */ +%rename(__call__) vips::VIMask::operator(); +%rename(__call__) vips::VDMask::operator(); + +/* Type conversion operators renamed as functions. + */ +%rename(convert_VImage) vips::VIMask::operator vips::VImage; +%rename(convert_VImage) vips::VDMask::operator vips::VImage; + +%rename(convert_VIMask) vips::VDMask::operator vips::VIMask; +%rename(convert_VDMask) vips::VIMask::operator vips::VDMask; + +%include vips/VMask.h diff --git a/python/vipsCC/__init__.py b/python/vipsCC/__init__.py new file mode 100644 index 00000000..0b05d457 --- /dev/null +++ b/python/vipsCC/__init__.py @@ -0,0 +1 @@ +__all__=["VImage","VMask","VError","VDisplay"] diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..f15088b8 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,6 @@ + +SUBDIRS = \ + iofuncs \ + mosaicing \ + other \ + scripts diff --git a/src/iofuncs/Makefile.am b/src/iofuncs/Makefile.am new file mode 100644 index 00000000..89c8e5b2 --- /dev/null +++ b/src/iofuncs/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = man1 + +bin_PROGRAMS = \ + vips \ + binfile \ + debugim \ + edvips \ + header \ + printlines + +vips_SOURCES = vips.c +binfile_SOURCES = binfile.c +debugim_SOURCES = debugim.c +edvips_SOURCES = edvips.c +header_SOURCES = header.c +printlines_SOURCES = printlines.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ + +install-exec-hook: + ${top_srcdir}/src/scripts/post_install ${DESTDIR}${bindir} + +uninstall-hook: + ${RM} ${bindir}/im_* + diff --git a/src/iofuncs/binfile.c b/src/iofuncs/binfile.c new file mode 100644 index 00000000..6238fbfc --- /dev/null +++ b/src/iofuncs/binfile.c @@ -0,0 +1,81 @@ +/* @(#) Command which adds a vasari header to a binary file + * @(#) The user must ensure that the size of the file is correct + * @(#) + * @(#) Usage: binfile infile outfile xs ys bands + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 31/07/1991 + * Modified on: + * 2/2/95 JC + * - ANSIfied + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *bin, *out; + int xs, ys, bands, offset; + + if( argc != 7 ) + error_exit( "usage: %s infile outfile xsize ysize bands offset", + argv[0] ); + + xs = atoi(argv[3]); + ys = atoi(argv[4]); + bands = atoi(argv[5]); + offset = atoi(argv[6]); + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if( !(out = im_open( argv[2], "w" )) ) + error_exit( "unable to open %s for output", argv[2] ); + if( !(bin = im_binfile( argv[1], xs, ys, bands, offset )) ) + error_exit( "unable to im_binfile" ); + if( im_copy( bin, out ) ) + error_exit( "unable to copy to %s", argv[2] ); + + im_close( out ); + im_close( bin ); + + return( 0 ); +} diff --git a/src/iofuncs/debugim.c b/src/iofuncs/debugim.c new file mode 100644 index 00000000..44796f0e --- /dev/null +++ b/src/iofuncs/debugim.c @@ -0,0 +1,69 @@ +/* @(#) Prints the values of a file + * @(#) Result is printed in stderr output + * @(#) + * @(#) Usage: debugim infile + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 03/08/1990 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *in; + + if( argc != 2 ) + error_exit( "usage: %s infile", argv[0] ); + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if( !(in = im_open( argv[1], "r" )) ) + error_exit( "unable to open %s for input", argv[1]); + + if( im_debugim( in ) ) + error_exit( "unable to im_debugim"); + + im_close( in ); + + return( 0 ); +} diff --git a/src/iofuncs/edvips.c b/src/iofuncs/edvips.c new file mode 100644 index 00000000..7aa73200 --- /dev/null +++ b/src/iofuncs/edvips.c @@ -0,0 +1,206 @@ +/* modify vips file header! - useful for setting resolution, coding... +very dangerous! +no way of setting non-used codes in variables like newxres +so need flags to show new parameter has been set.. boring +Copyright K.Martinez 30/6/93 +29/7/93 JC + -format added + - ==0 added to strcmp! +17/11/94 JC + - new header fields added +21/10/04 + - more header updates + +22/8/05 + - less-stupid-ified +20/9/05 + - rewritten with glib option parser, ready for xml options to go in + + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* We have to represent all header fields as char* so we can spot unset args + * safely. + */ +static char *xsize = NULL; +static char *ysize = NULL; +static char *bands = NULL; +static char *format = NULL; +static char *type = NULL; +static char *coding = NULL; +static char *xres = NULL; +static char *yres = NULL; +static char *xoffset = NULL; +static char *yoffset = NULL; +static gboolean setext = FALSE; + +static GOptionEntry entries[] = { + { "xsize", 'x', 0, G_OPTION_ARG_STRING, &xsize, + N_( "set Xsize to N" ), "N" }, + { "ysize", 'y', 0, G_OPTION_ARG_STRING, &ysize, + N_( "set Ysize to N" ), "N" }, + { "bands", 'b', 0, G_OPTION_ARG_STRING, &bands, + N_( "set Bands to N" ), "N" }, + { "format", 'f', 0, G_OPTION_ARG_STRING, &format, + N_( "set BandFmt to F (eg. IM_BANDFMT_UCHAR)" ), "F" }, + { "type", 't', 0, G_OPTION_ARG_STRING, &type, + N_( "set Type to T (eg. IM_TYPE_XYZ)" ), "T" }, + { "coding", 'c', 0, G_OPTION_ARG_STRING, &coding, + N_( "set Coding to C (eg. IM_CODING_LABQ)" ), "C" }, + { "xres", 'X', 0, G_OPTION_ARG_STRING, &xres, + N_( "set Xres to R pixels/mm" ), "R" }, + { "yres", 'Y', 0, G_OPTION_ARG_STRING, &yres, + N_( "set Yres to R pixels/mm" ), "R" }, + { "xoffset", 'u', 0, G_OPTION_ARG_STRING, &xoffset, + N_( "set Xoffset to N" ), "N" }, + { "yoffset", 'v', 0, G_OPTION_ARG_STRING, &yoffset, + N_( "set Yoffset to N" ), "N" }, + { "setext", 'e', 0, G_OPTION_ARG_NONE, &setext, + N_( "replace extension block with stdin" ), NULL }, + { NULL } +}; + +static void +parse_pint( char *arg, int *out ) +{ + /* Might as well set an upper limit. + */ + *out = atoi( arg ); + if( *out <= 0 || *out > 1000000 ) + error_exit( _( "'%s' is not a positive integer" ), arg ); +} + +int +main( int argc, char **argv ) +{ + GOptionContext *context; + GError *error = NULL; + IMAGE *im; + unsigned char header[IM_SIZEOF_HEADER]; + + if( im_init_world( argv[0] ) ) + error_exit( _( "unable to start VIPS" ) ); + + context = g_option_context_new( + _( "vipsfile - edit vipsfile header" ) ); + g_option_context_add_main_entries( context, entries, GETTEXT_PACKAGE ); + g_option_context_add_group( context, im_get_option_group() ); + if( !g_option_context_parse( context, &argc, &argv, &error ) ) { + if( error ) { + fprintf( stderr, "%s\n", error->message ); + g_error_free( error ); + } + + exit( -1 ); + } + if( argc != 2 ) { + fprintf( stderr, _( "usage: %s [OPTION...] vipsfile\n" ), + g_get_prgname() ); + exit( -1 ); + } + + if( !(im = im_init( argv[1] )) || + (im->fd = im__open_image_file( im->filename )) == -1 ) + error_exit( _( "could not open image %s" ), argv[1] ); + if( read( im->fd, header, IM_SIZEOF_HEADER ) != IM_SIZEOF_HEADER || + im__read_header_bytes( im, header ) ) + error_exit( _( "could not read VIPS header for %s" ), + im->filename ); + + if( xsize ) + parse_pint( xsize, &im->Xsize ); + if( ysize ) + parse_pint( ysize, &im->Ysize ); + if( bands ) + parse_pint( bands, &im->Bands ); + if( format ) { + if( (im->BandFmt = im_char2BandFmt( format )) < 0 ) + error_exit( _( "bad format %s" ), format ); + im->Bbits = im_bits_of_fmt( im->BandFmt ); + } + if( type ) { + if( (im->Type = im_char2Type( type )) < 0 ) + error_exit( _( "bad type %s" ), type ); + } + if( coding ) { + if( (im->Coding = im_char2Coding( coding )) < 0 ) + error_exit( _( "bad coding %s" ), coding ); + } + if( xres ) + im->Xres = atof( xres ); + if( yres ) + im->Yres = atof( yres ); + if( xoffset ) + im->Xoffset = atoi( xoffset ); + if( yoffset ) + im->Yoffset = atoi( yoffset ); + + if( lseek( im->fd, 0, SEEK_SET ) == (off_t) -1 ) + error_exit( _( "could not seek on %s" ), im->filename ); + if( im__write_header_bytes( im, header ) || + im__write( im->fd, header, IM_SIZEOF_HEADER ) ) + error_exit( _( "could not write to %s" ), im->filename ); + + if( setext ) { + char *xml; + unsigned int size; + + if( !(xml = im__file_read( stdin, "stdin", &size )) ) + error_exit( _( "could not get ext data" ) ); + + /* Strip trailing whitespace ... we can get stray \n at the + * end, eg. "echo | edvips --setext fred.v". + */ + while( size > 0 && isspace( xml[size - 1] ) ) + size -= 1; + + if( im__write_extension_block( im, xml, size ) ) + error_exit( _( "could not set extension" ) ); + im_free( xml ); + } + + im_close( im ); + + return( 0 ); +} + diff --git a/src/iofuncs/header.c b/src/iofuncs/header.c new file mode 100644 index 00000000..97f0d1ea --- /dev/null +++ b/src/iofuncs/header.c @@ -0,0 +1,174 @@ +/* @(#) Command; reads the header of a Vasari picture file. + * @(#) Usage: header vasari_file + * @(#) + * + * Copyright: Birkbeck College, History of Art Dept, London, VASARI project. + * + * Author: Nicos Dessipris + * Written on: 17/01/1990 + * Modified on : 17/04/1990, 2/6/93 K.Martinez + * 16/6/93 JC + * - now calls im_mmapin instead of bizzare bogosity + * 1/6/95 JC + * - extra field argument for testing particular bits of the header + * 29/10/98 JC + * - now uses im_open() + * 24/5/01 JC + * - uses im_tiff2vips_header() etc., for speed + * 7/5/03 JC + * - uses im_open_header() + * 1/8/05 + * - uses new header API, for great smallness + * 4/8/05 + * - back to plain im_open() now that's lazy enough for us + * 9/9/05 + * - display meta fields in save format, if possible + * 20/9/05 + * - new field name "getext" reads extension block + * 24/8/06 + * - use GOption, loop over args + * 4/1/07 + * - use im_history_get() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +static char *main_option_field = NULL; + +static GOptionEntry main_option[] = { + { "field", 'f', 0, G_OPTION_ARG_STRING, &main_option_field, + N_( "print value of FIELD (\"getext\" reads extension block, " + "\"Hist\" reads image history)" ), + "FIELD" }, + { NULL } +}; + +/* Print header, or parts of header. + */ +static int +print_header( IMAGE *im ) +{ + if( !main_option_field ) + im_printdesc( im ); + else if( strcmp( main_option_field, "getext" ) == 0 ) { + if( im__has_extension_block( im ) ) { + void *buf; + int size; + + if( !(buf = im__read_extension_block( im, &size )) ) + return( -1 ); + printf( "%s", (char *) buf ); + im_free( buf ); + } + } + else if( strcmp( main_option_field, "Hist" ) == 0 ) + printf( "%s", im_history_get( im ) ); + else { + GValue value = { 0 }; + GType type; + + if( im_header_get( im, main_option_field, &value ) ) + return( -1 ); + + /* Display the save form, if there is one. This was we display + * something useful for ICC profiles, xml fields, etc. + */ + type = G_VALUE_TYPE( &value ); + if( g_value_type_transformable( type, IM_TYPE_SAVE_STRING ) ) { + GValue save_value = { 0 }; + + g_value_init( &save_value, IM_TYPE_SAVE_STRING ); + if( !g_value_transform( &value, &save_value ) ) + return( -1 ); + printf( "%s\n", im_save_string_get( &save_value ) ); + g_value_unset( &save_value ); + } + else { + char *str_value; + + str_value = g_strdup_value_contents( &value ); + printf( "%s\n", str_value ); + g_free( str_value ); + } + + g_value_unset( &value ); + } + + return( 0 ); +} + +int +main( int argc, char *argv[] ) +{ + GOptionContext *context; + GError *error = NULL; + int i; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + context = g_option_context_new( _( "- print image header" ) ); + + g_option_context_add_main_entries( context, + main_option, GETTEXT_PACKAGE ); + g_option_context_add_group( context, im_get_option_group() ); + + if( !g_option_context_parse( context, &argc, &argv, &error ) ) { + if( error ) { + fprintf( stderr, "%s\n", error->message ); + g_error_free( error ); + } + + error_exit( "try \"%s --help\"", g_get_prgname() ); + } + + g_option_context_free( context ); + + for( i = 1; i < argc; i++ ) { + IMAGE *im; + + if( !(im = im_open( argv[i], "r" )) ) + error_exit( "unable to open %s", argv[i] ); + if( print_header( im ) ) + error_exit( _( "unable to print header of \"%s\"" ), + argv[i] ); + im_close( im ); + } + + return( 0 ); +} diff --git a/src/iofuncs/man1/Makefile.am b/src/iofuncs/man1/Makefile.am new file mode 100644 index 00000000..49d47d1b --- /dev/null +++ b/src/iofuncs/man1/Makefile.am @@ -0,0 +1,9 @@ +man_MANS = \ + debugim.1 \ + header.1 \ + vips.1 \ + binfile.1 \ + edvips.1 \ + printlines.1 + +EXTRA_DIST = ${man_MANS} diff --git a/src/iofuncs/man1/binfile.1 b/src/iofuncs/man1/binfile.1 new file mode 100644 index 00000000..81f7c62a --- /dev/null +++ b/src/iofuncs/man1/binfile.1 @@ -0,0 +1,19 @@ +.TH BINFILE 1 "11 April 1990" +.SH NAME +im_binfile \- convert raw binary files to VIPS format +.SH SYNOPSIS +.B binfile in out xs ys b +.SH DESCRIPTION +.B binfile +expects as input a raw UNIX file with its filename held by the string in +and writes it to the vasari image file out by filling properly the header +details. It is expected that the file has sizes xs by ys and +has b bands. It is an error if the file in has less than xs*ys*b data. +The program is unable to check whether the supplied xs, ys and b are correct. +.SH SEE\ ALSO +im_binfile(3). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 11/04/1990 diff --git a/src/iofuncs/man1/debugim.1 b/src/iofuncs/man1/debugim.1 new file mode 100644 index 00000000..f1639b5b --- /dev/null +++ b/src/iofuncs/man1/debugim.1 @@ -0,0 +1,21 @@ +.TH DEBUGIM 1 "12 July 1990" +.SH NAME +debugim, printlines \- prints the raw image data of a vasari file format +.SH SYNOPSIS +debugim infile + +printlines infile +.SH DESCRIPTION +debugim prints at the standard error output the raw image data of a vasari +format file. This function is useful for debugging when applied on small +image files + +printlines prints at the standard error output the raw image data of a vasari +format together with the line no and the x location and the value(s) of each +pixel. +.SH SEE ALSO +im_intro(3X), im_debugim(3X), im_printlines(3X), vips2mask(1X). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 12/07/1990 diff --git a/src/iofuncs/man1/edvips.1 b/src/iofuncs/man1/edvips.1 new file mode 100644 index 00000000..73fd861e --- /dev/null +++ b/src/iofuncs/man1/edvips.1 @@ -0,0 +1,49 @@ +.TH EDVIPS 1 "30 June 1993" +.SH NAME +edvips \- edit header of a vips image file +.SH SYNOPSIS +.B edvips [OPTION...] vipsfile +.SH DESCRIPTION +.B edvips +alters a VIPS image file's header. This is useful for setting the resolution, +for example. + +The options are: + + -x, --xsize=N set Xsize to N + -y, --ysize=N set Ysize to N + -b, --bands=N set Bands to N + -f, --format=F set BandFmt to F (eg. IM_BANDFMT_UCHAR) + -t, --type=T set Type to T (eg. IM_TYPE_XYZ) + -c, --coding=C set Coding to C (eg. IM_CODING_LABQ) + -X, --xres=R set Xres to R pixels/mm + -Y, --yres=R set Yres to R pixels/mm + -u, --xoffset=N set Xoffset to N + -v, --yoffset=N set Yoffset to N + -e, --setext replace extension block with stdin + +Be very careful when changing Xsize, Ysize, BandFmt or Bands. edvips does no +checking! + +.SH EXAMPLES +To set the Xsize to 512 and Bands to 6: + + edvips --xsize=512 --bands=6 fred.v + +or + + edvips -x 512 -b 6 fred.v + +Extract the XML metadata from an image with +.B header(1), +edit it, and reattach with +.B edvips(1). + + header -f getext fred.v | sed s/banana/pineapple/ | edvips -e fred.v + +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1) +.SH COPYRIGHT +K. Martinez 1993 diff --git a/src/iofuncs/man1/header.1 b/src/iofuncs/man1/header.1 new file mode 100644 index 00000000..091da1be --- /dev/null +++ b/src/iofuncs/man1/header.1 @@ -0,0 +1,33 @@ +.TH HEADER 1 "12 July 1990" +.SH NAME +header \- prints information about an image file +.SH SYNOPSIS +header [OPTIONS ...] files ... +.SH DESCRIPTION +.B header(1) +prints image header fields to stdout. + +.SH OPTIONS +.TP +.B -f FIELD, --field=FIELD +Print value of +.B FIELD +from image header. The special field name getext prints +the VIPS extension block: the XML defining the image metadata. You can alter +this, then reattach with +.B edvips(1). + +.SH EXAMPLES + pineapple:~/CVS/vips-7.12/src/iofuncs john$ header -f Xsize ~/pics/*.v + 1024 + 1279 + 22865 + 1 + 256 + +.SH SEE ALSO +im_intro(3), edvips(1), im_printdesc(3), im_header_get(3). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 12/07/1990 diff --git a/src/iofuncs/man1/printlines.1 b/src/iofuncs/man1/printlines.1 new file mode 100644 index 00000000..a5f43318 --- /dev/null +++ b/src/iofuncs/man1/printlines.1 @@ -0,0 +1 @@ +.so man1/debugim.1 diff --git a/src/iofuncs/man1/vips.1 b/src/iofuncs/man1/vips.1 new file mode 100644 index 00000000..7ddc8ada --- /dev/null +++ b/src/iofuncs/man1/vips.1 @@ -0,0 +1,58 @@ +.TH VIPS 1 "30 June 1993" +.SH NAME +vips \- run vips function from UNIX command line +.SH SYNOPSIS +.B vips [flags] [command] [command-args] +.SH DESCRIPTION +.B vips(1) +is the VIPS universal main program. You can use it to run any VIPS function +from the command line, to query the VIPS function database, and to +maintain parts of the VIPS library. + +To run a VIPS function, the first argument should be the name of the function +and following arguments should be the function parameters. For example: + + example% vips im_invert lena.v lena2.v + +If you make a symbolic link to the vips executable named after an operation, +then you can use that link to call an operation directly. For example: + + example% ln -s vips im_invert + example% im_invert lena.v lena2.v + +.SH OPTIONS +.TP +.B -l PACKAGE, --list=PACKAGE +List operations defined in PACKAGE. + +.TP +.B -u OPERATION, --usage=OPERATION +Show usage message for OPERATION. + +.TP +.B -p PLUGIN, --plugin=PLUGIN +Load PLUGIN. Note that plugins in $VIPSHOME/lib are loaded automatically. + +.TP +.B -k, --links +Print link lines for all operations. + +.TP +.B -h PACKAGE, --cpph=PACKAGE +Print C++ header for PACKAGE. PACKAGE can also be a function name, or "all". + +.TP +.B -c PACKAGE, --cppc=PACKAGE +Print C++ binding for PACKAGE. PACKAGE can also be a function name, or "all". + +.TP +.B -s PACKAGE, --swig=PACKAGE +Print SWIG declaration for PACKAGE. PACKAGE can also be a function name, +or "all". + +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1) +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1989-1996. diff --git a/src/iofuncs/printlines.c b/src/iofuncs/printlines.c new file mode 100644 index 00000000..8930bd01 --- /dev/null +++ b/src/iofuncs/printlines.c @@ -0,0 +1,70 @@ +/* @(#) Prints the values of a file + * @(#) Result is printed in stderr output + * @(#) + * @(#) Usage: printlines infile + * @(#) + * + * Copyright: 1990, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 03/08/1990 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *in; + + if ( (argc != 2)||(argv[1][0] == '-') ) + error_exit( "Usage:\n%s infile\n\n\ +Image is printed in stderr\n", argv[0]); + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if ((in = im_open(argv[1],"r")) == NULL) + error_exit("Unable to open %s for input", argv[1]); + + if (im_printlines(in) == -1) + error_exit("unable to im_printlines"); + + im_close(in); + + return(0); +} diff --git a/src/iofuncs/vips.c b/src/iofuncs/vips.c new file mode 100644 index 00000000..62bf60fa --- /dev/null +++ b/src/iofuncs/vips.c @@ -0,0 +1,944 @@ +/* VIPS universal main program. + * + * J. Cupitt, 8/4/93. + * 12/5/06 + * - use GOption. g_*_prgname() + * 16/7/06 + * - hmm, was broken for function name as argv1 case + * 11/7/06 + * - add "all" option to -l + * 14/7/06 + * - ignore "--" arguments. + * 2/9/06 + * - do less init ... im_init_world() does more now + * 18/8/06 + * - use IM_EXEEXT + * 16/10/06 + * - add --version + * 17/10/06 + * - add --swig + * - cleanups + * - remove --swig again, sigh + * - add throw() decls to C++ to help SWIG + * 14/1/07 + * - add --list packages + * 26/2/07 + * - add input *VEC arg types to C++ binding + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#ifdef OS_WIN32 +#define strcasecmp(a,b) _stricmp(a,b) +#endif + +static char *main_option_list = NULL; +static char *main_option_usage = NULL; +static char *main_option_plugin = NULL; +static gboolean main_option_links; +static char *main_option_cpph = NULL; +static char *main_option_cppc = NULL; +static gboolean *main_option_version; + +static GOptionEntry main_option[] = { + { "list", 'l', 0, G_OPTION_ARG_STRING, &main_option_list, + N_( "list operations in PACKAGE (or \"all\", \"packages\")" ), + "PACKAGE" }, + { "usage", 'u', 0, G_OPTION_ARG_STRING, &main_option_usage, + N_( "show usage message for OPERATION" ), + "OPERATION" }, + { "plugin", 'p', 0, G_OPTION_ARG_FILENAME, &main_option_plugin, + N_( "load PLUGIN" ), + "PLUGIN" }, + { "links", 'k', 0, G_OPTION_ARG_NONE, &main_option_links, + N_( "print link lines for all operations" ), NULL }, + { "cpph", 'h', 0, G_OPTION_ARG_STRING, &main_option_cpph, + N_( "print C++ decls for PACKAGE (or \"all\")" ), + "PACKAGE" }, + { "cppc", 'c', 0, G_OPTION_ARG_STRING, &main_option_cppc, + N_( "print C++ binding for PACKAGE (or \"all\")" ), + "PACKAGE" }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &main_option_version, + N_( "print im_version_string" ), NULL }, + { NULL } +}; + +typedef void *(*map_name_fn)( im_function * ); + +/* Loop over a package. + */ +static void * +map_package( im_package *pack, map_name_fn fn ) +{ + int i; + void *result; + + for( i = 0; i < pack->nfuncs; i++ ) + if( (result = fn( pack->table[i] )) ) + return( result ); + + return( NULL ); +} + +/* Apply a function to a vips operation, or map over a package of operations. + */ +static void * +map_name( const char *name, map_name_fn fn ) +{ + im_package *pack; + im_function *func; + + if( strcmp( name, "all" ) == 0 ) + /* Do all packages. + */ + im_map_packages( (VSListMap2Fn) map_package, fn ); + else if( (pack = im_find_package( name )) ) + /* Do one package. + */ + map_package( pack, fn ); + else if( (func = im_find_function( name )) ) + /* Do a single function. + */ + fn( func ); + else { + im_error( "map_name", + _( "no package or function \"%s\"" ), name ); + return( fn ); + } + + return( NULL ); +} + +static void * +list_package( im_package *pack ) +{ + printf( "%-18s - %d operations\n", pack->name, pack->nfuncs ); + + return( NULL ); +} + +static void * +list_function( im_function *func ) +{ + printf( "%-18s - %s\n", func->name, _( func->desc ) ); + + return( NULL ); +} + +static void +print_list( const char *name ) +{ + if( strcmp( name, "packages" ) == 0 ) + im_map_packages( (VSListMap2Fn) list_package, NULL ); + else { + if( map_name( name, list_function ) ) + error_exit( "unknown package \"%s\"", name ); + } +} + +/* Is s1 a prefix of s2? + */ +static int +isprefix( const char *s1, const char *s2 ) +{ + while( *s1 && *s1 == *s2 ) { + s1++; + s2++; + } + + return( *s1 == '\0' ); +} + +/* Is s1 a postfix of s2? + */ +static int +ispostfix( const char *s1, const char *s2 ) +{ + int l1 = strlen( s1 ); + int l2 = strlen( s2 ); + + if( l2 < l1 ) + return( 0 ); + + return( strcasecmp( s1, s2 + l2 - l1 ) == 0 ); +} + +/* Print "ln -s" lines for this package. + */ +static void * +print_links( im_package *pack ) +{ + int i; + + for( i = 0; i < pack->nfuncs; i++ ) + printf( "rm -f %s" IM_EXEEXT "; " + "ln -s vips" IM_EXEEXT " %s" IM_EXEEXT "\n", + pack->table[i]->name, pack->table[i]->name ); + + return( NULL ); +} + +/* Does a function have any printing output? + */ +static int +has_print( im_function *fn ) +{ + int i; + + for( i = 0; i < fn->argc; i++ ) + if( fn->argv[i].print ) + return( -1 ); + + return( 0 ); +} + +/* Print a usage string from an im_function descriptor. + */ +static void +usage( im_function *fn ) +{ + int i; + im_package *pack = im_package_of_function( fn->name ); + + /* Don't print the prgname if we're being run as a symlink. + */ + fprintf( stderr, "usage: " ); + if( im_isprefix( "vips", g_get_prgname() ) ) + fprintf( stderr, "%s ", g_get_prgname() ); + fprintf( stderr, "%s ", fn->name ); + + /* Print args requiring command-line input. + */ + for( i = 0; i < fn->argc; i++ ) + if( fn->argv[i].desc->flags & IM_TYPE_ARG ) + fprintf( stderr, "%s ", fn->argv[i].name ); + + /* Print types of command line args. + */ + fprintf( stderr, "\nwhere:\n" ); + for( i = 0; i < fn->argc; i++ ) + if( fn->argv[i].desc->flags & IM_TYPE_ARG ) + fprintf( stderr, "\t%s is of type \"%s\"\n", + fn->argv[i].name, fn->argv[i].desc->type ); + + /* Print output print args. + */ + if( has_print( fn ) ) { + fprintf( stderr, "prints:\n" ); + for( i = 0; i < fn->argc; i++ ) + if( fn->argv[i].print ) + fprintf( stderr, "\t%s of type \"%s\"\n", + fn->argv[i].name, + fn->argv[i].desc->type ); + } + + /* Print description of this function, and package it comes from. + */ + fprintf( stderr, "%s", _( fn->desc ) ); + if( pack ) + fprintf( stderr, ", from package \"%s\"", pack->name ); + fprintf( stderr, "\n" ); + + /* Print any flags this function has. + */ + fprintf( stderr, "flags: " ); + if( fn->flags & IM_FN_PIO ) + fprintf( stderr, "(PIO function) " ); + else + fprintf( stderr, "(WIO function) " ); + if( fn->flags & IM_FN_TRANSFORM ) + fprintf( stderr, "(coordinate transformer) " ); + else + fprintf( stderr, "(no coordinate transformation) " ); + if( fn->flags & IM_FN_PTOP ) + fprintf( stderr, "(point-to-point operation) " ); + else + fprintf( stderr, "(area operation) " ); + if( fn->flags & IM_FN_NOCACHE ) + fprintf( stderr, "(nocache operation) " ); + else + fprintf( stderr, "(result can be cached) " ); + + fprintf( stderr, "\n" ); +} + +/* Convert VIPS type name to C++ type name. NULL for type unsupported by C++ + * layer. + */ +static char * +vips2cpp( im_type_desc *ty ) +{ + int k; + + /* VIPS types. + */ + static char *vtypes[] = { + IM_TYPE_DOUBLE, + IM_TYPE_INT, + IM_TYPE_COMPLEX, + IM_TYPE_STRING, + IM_TYPE_IMAGE, + IM_TYPE_IMASK, + IM_TYPE_DMASK, + IM_TYPE_DISPLAY, + IM_TYPE_IMAGEVEC, + IM_TYPE_DOUBLEVEC, + IM_TYPE_INTVEC + }; + + /* Corresponding C++ types. + */ + static char *ctypes[] = { + "double", + "int", + "std::complex", + "char*", + "VImage", + "VIMask", + "VDMask", + "VDisplay", + "std::vector", + "std::vector", + "std::vector" + }; + + for( k = 0; k < IM_NUMBER( vtypes ); k++ ) + if( strcmp( ty->type, vtypes[k] ) == 0 ) + return( ctypes[k] ); + + return( NULL ); +} + +/* Test a function definition for C++ suitability. + */ +static int +is_cppable( im_function *fn ) +{ + int j; + + /* Check we know all the types. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + if( !vips2cpp( ty ) ) + return( 0 ); + } + + /* We dont wrap output IMAGEVEC/DOUBLEVEC/INTVEC. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + if( ty->flags & IM_TYPE_OUTPUT ) + if( strcmp( ty->type, IM_TYPE_IMAGEVEC ) == 0 || + strcmp( ty->type, IM_TYPE_DOUBLEVEC ) == 0 || + strcmp( ty->type, IM_TYPE_INTVEC ) == 0 ) + return( 0 ); + } + + /* Must be at least one image argument (input or output) ... since we + * get inserted in the VImage class. Other funcs get wrapped by hand. + */ + for( j = 0; j < fn->argc; j++ ) + if( strcmp( fn->argv[j].desc->type, IM_TYPE_IMAGE ) == 0 ) + break; + if( j == fn->argc ) + return( 0 ); + + return( -1 ); +} + +/* Search for the first output arg, and the first IMAGE input arg. + */ +static void +find_ioargs( im_function *fn, int *ia, int *oa ) +{ + int j; + + /* Look for first output arg - this will be the result of the + * function. + */ + *oa = -1; + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + if( ty->flags & IM_TYPE_OUTPUT ) { + *oa = j; + break; + } + } + + /* Look for first input IMAGE arg. This will become the implicit + * "this" arg. + */ + *ia = -1; + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + if( !(ty->flags & IM_TYPE_OUTPUT) && + strcmp( ty->type, IM_TYPE_IMAGE ) == 0 ) { + *ia = j; + break; + } + } +} + +/* Turn a VIPS name into a C++ name. Eg. im_lintra_vec becomes lin. + */ +static void +c2cpp_name( const char *in, char *out ) +{ + /* chop off "im_" prefix. + */ + if( isprefix( "im_", in ) ) + strcpy( out, in + 3 ); + else + strcpy( out, in ); + + /* Drop "_vec" postfix (eg. so im_lintra_vec becomes lintra). We rely + * on overloading to distinguish conflicts. + */ + if( ispostfix( "_vec", out ) ) + out[strlen( out ) - 4] = '\0'; + + /* Drop "const" postfix (eg. so im_eorimageconst becomes eorimage). + */ + if( ispostfix( "const", out ) ) + out[strlen( out ) - 5] = '\0'; + + /* Drop "tra" postfix (eg. so im_costra becomes cos). + */ + if( ispostfix( "tra", out ) ) + out[strlen( out ) - 3] = '\0'; +} + +/* Print prototype for a function (ie. will be followed by code). + * + * Eg.: + * VImage VImage::lin( double a, double b ) throw( VError ) + */ +static void * +print_cppproto( im_function *fn ) +{ + int j; + char name[4096]; + int oa, ia; + int flg; + + /* If it's not cppable, do nothing. + */ + if( !is_cppable( fn ) ) + return( NULL ); + + /* Make C++ name. + */ + c2cpp_name( fn->name, name ); + + /* Find input and output args. + */ + find_ioargs( fn, &ia, &oa ); + + /* Print output type. + */ + if( oa == -1 ) + printf( "void " ); + else + printf( "%s ", vips2cpp( fn->argv[oa].desc ) ); + + printf( "VImage::%s(", name ); + + /* Print arg list. + */ + flg = 0; + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + /* Skip ia and oa. + */ + if( j == ia || j == oa ) + continue; + + /* Print arg type. + */ + if( flg ) + printf( ", %s", vips2cpp( ty ) ); + else { + printf( " %s", vips2cpp( ty ) ); + flg = 1; + } + + /* If it's an putput arg, print a "&" to make a reference + * argument. + */ + if( ty->flags & IM_TYPE_OUTPUT ) + printf( "&" ); + + /* Print arg name. + */ + printf( " %s", fn->argv[j].name ); + } + + /* End of arg list! + */ + if( flg ) + printf( " " ); + printf( ") throw( VError )\n" ); + + return( NULL ); +} + +/* Print cpp decl for a function. + * + * Eg. + * VImage lin( double, double ) throw( VError ); + */ +static void * +print_cppdecl( im_function *fn ) +{ + int j; + char name[4096]; + int oa, ia; + int flg; + + /* If it's not cppable, do nothing. + */ + if( !is_cppable( fn ) ) + return( NULL ); + + /* Make C++ name. + */ + c2cpp_name( fn->name, name ); + + /* Find input and output args. + */ + find_ioargs( fn, &ia, &oa ); + if( ia == -1 ) + /* No input image, so make it a static in the class + * declaration. + */ + printf( "static " ); + + /* Print output type. + */ + if( oa == -1 ) + printf( "void " ); + else + printf( "%s ", vips2cpp( fn->argv[oa].desc ) ); + + /* Print function name and start arg list. + */ + printf( "%s(", name ); + + /* Print arg list. + */ + flg = 0; + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + /* Skip ia and oa. + */ + if( j == ia || j == oa ) + continue; + + /* Print arg type. + */ + if( flg ) + printf( ", %s", vips2cpp( ty ) ); + else { + printf( " %s", vips2cpp( ty ) ); + flg = 1; + } + + /* If it's an putput arg, print a "&" to make a reference + * argument. + */ + if( ty->flags & IM_TYPE_OUTPUT ) + printf( "&" ); + } + + /* End of arg list! + */ + if( flg ) + printf( " " ); + + printf( ") throw( VError );\n" ); + + return( NULL ); +} + +static void +print_invec( int j, const char *arg, + const char *vips_name, const char *c_name, const char *extract ) +{ + printf( "\t((%s*) _vec.data(%d))->n = %s.size();\n", + vips_name, j, arg ); + printf( "\t((%s*) _vec.data(%d))->vec = new %s[%s.size()];\n", + vips_name, j, c_name, arg ); + printf( "\tfor( unsigned int i = 0; i < %s.size(); i++ )\n", + arg ); + printf( "\t\t((%s*) _vec.data(%d))->vec[i] = %s[i]%s;\n", + vips_name, j, arg, extract ); +} + +/* Print the definition for a function. + */ +static void * +print_cppdef( im_function *fn ) +{ + int j; + int ia, oa; + + /* If it's not cppable, do nothing. + */ + if( !is_cppable( fn ) ) + return( NULL ); + + find_ioargs( fn, &ia, &oa ); + + printf( "// %s: %s\n", fn->name, _( fn->desc ) ); + print_cppproto( fn ); + printf( "{\n" ); + + /* Declare the implicit input image. + */ + if( ia != -1 ) + printf( "\tVImage %s = *this;\n", fn->argv[ia].name ); + + /* Declare return value, if any. + */ + if( oa != -1 ) + printf( "\t%s %s;\n\n", + vips2cpp( fn->argv[oa].desc ), + fn->argv[oa].name ); + + /* Declare the arg vector. + */ + printf( "\tVargv _vec( \"%s\" );\n\n", fn->name ); + + /* Create the input args. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + /* Images are special - have to init the vector, even + * for output args. Have to translate VImage. + */ + if( strcmp( ty->type, IM_TYPE_IMAGE ) == 0 ) { + printf( "\t_vec.data(%d) = %s.image();\n", + j, fn->argv[j].name ); + continue; + } + + /* For output masks, we have to set an input filename. Not + * freed, so constant string is OK. + */ + if( (ty->flags & IM_TYPE_OUTPUT) && + (strcmp( ty->type, IM_TYPE_IMASK ) == 0 || + strcmp( ty->type, IM_TYPE_DMASK ) == 0) ) { + printf( "\t((im_mask_object*) _vec.data(%d))->name = " + "(char*)\"noname\";\n", j ); + continue; + } + + /* Skip other output args. + */ + if( ty->flags & IM_TYPE_OUTPUT ) + continue; + + if( strcmp( ty->type, IM_TYPE_IMASK ) == 0 ) + /* Mask types are different - have to use + * im_mask_object. + */ + printf( "\t((im_mask_object*) " + "_vec.data(%d))->mask = %s.mask().iptr;\n", + j, fn->argv[j].name ); + else if( strcmp( ty->type, IM_TYPE_DMASK ) == 0 ) + printf( "\t((im_mask_object*) " + "_vec.data(%d))->mask = %s.mask().dptr;\n", + j, fn->argv[j].name ); + else if( strcmp( ty->type, IM_TYPE_DISPLAY ) == 0 ) + /* Display have to use VDisplay. + */ + printf( "\t_vec.data(%d) = %s.disp();\n", + j, fn->argv[j].name ); + else if( strcmp( ty->type, IM_TYPE_STRING ) == 0 ) + /* Zap input strings directly into _vec. + */ + printf( "\t_vec.data(%d) = (im_object) %s;\n", + j, fn->argv[j].name ); + else if( strcmp( ty->type, IM_TYPE_IMAGEVEC ) == 0 ) + print_invec( j, fn->argv[j].name, + "im_imagevec_object", "IMAGE *", ".image()" ); + else if( strcmp( ty->type, IM_TYPE_DOUBLEVEC ) == 0 ) + print_invec( j, fn->argv[j].name, + "im_doublevec_object", "double", "" ); + else if( strcmp( ty->type, IM_TYPE_INTVEC ) == 0 ) + print_invec( j, fn->argv[j].name, + "im_intvec_object", "int", "" ); + else + /* Just use vips2cpp(). + */ + printf( "\t*((%s*) _vec.data(%d)) = %s;\n", + vips2cpp( ty ), j, fn->argv[j].name ); + } + + /* Call function. + */ + printf( "\t_vec.call();\n" ); + + /* Extract output args. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty = fn->argv[j].desc; + + /* Skip input args. + */ + if( !(ty->flags & IM_TYPE_OUTPUT) ) + continue; + + /* Skip images (done on input side, really). + */ + if( strcmp( ty->type, IM_TYPE_IMAGE ) == 0 ) + continue; + + if( strcmp( ty->type, IM_TYPE_IMASK ) == 0 || + strcmp( ty->type, IM_TYPE_DMASK ) == 0 ) + /* Mask types are different - have to use + * im_mask_object. + */ + printf( "\t%s.embed( (DOUBLEMASK *)((im_mask_object*)" + "_vec.data(%d))->mask );\n", + fn->argv[j].name, j ); + else if( strcmp( ty->type, IM_TYPE_STRING ) == 0 ) + /* Strings are grabbed out of the vec. + */ + printf( "\t%s = (char*) _vec.data(%d);\n", + fn->argv[j].name, j ); + else + /* Just use vips2cpp(). + */ + printf( "\t%s = *((%s*)_vec.data(%d));\n", + fn->argv[j].name, vips2cpp( ty ), j ); + } + + /* Note dependancies if out is an image and this function uses + * PIO. + */ + if( oa != -1 ) { + im_type_desc *ty = fn->argv[oa].desc; + + if( strcmp( ty->type, IM_TYPE_IMAGE ) == 0 && + (fn->flags & IM_FN_PIO) ) { + /* Loop for all input args again .. + */ + for( j = 0; j < fn->argc; j++ ) { + im_type_desc *ty2 = fn->argv[j].desc; + + /* Skip output args. + */ + if( ty2->flags & IM_TYPE_OUTPUT ) + continue; + + /* Input image. + */ + if( strcmp( ty2->type, IM_TYPE_IMAGE ) == 0 ) + printf( "\t%s._ref->addref( " + "%s._ref );\n", + fn->argv[oa].name, + fn->argv[j].name ); + else if( strcmp( ty2->type, IM_TYPE_IMAGEVEC ) + == 0 ) { + /* The out depends on every image in + * the input vector. + */ + printf( "\tfor( unsigned int i = 0; " + "i < %s.size(); i++ )\n", + fn->argv[j].name ); + printf( "\t\t%s._ref->addref( " + "%s[i]._ref );\n", + fn->argv[oa].name, + fn->argv[j].name ); + } + } + } + } + + /* Return result. + */ + if( oa != -1 ) + printf( "\n\treturn( %s );\n", fn->argv[oa].name ); + + printf( "}\n\n" ); + + return( NULL ); +} + +/* Print C++ decls for function, package or all. + */ +static void +print_cppdecls( char *name ) +{ + printf( "// this file automatically generated from\n" + "// VIPS library %s\n", im_version_string() ); + + if( map_name( name, print_cppdecl ) ) + error_exit( "unknown package \"%s\"", name ); +} + +/* Print C++ bindings for function, package or all. + */ +static void +print_cppdefs( char *name ) +{ + printf( "// this file automatically generated from\n" + "// VIPS library %s\n", im_version_string() ); + + if( map_name( name, print_cppdef ) ) + error_exit( "unknown package \"%s\"", name ); +} + +/* VIPS universal main program. + */ +int +main( int argc, char **argv ) +{ + GOptionContext *context; + GError *error = NULL; + im_function *fn; + int i, j; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + context = g_option_context_new( _( "- VIPS driver program" ) ); + + g_option_context_add_main_entries( context, + main_option, GETTEXT_PACKAGE ); + g_option_context_add_group( context, im_get_option_group() ); + + if( !g_option_context_parse( context, &argc, &argv, &error ) ) { + if( error ) { + fprintf( stderr, "%s\n", error->message ); + g_error_free( error ); + } + + error_exit( "try \"%s --help\"", g_get_prgname() ); + } + + g_option_context_free( context ); + + if( main_option_plugin ) { + if( !im_load_plugin( main_option_plugin ) ) + error_exit( "unable to load plugin %s", + main_option_plugin ); + } + if( main_option_cpph ) + print_cppdecls( main_option_cpph ); + if( main_option_cppc ) + print_cppdefs( main_option_cppc ); + if( main_option_links ) + im_map_packages( (VSListMap2Fn) print_links, NULL ); + if( main_option_list ) + print_list( main_option_list ); + if( main_option_usage ) { + if( !(fn = im_find_function( main_option_usage )) ) + error_exit( "unknown operation %s", main_option_usage ); + usage( fn ); + } + if( main_option_version ) + printf( "vips-%s\n", im_version_string() ); + + /* Remove any "--" argument. If one of our arguments is a negative + * number, the user will need to have added the "--" flag to stop + * GOption parsing. But "--" is still passed down to us and we need to + * ignore it. + */ + for( i = 1; i < argc - 1; i++ ) + if( strcmp( argv[i], "--" ) == 0 ) { + for( j = i; j < argc; j++ ) + argv[j] = argv[j + 1]; + + argc -= 1; + } + + /* Should we try to run the thing we are named as? + */ + if( !im_isprefix( "vips", g_get_prgname() ) ) { + char name[256]; + + /* Drop any .exe suffix. + */ + im_strncpy( name, g_get_prgname(), 256 ); + if( ispostfix( ".exe", name ) ) + name[strlen( name ) - 4] = '\0'; + + /* If unknown, try with "im_" prepended. + */ + if( !(fn = im_find_function( name )) ) { + im_snprintf( name, 256, "im_%s", g_get_prgname() ); + if( ispostfix( ".exe", name ) ) + name[strlen( name ) - 4] = '\0'; + + if( !(fn = im_find_function( name )) ) + error_exit( "unknown function" ); + } + + /* Execute it! + */ + if( im_run_command( name, argc - 1, argv + 1 ) ) { + usage( fn ); + error_exit( "error calling function" ); + } + } + else if( argc > 1 ) { + /* Nope ... run the first arg instead. + */ + if( im_run_command( argv[1], argc - 2, argv + 2 ) ) { + if( !(fn = im_find_function( argv[1] )) ) + error_exit( "unknown function" ); + usage( fn ); + error_exit( "error calling function" ); + } + } + + im_close_plugins(); + + return( 0 ); +} diff --git a/src/mosaicing/Makefile.am b/src/mosaicing/Makefile.am new file mode 100644 index 00000000..f6e5911e --- /dev/null +++ b/src/mosaicing/Makefile.am @@ -0,0 +1,12 @@ + +bin_PROGRAMS = \ + find_mosaic \ + mergeup + +find_mosaic_SOURCES = find_mosaic.c +mergeup_SOURCES = mergeup.c + +INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ + diff --git a/src/mosaicing/find_mosaic.c b/src/mosaicing/find_mosaic.c new file mode 100644 index 00000000..46b62ec4 --- /dev/null +++ b/src/mosaicing/find_mosaic.c @@ -0,0 +1,430 @@ +/* Join together images. + * + * find_mosaic x y file_name .0x0.v .0x1.v ... + * + * Where the image has been take with patches named as + * + * . . + * . . + * .0x1.v .1x1.v .. + * .0x0.v .1x0.v .. + * + * Uses im__find_lroverlap and im__find_tboverlap routines to make .v. + * + * It stores the tie points between patches in a data_file. + * + * It uses partials on all IO by including tbmerge / lrmerge programs. + * + * + * Copyright (C) Feb./1995, Ahmed. Abbood + * National Gallery. London + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define NUM_FILES 1000 +#define MAXPOINTS 60 +int xoverlap; +int yoverlap; + +extern int im_lrmerge(); +extern int im_merge_analysis(); +extern int im__find_lroverlap(); +extern int im__find_tboverlap(); +static int file_ptr = 0; +static IMAGE *in[ NUM_FILES ]; + + + +/* Strategy: build a tree describing the sequence of joins we want. Walk the + * tree assigning temporary file names, compile the tree into a linear + * sequence of join commands. + */ + + + +/* Decoded file name info. + */ +static char *file_root = NULL; +static char *output_file = NULL; +static int width = 0; /* Number of frames across */ +static int height = 0; /* Number of frames down */ + +static int file_list[ NUM_FILES ]; + + + + +/* Find the root name of a file name. Return new, shorter, string. + */ +static char * +find_root( name ) +char *name; +{ char *out = strdup( name ); + char *p; + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( NULL ); + } + *p = '\0'; + + /* Chop off nxn. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( NULL ); + } + *p = '\0'; + + return( out ); +} + +/* Find the x position of a file name (extract n from .nxm.v). + */ +static int +find_x( name ) +char *name; +{ int n; + char *p; + char *out = strdup( name ); + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + *p = '\0'; + + /* Find '.nxm'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + /* Read out x posn. + */ + if( sscanf( p, ".%dx%*d", &n ) != 1 ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + return( n ); +} + +/* Find the y position of a file name (extract m from .nxm.v). + */ +static int +find_y( name ) +char *name; +{ int m; + char *p; + char *out = strdup( name ); + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + *p = '\0'; + + /* Find '.nxm'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + /* Read out y posn. + */ + if( sscanf( p, ".%*dx%d", &m ) != 1 ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + free( out ); + return( m ); +} + + + + +static int +mosaic_analysis(int width, int height,IMAGE **inp, IMAGE *out, + int xoff, int yoff, int *vxdisp, int *vydisp,int *hxdisp, int *hydisp) { + + + +int i, j, dx, dy, curr_im, fx, fy; +int halfcorsize, halfareasize; +int mincorsize, minareasize; +int prev_row, curr_row, curr_disp_x, curr_disp_y; +double scale1, angle1, dx1, dy1; + + + curr_im = -1; + curr_disp_x = -1; + curr_disp_y = -1; + dy = -1; + for(i=0; i<=height; i++){ + for(j=0; j<=width; j++){ + ++curr_im; + halfcorsize = 5; + halfareasize = 14; + dx = xoff - inp[curr_im]->Xsize; + dy = yoff - inp[curr_im]->Ysize; + + if( ( j < width ) && ( width > 0 ) ){ + if( dx < 0 ){ + mincorsize = (int)(inp[curr_im]->Xsize + dx - 1)/6; + minareasize = (int)(inp[curr_im]->Xsize + dx + - 3*halfcorsize -1)/2 - mincorsize; + if(mincorsize > halfcorsize) + mincorsize = halfcorsize; + if( minareasize > 0 ){ + if( minareasize < halfareasize ){ + if( minareasize > + (int)(halfcorsize +(int)(halfcorsize/2 + 1))){ + halfareasize = minareasize; + } + else if(mincorsize > 2){ + halfcorsize=mincorsize; + halfareasize=(int)(mincorsize+mincorsize/2 +1); + } + } + } + } + + if( ( inp[curr_im]->Xsize < xoff ) || ( inp[curr_im+1]->Xsize < xoff ) || + ( inp[curr_im]->Ysize < yoff ) || ( inp[curr_im]->Ysize < yoff) ){ + ++curr_disp_x; + hxdisp[curr_disp_x] = 0; + hydisp[curr_disp_x] = 0; + } + else{ + if ( im__find_lroverlap(inp[curr_im], inp[curr_im+1], + out, 0, + (int)(inp[curr_im]->Xsize -xoff/2), + (int)(inp[curr_im]->Ysize /2), + (int)(xoff/2), (int)(inp[curr_im+1]->Ysize /2), + halfcorsize, halfareasize , &fx, &fy, + &scale1, &angle1, &dx1, &dy1 ) == -1 ) + error_exit("Unable to im__find_lroverlap"); + + ++curr_disp_x; + hxdisp[curr_disp_x] = inp[curr_im]->Xsize - xoff + fx; + hydisp[curr_disp_x] = fy; + } + } + } + if( ( i < height ) && ( height > 0 ) ){ + curr_row = curr_im+1+(int)(width/2); + prev_row = curr_im - width+(int)(width/2); + halfcorsize = 5; + halfareasize = 14; + + if( dy < 0){ + mincorsize = (int)(inp[prev_row]->Ysize + dy - 1)/6; + minareasize = (int)(inp[prev_row]->Ysize + dy + - 3*halfcorsize -1)/2 - mincorsize; + if(mincorsize > halfcorsize) + mincorsize = halfcorsize; + if( minareasize > 0 ){ + if( minareasize < halfareasize ){ + if( minareasize > + (int)(halfcorsize +(int)(halfcorsize/2 + 1))){ + halfareasize = minareasize; + } + else if(mincorsize > 2){ + halfcorsize=mincorsize; + halfareasize=(int)(mincorsize+mincorsize/2 +1); + } + } + } + } + if( ( inp[curr_row]->Xsize < xoff ) || ( inp[prev_row]->Xsize < xoff ) || + ( inp[curr_row]->Ysize < yoff ) || ( inp[prev_row]->Ysize < yoff ) ){ + ++curr_disp_y; + vxdisp[curr_disp_y] = 0; + vydisp[curr_disp_y] = 0; + } + else{ + if ( im__find_tboverlap(inp[prev_row], inp[curr_row], + out, 0, + (int)(inp[prev_row]->Xsize/2 ), + (int)(inp[prev_row]->Ysize - yoff/2 ), + (int)(inp[curr_row]->Xsize/2 ), (int)(yoff/2), + halfcorsize, halfareasize, &fx, &fy, + &scale1, &angle1, &dx1, &dy1 ) == -1 ) + error_exit("Unable to im__find_tboverlap"); + + + ++curr_disp_y; + vxdisp[curr_disp_y] = fx; + vydisp[curr_disp_y] = inp[prev_row]->Ysize - yoff + fy; + } + } + } + + + return ( 0 ); +} + + + +int +main( argc, argv ) +int argc; +char **argv; +{ + int i, n, j, k; + char name[ 1000 ]; + FILE *fp; + char *r; + IMAGE *out; + int vxdisp[NUM_FILES + 1] ; + int vydisp[NUM_FILES + 1] ; + int hxdisp[NUM_FILES + 1] ; + int hydisp[NUM_FILES + 1] ; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + /* Too many? + */ + if( argc > NUM_FILES + 1 ) + error_exit( "Too many files to merge" ); + for(i=0; i< NUM_FILES; i++) + file_list[i] = 0; + /* Too few? + */ + if( argc == 1 ) + error_exit( "usage: xoverlap yoverlap file_name " + ".0x0.v .0x1.v ..." ); + xoverlap = atoi(argv[1]); + yoverlap = atoi(argv[2]); + fp = fopen( argv[3] , "w" ); + + for( i = 4; i < argc; i++ ){ + /* Find/check root. + */ + if( !file_root ) { + file_root = find_root( argv[i] ); + if( !file_root ) + error_exit( "error at file_root" ); + } + else { + if( !(r = find_root( argv[i] )) ) + error_exit( "Error in reading parameters" ); + if( strcmp( r, file_root ) != 0 ) + error_exit( "Not all roots identical!" ); + } + + /* Read out position. + */ + if( (n = find_x( argv[i] )) < 0 ) + error_exit( "Error in reading file name" ); + if( n > width - 1 ) + width = n; + if( (n = find_y( argv[i] )) < 0 ) + error_exit( "Error in reading file name" ); + if( n > height - 1 ) + height = n; + + file_list[n] +=1; + } + + /* Make output name. and store them in an array. + */ + if( !(out = im_open( "tmp.v", "t" )) ) + error_exit("unable to open file for output"); + + file_ptr =0; + for(i=height; i>=0; i--) + for(j=0; j.0x0.v .0x1.v ... + * + * Where the image has been take with patches named as + * + * . . + * . . + * .0x1.v .1x1.v .. + * .0x0.v .1x0.v .. + * + * + * Tries to generate optimal join sequence. Does not require any intermidiate + * files for temporary storage. + * It uses partials on all IO by including tbmerge / lrmerge programs. + * + * + * Copyright (C) Feb./1995, Ahmed. Abbood + * National Gallery. London + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define NUM_FILES 1000 +#define MAXPOINTS 60 + +static int xoverlap; +static int yoverlap; + +static int file_ptr = 0; +static IMAGE *in[ NUM_FILES ]; + +/* Strategy: build a tree describing the sequence of joins we want. Walk the + * tree assigning temporary file names, compile the tree into a linear + * sequence of join commands. + */ + + + +/* Decoded file name info. + */ +static char *file_root = NULL; +static char *output_file = NULL; +static int width = 0; /* Number of frames across */ +static int height = 0; /* Number of frames down */ + +static int file_list[ NUM_FILES ]; + + +static int +im_phmerge( Rect *larea, Rect *rarea, Rect *outarea ) +{ + + Rect overlap; + + + /* Compute overlap. + */ + im_rect_intersectrect( larea, rarea, &overlap ); + + outarea->width = rarea->left + rarea->width; + outarea->height = overlap.height; + outarea->top = overlap.top; + outarea->left = larea->left; + + return( 0 ); +} + + +static int +im_pvmerge( Rect *tarea, Rect *barea, Rect *outarea ) +{ + + Rect overlap; + + + /* Compute overlap. + */ + im_rect_intersectrect( tarea, barea, &overlap ); + + outarea->width = overlap.width; + outarea->height = barea->top + barea->height ; + outarea->left = overlap.left; + outarea->top = tarea->top; + + return( 0 ); +} + + + + +static int +merge_analysis(int width,int height,IMAGE **in,int xoff, + int yoff,int *vxdisp,int *vydisp,int *hxdisp, + int *hydisp,Rect *hrect,Rect *vrect) +{ +int i,j; +int curr_im,offset; +int curr_x, curr_y; +Rect larea, rarea, barea; + + + + curr_im = -1; + curr_x = -1; + curr_y = -1; + for(i=0; i<=height; i++){ + for(j=0; j<=width; j++){ + ++curr_im; + if( width == 0 ){ + ++curr_x; + hrect[curr_x].width = in[curr_im]->Xsize; + hrect[curr_x].height= in[curr_im]->Ysize; + hrect[curr_x].top = 0; + hrect[curr_x].left = 0; + } + else{ + if( j == 0){ + ++curr_x; + + /* Area occupied by left image. + */ + larea.left = 0; + larea.top = 0; + larea.height = in[curr_im]->Ysize; + larea.width = in[curr_im]->Xsize; + /* Area occupied by right image. + */ + if( in[curr_im]->Xsize < xoff ) + offset = 0; + else + offset =xoff; + rarea.left = in[curr_im]->Xsize - (offset + hxdisp[curr_x]) ; + rarea.top = hydisp[curr_x]; + rarea.width = in[curr_im+1]->Xsize; + rarea.height = in[curr_im+1]->Ysize; + im_phmerge( &larea, &rarea, &hrect[curr_x] ); + } + else if( j < width ){ + ++curr_x; + + /* Area occupied by right image. + */ + if( in[curr_im+1]->Xsize < xoff ) + offset = 0; + else + offset =xoff; + + rarea.left = hrect[curr_x -1].width - (offset + hxdisp[curr_x]) ; + rarea.top = hydisp[curr_x]; + rarea.width = in[curr_im+1]->Xsize; + rarea.height = in[curr_im+1]->Ysize; + im_phmerge( &hrect[curr_x -1], &rarea, &hrect[curr_x] ); + } + } + } + if( i > 0 ){ + ++curr_y; + + /* Area occupied by bottom image in output. + */ + barea.left = vxdisp[curr_y]; + barea.width = hrect[curr_x].width; + barea.height = hrect[curr_x].height; + if( in[curr_x - width]->Ysize < yoff ) + offset = 0; + else + offset = yoff; + if( i == 1){ + barea.top = hrect[curr_x - width].height - offset - vydisp[curr_y] ; + im_pvmerge( &hrect[curr_x - width], &barea, &vrect[curr_y] ); + } + else{ + barea.top = vrect[curr_y - 1].height - yoff - vydisp[curr_y] ; + im_pvmerge( &vrect[curr_y -1], &barea, &vrect[curr_y] ); + } + } + } + + + return( 0 ); +} + +/* Find the root name of a file name. Return new, shorter, string. + */ +static char * +find_root( name ) +char *name; +{ char *out = strdup( name ); + char *p; + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( NULL ); + } + *p = '\0'; + + /* Chop off nxn. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( NULL ); + } + *p = '\0'; + + return( out ); +} + +/* Find the x position of a file name (extract n from .nxm.v). + */ +static int +find_x( name ) +char *name; +{ int n; + char *p; + char *out = strdup( name ); + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + *p = '\0'; + + /* Find '.nxm'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + /* Read out x posn. + */ + if( sscanf( p, ".%dx%*d", &n ) != 1 ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + return( n ); +} + +/* Find the y position of a file name (extract m from .nxm.v). + */ +static int +find_y( name ) +char *name; +{ int m; + char *p; + char *out = strdup( name ); + + /* Chop off '.v'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + *p = '\0'; + + /* Find '.nxm'. + */ + if( !(p = strrchr( out, '.' )) ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + /* Read out y posn. + */ + if( sscanf( p, ".%*dx%d", &m ) != 1 ) { + im_errormsg( "Bad file name format '%s'", name ); + free( out ); + return( -1 ); + } + + free( out ); + return( m ); +} + + + + + + +/* Join two frames left-right. Have to open them and find their sizes. + */ +static int +join_leftright(IMAGE *left, IMAGE *right, IMAGE *out, int dx, int dy ) +{ + + if (im_lrmerge(left, right, out, dx, dy, 20) == -1){ + im_errormsg("mergeup: unable to run im_lrmerge"); + return( -1 ); + } +return( 0 ); +} + + +/* Join two frames up-down. Have to open them and find their sizes. +*/ +static int +join_updown( IMAGE *top, IMAGE *bottom, IMAGE *out, int dx, int dy ) +{ + if (im_tbmerge(top, bottom, out, dx, dy, 20) == -1){ + im_errormsg("mergeup: unable to run im_tbmerge"); + return( -1 ); + } + +return( 0 ); +} + + +static int +merge_up( int width, int height, IMAGE **inp, IMAGE *outp, int xoff, int yoff, + int *hxdisp, int *hydisp, Rect *vrect ) +{ + int dx,dy,first_row; + int i, j, partial_no, in_no; + IMAGE **p_img; + char name[29]; + int v_no, h_no; + + + + p_img = (IMAGE **) malloc(1 + 3 * width * height * sizeof(IMAGE *)); + if( p_img == NULL ){ + im_errormsg("mergeup: allocation failure in mergeup"); + return( -1 ); + } + partial_no = 0; + v_no = 0; + h_no = 0; + in_no = 0; + first_row = 0; + + if( (width == 0 ) && (height == 0 ) ){ + im_errormsg("mergeup: Need more than one image"); + return( -1 ); + } + + + for(i=0; i<=height; i++){ + for(j=0; j<=width; j++){ + p_img[partial_no] = inp[in_no]; + ++partial_no; + if( j != 0 ){ + im_snprintf( name, 29, "partial_img.%d.v",partial_no ); + if( !( p_img[partial_no] = im_open( name, "p" )) ){ + im_errormsg("mergeup: unable to open partial image"); + free(p_img); + return( -1 ); + } + ++partial_no; + dy = hydisp[h_no ] ; + dx = -p_img[partial_no-3]->Xsize + hxdisp[h_no] + xoff ; + + if( (height == 0) && ( j == width) ) + join_leftright( p_img[partial_no-3], + p_img[partial_no-2],outp,dx,dy ); + else + join_leftright( p_img[partial_no-3], + p_img[partial_no-2],p_img[partial_no-1],dx,dy ); + ++h_no; + } + ++in_no; + } + + if( first_row == 0) + first_row = partial_no - 1; + + if( ( i > 0 ) || ( height == 0) ){ + if( i < height ){ + im_snprintf( name, 29, "partial_img.%d.v", partial_no ); + if( !( p_img[partial_no] = im_open( name, "p" )) ){ + im_errormsg("mergeup: unable to open partial image"); + free(p_img); + return( -1 ); + } + ++partial_no; + + dy = -( vrect[v_no].height - p_img[partial_no-2]->Ysize ); + dx = vrect[v_no].left ; + + ++v_no; + join_updown( p_img[first_row], + p_img[partial_no-2], p_img[partial_no-1],dx,dy ); + first_row = partial_no-1; + } + else{ + dy = -( vrect[v_no].height - p_img[partial_no-1]->Ysize ); + dx = vrect[v_no].left ; + + join_updown( p_img[first_row], p_img[partial_no-1],outp,dx,dy ); + } + } + } +return( 0 ); +} + + + + + +int +main( argc, argv ) +int argc; +char **argv; +{ + int i, n, j, k; + char name[ 1000 ]; + FILE *fp; + char *r; + IMAGE *out; + int vxdisp[NUM_FILES + 1] ; + int vydisp[NUM_FILES + 1] ; + int hxdisp[NUM_FILES + 1] ; + int hydisp[NUM_FILES + 1] ; + Rect hrect[NUM_FILES]; + Rect vrect[NUM_FILES]; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + /* Too many? + */ + if( argc > NUM_FILES + 1 ) + error_exit( "Too many files to merge" ); + for(i=0; i< NUM_FILES; i++) + file_list[i] = 0; + /* Too few? + */ + if( argc == 1 ) + error_exit( "usage: xoverlap yoverlap file_name output_dir " + ".0x0.v .0x1.v ..." ); + xoverlap = atoi(argv[1]); + yoverlap = atoi(argv[2]); + fp = fopen( argv[3] , "r" ); + + for( i = 5; i < argc; i++ ){ + /* Find/check root. + */ + if( !file_root ) { + file_root = find_root( argv[i] ); + if( !file_root ) + error_exit( "error at file_root" ); + } + else { + if( !(r = find_root( argv[i] )) ) + error_exit( "Error in reading parameters" ); + if( strcmp( r, file_root ) != 0 ) + error_exit( "Not all roots identical!" ); + } + + /* Read out position. + */ + if( (n = find_x( argv[i] )) < 0 ) + error_exit( "Error in reading file name" ); + if( n > width - 1 ) + width = n; + if( (n = find_y( argv[i] )) < 0 ) + error_exit( "Error in reading file name" ); + if( n > height - 1 ) + height = n; + + file_list[n] +=1; + } + + /* Make output name. and store them in an array. + */ + im_snprintf( name, 1000, "%s/paint.hr.v", argv[4] ); + if( !(out = im_open( name, "w" )) ) + error_exit("unable to open file for output"); + + file_ptr =0; + for(i=height; i>=0; i--) + for(j=0; j +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image, *matrix; + int xpos, ypos, xsize, ysize, dx, dy, flag; + + if (argc != 10) + error_exit("Usage:\n\ +%s image matrix xpos ypos xsize ysize dx dy flag\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + xpos = atoi(argv[3]); + ypos = atoi(argv[4]); + xsize = atoi(argv[5]); + ysize = atoi(argv[6]); + dx = atoi(argv[7]); + dy = atoi(argv[8]); + flag = atoi(argv[9]); + + if ( (image = im_open(argv[1],"r")) == NULL ) + error_exit("Unable to open %s for input", argv[1]); + + if ( (matrix = im_open(argv[2],"w")) == NULL ) + error_exit("Unable to open %s for output", argv[2]); + + if ( im_cooc_matrix(image, matrix, xpos, ypos, xsize, ysize, + dx, dy, flag) == -1 ) + error_exit("Unable to im_cooc_matrix"); + + if ( im_updatehist(matrix, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( ( im_close( image ) == -1 )||( im_close( matrix ) == -1 ) ) + error_exit("Unable to close %s or %s",argv[1], argv[2]); + + return(0); +} diff --git a/src/other/cooc_features.c b/src/other/cooc_features.c new file mode 100644 index 00000000..7b8a9ea6 --- /dev/null +++ b/src/other/cooc_features.c @@ -0,0 +1,86 @@ +/* @(#) Prints features of cooc to stdout + * @(#) Usage: cooc_features matrix + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + * 16/6/93 J.Cupitt + * - stupid cooc_features externs removed + * - ANSIfied + * - print to stdout + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char *argv[] ) +{ + IMAGE *matrix; + double fasm, fent, fcor, fcon; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if( argc != 2 ) + error_exit( "usage: %s matrix_image", argv[0] ); + + if( !(matrix = im_open(argv[1],"r")) ) + error_exit( "unable to open %s for input", + argv[1] ); + + if( im_cooc_asm( matrix, &fasm ) ) + error_exit( "unable to im_cooc_asm" ); + + if( im_cooc_contrast( matrix, &fcon ) ) + error_exit( "unable to im_cooc_contrast"); + + if( im_cooc_entropy( matrix, &fent ) ) + error_exit( "unable to im_cooc_entropy"); + + if( im_cooc_correlation( matrix, &fcor ) ) + error_exit( "unable to im_cooc_correlation"); + + if( im_close( matrix ) ) + error_exit( "unable to close %s", argv[1]); + + printf( "cooc: ASM=%f, ENT=%f, COR=%f, CON=%f\n", + fasm, fent, fcor, fcon); + + return( 0 ); +} diff --git a/src/other/glds.c b/src/other/glds.c new file mode 100644 index 00000000..814df4ff --- /dev/null +++ b/src/other/glds.c @@ -0,0 +1,86 @@ +/* @(#) Creates a cooourrence matrix from an image + * @(#) Usage: glds image matrix xpos ypos xsize ysize dx dy + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image, *matrix; + int xpos, ypos, xsize, ysize, dx, dy; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if (argc != 9) + error_exit("Usage:\n\ +%s image matrix xpos ypos xsize ysize dx dy\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + xpos = atoi(argv[3]); + ypos = atoi(argv[4]); + xsize = atoi(argv[5]); + ysize = atoi(argv[6]); + dx = atoi(argv[7]); + dy = atoi(argv[8]); + + if ( (image = im_open(argv[1],"r")) == NULL ) + error_exit("Unable to open %s for input", argv[1]); + + if ( (matrix = im_open(argv[2],"w")) == NULL ) + error_exit("Unable to open %s for output", argv[2]); + + if ( im_glds_matrix(image, matrix, xpos, ypos, xsize, ysize, + dx, dy) == -1 ) + error_exit("Unable to im_glds_matrix"); + + if ( im_updatehist(image, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( ( im_close( image ) == -1 )||( im_close( matrix ) == -1 ) ) + error_exit("Unable to close %s or %s", argv[1], argv[2]); + + return(0); +} diff --git a/src/other/glds_features.c b/src/other/glds_features.c new file mode 100644 index 00000000..07aff552 --- /dev/null +++ b/src/other/glds_features.c @@ -0,0 +1,83 @@ +/* @(#) Prints features of glds in stderr + * @(#) Usage: glds_features matrix + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char *argv[] ) +{ + IMAGE *matrix; + double fasm, fent, fmean, fcon; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if( argc != 2 ) + error_exit( "usage: %s matrix_image", argv[0] ); + + + if( !(matrix = im_open(argv[1],"r")) ) + error_exit( "unable to open %s for input", + argv[1] ); + + if( im_glds_asm( matrix, &fasm ) ) + error_exit( "unable to im_glds_asm"); + + if( im_glds_contrast( matrix, &fcon ) ) + error_exit( "unable to im_glds_contrast"); + + if( im_glds_entropy( matrix, &fent ) ) + error_exit( "unable to im_glds_entropy"); + + if( im_glds_mean( matrix, &fmean ) ) + error_exit( "unable to im_glds_mean"); + + if( im_close( matrix ) ) + error_exit( "unable to close %s", argv[1]); + + printf( "glds: ASM=%f, ENT=%f, MEAN=%f, CON=%f\n", + fasm, fent, fmean, fcon); + + return(0); +} diff --git a/src/other/man1/Makefile.am b/src/other/man1/Makefile.am new file mode 100644 index 00000000..01bb7536 --- /dev/null +++ b/src/other/man1/Makefile.am @@ -0,0 +1,10 @@ +man_MANS = \ + cooc.1 \ + cooc_features.1 \ + glds.1 \ + glds_features.1 \ + simcontr.1 \ + sines.1 \ + squares.1 + +EXTRA_DIST = ${man_MANS} diff --git a/src/other/man1/cooc.1 b/src/other/man1/cooc.1 new file mode 100644 index 00000000..049a7117 --- /dev/null +++ b/src/other/man1/cooc.1 @@ -0,0 +1,41 @@ +.TH COOC 1 "27 Jan 1992" +.SH NAME +cooc, cooc_features \- calculate the co-occurrence matrix and features on it +.SH SYNOPSIS +cooc image matrix xpos ypos xsize ysize dx dy flag + +.br +.B cooc_features matrix +.SH DESCRIPTION +.B cooc +creates a 256 by 256 one channel co-occurrence matrix of the box +determined by the parameters (xp, yp; xs, ys) within +the image file. The matrix +is written onto the Vasari image file matrix. The displacement +vector is determined by (dx, dy). The user must ensure that there +is enough border pixels around the box within im dictated by the displacement +vector (dx,dy) or else the program fails. +All entries of the co-occurrence matrix are double normalised to the number +of pairs involved. This function is a direct implementation of the paper: +Haralick R. M., Shanmugan K. and Dinstein I., 'Textural features for +image classification', IEEE Transactions on Systems, Man, and Cybernetics, +Vol. SMC-3, No 6, Nov. 1973, pp 610-621. + +If flag sym is 1, the created co-occurrence matrix is symmetric that is +dispacement vectors (dx, dy), (-dx, -dy) create exactly the same matrix. +If sym is 0, the created co-occurrence matrix is not symmetric that is +dispacement vectors (dx, dy), (-dx, -dy) create different matrices. + +Input image should be one band unsigned char image. + +.B cooc_features +calculates and prints at the standard error output features +of the cooccurrence matrix matrix. +.SH SEE\ ALSO +im_glds_matrix(3X), im_cooc_asm(3X), im_cooc_contrast(3X), +im_cooc_correlation(3X), im_cooc_entropy(3X) +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 27/2/1992 diff --git a/src/other/man1/cooc_features.1 b/src/other/man1/cooc_features.1 new file mode 100644 index 00000000..50459e9e --- /dev/null +++ b/src/other/man1/cooc_features.1 @@ -0,0 +1 @@ +.so man1/cooc.1 diff --git a/src/other/man1/glds.1 b/src/other/man1/glds.1 new file mode 100644 index 00000000..e98636d3 --- /dev/null +++ b/src/other/man1/glds.1 @@ -0,0 +1,35 @@ +.TH GLDS 1 "27 January 1992" +.SH NAME +glds, glds_features \- calculate the spatial grey level difference matrix and features on it +.SH SYNOPSIS +glds image matrix xpos ypos xsize ysize dx dy + +glds_features matrix +.SH DESCRIPTION +.B glds +creates a 256 by 1 one channel spatial grey level difference +matrix (sglds) of the box +determined by the parameters (xp, yp; xs, ys) within +the vasari image file image. The matrix +is written onto the vasari image file matrix. The displacement +vector is determined by (dx, dy). The user must ensure that there +is enough border pixels around the box within im dictated by the displacement +vector (dx,dy) or else the program fails. +All entries of the sgld matrix are double normalised to the number +of pairs involved. This function is a direct implementation of the paper: +Haralick R. M., Shanmugan K. and Dinstein I., 'Textural features for +image classification', IEEE Transactions on Systems, Man, and Cybernetics, +Vol. SMC-3, No 6, Nov. 1973, pp 610-621. + +Input im should be one band unsigned char image file. + +.B glds_features +calculates features on the spatial grey level difference vasari image +file matrix. The calculated features are printed in the stderr. +.SH SEE\ ALSO +im_glds_matrix(3X), im_glds_asm(3X), im_glds_contrast(3X), +im_glds_correlation(3X), im_glds_entropy(3X). +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 27/02/1992 diff --git a/src/other/man1/glds_features.1 b/src/other/man1/glds_features.1 new file mode 100644 index 00000000..0dd971a6 --- /dev/null +++ b/src/other/man1/glds_features.1 @@ -0,0 +1 @@ +.so man1/glds.1 diff --git a/src/other/man1/simcontr.1 b/src/other/man1/simcontr.1 new file mode 100644 index 00000000..4d92fae7 --- /dev/null +++ b/src/other/man1/simcontr.1 @@ -0,0 +1,18 @@ +.TH SIMCONTR 1 "27 January 1992" +.SH NAME +simcontr \- shows the effect of simultaneous contrast +.SH SYNOPSIS +.B simcontr image xsize ysize +.SH DESCRIPTION +.B simcontr +creates an unsigned char one band grey scale image of sizes xsize by ysize. +The created pattern consists of two neighbouring squares one dark (left +square) and one light (right square). The left +one containes a smaller light square and the right +one containes a darker square. +.SH SEE\ ALSO +grey(1) +.SH COPYRIGHT +N. Dessipris +.SH AUTHOR +N. Dessipris \- 27/02/1992 diff --git a/src/other/man1/sines.1 b/src/other/man1/sines.1 new file mode 100644 index 00000000..d767e5e7 --- /dev/null +++ b/src/other/man1/sines.1 @@ -0,0 +1,24 @@ +.TH SINES 1 "27 January 1992" +.SH NAME +sines, squares \- creates a spatial sine or square wave form +.SH SYNOPSIS +.B sines image xsize ysize horfreq verfreq + +.B squares image xsize ysize horfreq verfreq +.SH DESCRIPTION +.B sines +creates an one band unsigned char +image of the a sine waveform in two dimensions. +The sizes of the created image are xsize by ysize. The number of +horizontal and vertical spatial frequencies are determined by the variables +horfreq and verfreq respectively. + +.B squares +creates an one band unsigned cahr image by thresholding a sine waveform. +.SH SEE\ ALSO +grey(1), im_sines(3X), squares(1). +.SH COPYRIGHT +.br +N. Dessipris +.SH AUTHOR +N. Dessipris \- 27/02/1992 diff --git a/src/other/man1/squares.1 b/src/other/man1/squares.1 new file mode 100644 index 00000000..baa612a7 --- /dev/null +++ b/src/other/man1/squares.1 @@ -0,0 +1 @@ +.so man1/sines.1 diff --git a/src/other/simcontr.c b/src/other/simcontr.c new file mode 100644 index 00000000..d0f1e53e --- /dev/null +++ b/src/other/simcontr.c @@ -0,0 +1,78 @@ +/* @(#) Creates a pattern showing the simultaneous contrast + * @(#) Usage: simcontr file xsize ysize + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image; + int xsize, ysize; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if (argc != 4) + error_exit("Usage:\n%s file xsize ysize\n\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + xsize = atoi(argv[2]); + ysize = atoi(argv[3]); + + + if ( (image = im_openout(argv[1])) == NULL ) + error_exit("Unable to open %s for output", argv[1]); + + if ( im_simcontr(image, xsize, ysize) == -1 ) + error_exit("Unable to im_simcontr"); + + if ( im_updatehist(image, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( im_close( image ) == -1 ) + error_exit("Unable to close %s", argv[1]); + + return(0); +} diff --git a/src/other/sines.c b/src/other/sines.c new file mode 100644 index 00000000..4ba99469 --- /dev/null +++ b/src/other/sines.c @@ -0,0 +1,86 @@ +/* @(#) Creates a scaled uchar sinewave waveform. + * @(#) Usage: sines file xsize ysize horfrew verfreq + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image, *bufim; + int xsize, ysize; + double horfreq, verfreq; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if (argc != 6) + error_exit("Usage:\n%s file xsize ysize horfreq verfreq\n\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + xsize = atoi(argv[2]); + ysize = atoi(argv[3]); + horfreq = atof(argv[4]); + verfreq = atof(argv[5]); + + if ( (bufim = im_setbuf("temp.v")) == NULL ) + error_exit("Unable to set buffer image"); + + if ( im_sines(bufim, xsize, ysize, horfreq, verfreq) == -1 ) + error_exit("Unable to im_sines"); + + if ( (image = im_openout(argv[1])) == NULL ) + error_exit("Unable to open %s for output", argv[1]); + + if ( im_scale(bufim, image) == -1) + error_exit("Unable to im_scale"); + + if ( im_updatehist(image, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( ( im_close( image ) == -1 )||( im_close( bufim ) == -1 ) ) + error_exit("Unable to close %s or buffer image",argv[1]); + + return(0); +} diff --git a/src/other/spatres.c b/src/other/spatres.c new file mode 100644 index 00000000..c41b46a5 --- /dev/null +++ b/src/other/spatres.c @@ -0,0 +1,83 @@ +/* @(#) Reduces the spatial resolution of an image by increasing the + * @(#) pixel size + * @(#) + * @(#) Usage: spatres in out step + * @(#) + * + * Copyright: 1991, N. Dessipris. + * + * Author: Nicos Dessipris + * Written on: 27/03/1991 + * Modified on : + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *in, *out; + int step = 0; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if ( (argc != 4)||(argv[1][0] == '-') ) + error_exit( + "Usage:\n%s in out step\n\n\ +WARNING: The program destroys the opfile if the owner has rw access on it.", + argv[0]); + + step = atoi(argv[3]); + + if ((in= im_open(argv[1],"r")) == NULL) + error_exit("Unable to open %s for input", argv[1]); + + if ( (out=im_open(argv[2],"w")) == NULL ) + error_exit("Unable to open %s", argv[2]); + + if ( im_spatres(in, out, step) == -1) + error_exit("Unable to im_spatres"); + + if ( im_updatehist(out, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( (im_close(in) == -1)||(im_close(out) == -1) ) + error_exit("unable to close %s or %s",argv[1],argv[2]); + + return(0); +} diff --git a/src/other/squares.c b/src/other/squares.c new file mode 100644 index 00000000..a91a989f --- /dev/null +++ b/src/other/squares.c @@ -0,0 +1,87 @@ +/* @(#) Creates a byte square waveform. Binary image with 0 and 255 + * @(#) Usage: squares file xsize ysize horfreq verfreq + * + * Copyright: 1991, N. Dessipris. + * + * Author: N. Dessipris + * Written on: 26/03/1991 + * Modified on: + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +int +main( int argc, char **argv ) +{ + IMAGE *image, *bufim; + int xsize, ysize; + double horfreq, verfreq; + + if( im_init_world( argv[0] ) ) + error_exit( "unable to start VIPS" ); + + if (argc != 6) + error_exit("Usage:\n%s file xsize ysize horfreq verfreq\n\n\ +WARNING: The program overwrites the output file if the owner has rw access.", +argv[0]); + + xsize = atoi(argv[2]); + ysize = atoi(argv[3]); + horfreq = atof(argv[4]); + verfreq = atof(argv[5]); + + if ( (bufim = im_setbuf("temp.v")) == NULL ) + error_exit("Unable to set buffer image"); + + if ( im_sines(bufim, xsize, ysize, horfreq, verfreq) == -1 ) + error_exit("Unable to im_sines"); + + if ( (image = im_openout(argv[1])) == NULL ) + error_exit("Unable to open %s for output", argv[1]); + + if ( im_thresh(bufim, image, (double)0.0) == -1) + error_exit("Unable to im_thresh"); + + if ( im_updatehist(image, argv[0], argc - 1, argv + 1) == -1) + error_exit("Unable to update history"); + + if ( ( im_close( image ) == -1 )||( im_close( bufim ) == -1 ) ) + error_exit("Unable to close %s or buffer image", + argv[1]); + + return(0); +} diff --git a/src/scripts/Makefile.am b/src/scripts/Makefile.am new file mode 100644 index 00000000..ab9d58aa --- /dev/null +++ b/src/scripts/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = man1 + +bin_SCRIPTS = \ + light_correct \ + shrink_width \ + batch_image_convert \ + batch_rubber_sheet \ + batch_crop \ + vips-7.12 + +noinst_SCRIPTS = post_install + +EXTRA_DIST = \ + vips-7.12 \ + ${noinst_SCRIPTS} \ + light_correct.in \ + shrink_width.in \ + batch_image_convert.in \ + batch_rubber_sheet.in \ + batch_crop.in + +install-exec-hook: + chmod ugo+x ${DESTDIR}${bindir}/light_correct + chmod ugo+x ${DESTDIR}${bindir}/shrink_width + chmod ugo+x ${DESTDIR}${bindir}/batch_image_convert + chmod ugo+x ${DESTDIR}${bindir}/batch_rubber_sheet + chmod ugo+x ${DESTDIR}${bindir}/batch_crop diff --git a/src/scripts/batch_crop.in b/src/scripts/batch_crop.in new file mode 100644 index 00000000..982b9e66 --- /dev/null +++ b/src/scripts/batch_crop.in @@ -0,0 +1,45 @@ +#!/bin/sh + +# Crop a set of image files +# +# usage: +# +# example% batch_crop left top width height image1 image2 etc +# +# writes output images crop_image1, crop_image2 + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +name=`basename $0` +left=$1 +top=$2 +width=$3 +height=$4 +shift 4 + +# check args +if [ $# -lt 1 ]; then + echo "usage: $name left top width height image1 image2 ..." + echo + echo "$name writes a new set of images called crop_image1, " + echo "crop_image2, etc., each cropped to the specified size" + + exit 1 +fi + +# convert each argument +for i in $*; do + dir=`dirname $i` + file=`basename $i` + new=$dir/crop_$file + echo "Cropping $file as $new ..." + + if [ -f $new ]; then + echo "$new exists, skipping" + else + $VIPSHOME/bin/vips im_extract_area $i $new $left $top $width $height + fi +done + + diff --git a/src/scripts/batch_image_convert.in b/src/scripts/batch_image_convert.in new file mode 100644 index 00000000..e4fff85a --- /dev/null +++ b/src/scripts/batch_image_convert.in @@ -0,0 +1,43 @@ +#!/bin/sh + +# Convert a set of image files to new file format +# +# usage: +# +# example% batch_image_convert image1 image2 etc +# +# writes output images image1.* and image2.* where * is the new file type. + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +name=`basename $0` +type=$1 +shift + +# check args +if [ $# -lt 1 ]; then + echo "usage: $name image1 image2 ..." + echo + echo "$name uses VIPS to convert a group of image files of" + echo "any image format into a new group of images all of the same" + echo "image format. VIPS can read almost any standard image format" + echo "but it can only write VIPS, JPEG, TIFF, PPM/PBM/PGM or PNG." + + exit 1 +fi + +# convert each argument +for i in $*; do + # drop the suffix on the filename + base=${i%*.*} + echo "Converting $i to $base.$type ..." + + if [ -f $base.$type ]; then + echo "$base.$type already exists skiping $i" + else + $VIPSHOME/bin/vips im_copy $i $base.$type + fi +done + + diff --git a/src/scripts/batch_rubber_sheet.in b/src/scripts/batch_rubber_sheet.in new file mode 100644 index 00000000..d36891f9 --- /dev/null +++ b/src/scripts/batch_rubber_sheet.in @@ -0,0 +1,38 @@ +#!/bin/sh + +# Corrects a set of image files for lens distortion using a preprepared +# recombination matrix + +# usage: +# +# example% batch_rubber_sheet matrix_file image1 image2 .. +# +# writes output images rsc_image1.v, rsc_image2.v .. + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +# get name we were run as +name=`basename $0` +rec=$1 +shift + +# check args +if [ $# -lt 1 ]; then + echo "usage: $name matrix image1 image2 ..." + echo "writes rsc_image1, rsc_image2, ..." + echo + echo "$name uses VIPS to correct a set of images for lens distortion" + echo "using a matrix calculated by the 'resample' function." + + exit 1 +fi + +# transform each argument +for i in $*; do + echo "Transforming $i to rsc_$i ..." + + # bilinear interp., don't wrap edges + $VIPSHOME/bin/vips im_transform $i rsc_$i $rec 1 0 +done + diff --git a/src/scripts/light_correct.in b/src/scripts/light_correct.in new file mode 100644 index 00000000..a96f400e --- /dev/null +++ b/src/scripts/light_correct.in @@ -0,0 +1,57 @@ +#!/bin/sh +# correct a set of files for illumination errors +# usage: +# +# example% light_correct grey.v im1.v im2.v +# +# writes output images ic_im1.v and ic_im2.v + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +# get name we were run as +name=$0 +bname=`basename $name` + +# names of our temp files +t1=light_correct_temp1 +t2=light_correct_temp2 + +# check args +if [ $# -lt 2 ]; then + echo "${bname}: usage: $bname ..." + exit 1 +fi + +echo "Preparing grey ..." +grey=$1 +shift + +# find image size +width=`$VIPSHOME/bin/vips im_header_int Xsize $grey` +height=`$VIPSHOME/bin/vips im_header_int Ysize $grey` + +# smooth the grey out +$VIPSHOME/bin/vips im_shrink $grey $t1.v 20 20 +$VIPSHOME/bin/vips im_resize_linear $t1.v $t2.v $width $height + +# and make the correction image +mean=`$VIPSHOME/bin/vips im_avg $t2.v` +$VIPSHOME/bin/vips im_powtra $t2.v $t1.v -1 +$VIPSHOME/bin/vips im_lintra $mean $t1.v 0 $t2.v + +# grey correct images in order +for i in "$@"; do + echo "Correcting $i as ic_$i ..." + $VIPSHOME/bin/vips im_multiply $t2.v "$i" $t1.v + $VIPSHOME/bin/vips im_clip $t1.v "ic_$i" + + # remove the .desc as well + name=`echo $name | sed -e 's/\.[^\.]*//'` + /bin/rm -f "ic_$name.desc" +done + +# more cleanup +echo "Cleaning up ..." +/bin/rm -f $t1.v $t1.desc +/bin/rm -f $t2.v $t2.desc diff --git a/src/scripts/man1/Makefile.am b/src/scripts/man1/Makefile.am new file mode 100644 index 00000000..0604d009 --- /dev/null +++ b/src/scripts/man1/Makefile.am @@ -0,0 +1,7 @@ +man_MANS = \ + light_correct.1 \ + batch_image_convert.1 \ + batch_crop.1 \ + batch_rubber_sheet.1 + +EXTRA_DIST = ${man_MANS} diff --git a/src/scripts/man1/batch_crop.1 b/src/scripts/man1/batch_crop.1 new file mode 100644 index 00000000..4546f071 --- /dev/null +++ b/src/scripts/man1/batch_crop.1 @@ -0,0 +1,22 @@ +.TH BATCH_CROP 1 "2 Feb 2002" +.SH NAME +batch_crop \- crop a set of images +.SH SYNOPSIS +.B batch_crop left top width height image1 image2 ... +.SH DESCRIPTION +The area defined by the rectangle left, top, width, height is cropped out of +each of the images and saved in a file of the same name, but prefixed by +"crop_". + +For example: + + batch_crop 10 10 100 100 fred.jpg jim.png + +will make two images, crop_fred.jpg and crop_jim.png, each of 100 by 100 +pixels, taken from the corresponding input images. + +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1), im_vips2tiff(3), im_vips2jpeg(3), im_vips2png(3), im_vips2ppm(3) + diff --git a/src/scripts/man1/batch_image_convert.1 b/src/scripts/man1/batch_image_convert.1 new file mode 100644 index 00000000..1ae6d054 --- /dev/null +++ b/src/scripts/man1/batch_image_convert.1 @@ -0,0 +1,29 @@ +.TH BATCH_IMAGE_CONVERT 1 "2 Feb 2002" +.SH NAME +batch_image_convert \- use VIPS to convert a set of images to a new type +.SH SYNOPSIS +.B batch_image_convert type image1 image2 ... +.SH DESCRIPTION +The first argument is the name of an image type, subsequent arguments are +the names of files to be converted to that type. VIPS can usually read almost +any image type, but it can only write VIPS, PNG, TIFF, PPM/PGM/PBM and JPEG. +You can specify conversion parameters in the type name. + +For example: + + batch_image_convert tiff fred.jpg jim.png + +will convert +.B fred.jpg +and +.B jim.png +to TIFF format. + + batch_image_convert jpeg:95 jim.png + +will write jim.jpeg with a 95% quality factor. +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1), im_vips2tiff(3), im_vips2jpeg(3), im_vips2png(3), im_vips2ppm(3) + diff --git a/src/scripts/man1/batch_rubber_sheet.1 b/src/scripts/man1/batch_rubber_sheet.1 new file mode 100644 index 00000000..976d80e6 --- /dev/null +++ b/src/scripts/man1/batch_rubber_sheet.1 @@ -0,0 +1,30 @@ +.TH BATCH_RUBBER_SHEET 1 "2 Feb 2002" +.SH NAME +batch_rubber_sheet \- warp a set of images with a rubber-sheet transformation +.SH SYNOPSIS +.B batch_rubber_sheet matrix image1 image2 ... +.SH DESCRIPTION +The first argument specifies a file containing the transformation, subsequent +arguments are image files to be transformed. The transformed image is written +to a new file, named as the old file, but with "rsc_" prepended to the file +name. + +For example: + + batch_rubber_sheet lens.mat fred.jpg jim.png + +will read a transform from the file +.B lens.mat +and apply it to +.B fred.jpg +and +.B jim.png, +writing files +.B rsc_fred.jpg +and +.B rsc_jim.png. +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +The "Image=>Rubber" menu in nip. + diff --git a/src/scripts/man1/light_correct.1 b/src/scripts/man1/light_correct.1 new file mode 100644 index 00000000..f992499b --- /dev/null +++ b/src/scripts/man1/light_correct.1 @@ -0,0 +1,39 @@ +.TH LIGHT_CORRECT 1 "14 Oct 1996" +.SH NAME +light_correct \- correct illumination errors on set of images +.SH SYNOPSIS +.B light_correct grey image1 image2 image3 ... +.SH DESCRIPTION +The first argument should be an image of a piece of grey card, subsequent +arguments should be images taken with the same lighting set-up which need +correcting. The corrected images are written to files prefixed with "ic_". + +For example, suppose you have a directory with the following files in: + + example% ls + dat1.1.v dat1.2.v dat2.1.v dat2.2.v dat3.1.v dat3.2.v + dat4.1.v dat4.2.v grey.v + +then run light_correct like this: + + example% light_correct grey.v dat*.v + +to generate this: + + example% ls + dat1.1.v dat1.2.v dat2.1.v dat2.2.v dat3.1.v dat3.2.v + dat4.1.v dat4.2.v grey.v + ic_dat1.1.v ic_dat1.2.v ic_dat2.1.v ic_dat2.2.v ic_dat3.1.v + ic_dat3.2.v ic_dat4.1.v ic_dat4.2.v + +light_correct works by smoothing out the grey card image, finding +grey-mean/pixel for each point, and then multiplying the result by all the +following images. It also removes any .desc files it generates, to avoid +problems with im_global_balance(3). + +.SH RETURN VALUE +returns 0 on success and non-zero on error. +.SH SEE ALSO +header(1), vips(1) +.SH COPYRIGHT +The National Gallery and Birkbeck College, 1989-1996. diff --git a/src/scripts/post_install b/src/scripts/post_install new file mode 100755 index 00000000..6bb9d65a --- /dev/null +++ b/src/scripts/post_install @@ -0,0 +1,5 @@ +#!/bin/sh + +bin=$1 + +( cd $bin ; $bin/vips -k | sh ) diff --git a/src/scripts/shrink_width.in b/src/scripts/shrink_width.in new file mode 100644 index 00000000..c9ba5764 --- /dev/null +++ b/src/scripts/shrink_width.in @@ -0,0 +1,18 @@ +#!/bin/sh +# shrink to a target width + +# default prefix +VIPSHOME=${VIPSHOME-@prefix@} + +name=$0 +bname=`basename $0` + +if [ $# != 3 ]; then + echo "${bname}: usage: $bname " + exit 1 +fi + +inwidth=`$VIPSHOME/bin/vips im_header_int Xsize $1` +factor=`(echo scale=10; echo $inwidth / $3) | bc` + +$VIPSHOME/bin/vips im_shrink $1 $2 $factor $factor diff --git a/src/scripts/vips-7.12 b/src/scripts/vips-7.12 new file mode 100755 index 00000000..ee887528 --- /dev/null +++ b/src/scripts/vips-7.12 @@ -0,0 +1,116 @@ +#!/bin/bash +# +# Start script for VIPS + +# need extended regexps, hence we insist on bash above +shopt -s extglob +# set -x + +# name we were invoked as +bname=`basename $0` + +# check args +if [[ $# < 1 ]]; then + echo "usage: $bname [command ...]" + echo "examples:" + echo " $bname nip" + echo " $bname man im_invert" + echo " $bname im_invert /pics/tmp/fred.jpg /pics/tmp/fred2.tif" + exit 1 +fi + +# try to extract the prefix from a path to an executable +# eg. "/home/john/vips/bin/fred" -> "/home/john/vips" +function find_prefix () { + # try to canonicalise the path + ep_canon=$1 + + # relative path? prefix with pwd + if [ ${ep_canon:0:1} != "/" ]; then + ep_canon=`pwd`/$ep_canon + fi + + # replace any "/./" with "/" + ep_canon=${ep_canon//\/.\//\/} + + # any "xxx/../" can go + ep_canon=${ep_canon//+([^\/])\/..\//} + + # trailing "xxx/.." can go + ep_canon=${ep_canon/%+([^\/])\/../} + + # remove trailing "/bin/xxx" to get the prefix + ep_prefix=${ep_canon/%\/bin\/+([^\/])/} + + # was there anything to remove in that final step? if not, the path + # must be wrong + if [ x$ep_prefix == x$ep_canon ]; then + return 1 + fi + + echo $ep_prefix; + + return 0 +} + +# try to guess the install prefix from $0 +function guess_prefix () { + # $0 is a file? must be us + if [ -f $0 ]; then + find_prefix $0 + return + fi + + # nope, extract program name from $0 and try looking along the + # searchpath for it + name=`basename $0` + + fred=$PATH + while [ x$fred != x"" ]; do + path=${fred/:*/}/$name + fred=${fred/*([^:])?(:)/} + + if [ -f $path ]; then + find_prefix $path + return + fi + done + + # not found on path either ... give up! + return 1 +} + +prefix=`guess_prefix`; + +if [ $? != 0 ]; then + echo "unable to find $0 from the file name, or from your PATH" + echo "either run directly, or add the install bin area to " + echo "your PATH" + exit 1 +fi + +export VIPSHOME=$prefix + +# add VIPSHOME to man pages +export MANPATH=$VIPSHOME/man:$MANPATH + +# add the VIPS lib area to the library path +case `uname` in +HPUX) + export SHLIB_PATH=$VIPSHOME/lib:$SHLIB_PATH + ;; + +Darwin) + export DYLD_LIBRARY_PATH=$VIPSHOME/lib:$DYLD_LIBRARY_PATH + ;; + +*) + export LD_LIBRARY_PATH=$VIPSHOME/lib:$LD_LIBRARY_PATH + ;; +esac + +# add VIPS bin area to path +export PATH=$VIPSHOME/bin:$PATH + +# run, passing in args we were passed +exec $* diff --git a/vips-7.12.pc.in b/vips-7.12.pc.in new file mode 100644 index 00000000..ee3aaf2c --- /dev/null +++ b/vips-7.12.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: vips-7.12 +Description: Image processing library +Version: @VERSION@ +Requires: @PACKAGES_USED@ +Libs: -L${libdir} -lvips @VIPS_LIBS@ +Cflags: -I${includedir} diff --git a/vips-7.12.spec.in b/vips-7.12.spec.in new file mode 100644 index 00000000..382281f5 --- /dev/null +++ b/vips-7.12.spec.in @@ -0,0 +1,114 @@ +%define name @PACKAGE@ +%define version @VERSION@ +%define release 1 +%define default_install_prefix /usr/local + +Summary: an image processing library +Name: %{name} +Version: %{version} +Release: %{release} +License: LGPL +Group: Development/Libraries +Source: http://www.vips.ecs.soton.ac.uk/vips-7.12/%{name}-%{version}.tar.gz +URL: http://www.vips.ecs.soton.ac.uk/ +BuildRoot: %{_tmppath}/%{name}-buildroot +Provides: vips +Prefix: %{default_install_prefix} + +%description +VIPS is an image processing library. It is good for very large images (ie. +larger than the amount of RAM in your machine), and for working with colour. +It includes a C++ API, complete man pages, a command-line interface, automatic +threading and an operation database. There are several user interfaces +built on top of VIPS: for example "nip2". + +%package devel +Summary: Headers and links to develop with vips +Requires: vips = %{version} +Group: Development/Libraries + +%description devel +These headers are needed to develop programs which want to use the vips +library. Programs already linked against it only need the vips package. + +%package python +Summary: Python bindings to use vips +Requires: vips = %{version} +Group: Development/Libraries + +%description python +Includes the necessary files to use and develop with vips and python + +%prep +rm -rf $RPM_BUILD_ROOT +%setup + +%build +# grr ... why do we have to set prefix by hand? if you don't do this, you get +# /usr, despite the "Prefix:" tag above +%define _prefix %{default_install_prefix} +%configure + +%install +%makeinstall + +# paste in the non-GPL stuff as a binary plugin +# comment this out if you don't have it :-( +# cp ~john/develop/transform-7.9/src/resample.plg ${RPM_BUILD_ROOT}%{_libdir} + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +ldconfig + +%files +# don't list explicitly ... install to a private prefix and rely on naming the +# dirs to pick up the files +%attr(-, root, root) +%doc AUTHORS THANKS README TODO COPYING ChangeLog +%{_bindir} +%{_libdir}/libvips*.so.* +%doc %{_mandir} +%doc %{_datadir}/doc/vips +%{_datadir}/vips +%{_datadir}/locale + +%files devel +%{_includedir}/vips +%{_libdir}/libvips*.so +%{_libdir}/libvips*.a +%{_libdir}/libvips*.la +%{_libdir}/pkgconfig/* + +%files python +%{_libdir}/python* + +%changelog -n vips +* Fri Apr 27 2007 John Cupitt 7.12 +- updated for 7.12 + +* Fri Dec 29 2006 - plasmahh@projectiwear.org +- Split up into library and devel package + +* Wed Jun 1 2005 John Cupitt 7.11 +- updated for 7.11 + +* Tue Dec 14 2004 John Cupitt 7.10.8 +- updated for 7.10.8 +- now updated from configure +- implicit deps and files + +* Wed Jul 16 2003 John Cupitt 7.8.10 +- updated for 7.8.10 +- updated %files +- copies formatted docs to install area + +* Wed Mar 12 2003 John Cupitt 7.8.8 +- updated for 7.8.8, adding libdrfftw + +* Mon Feb 3 2003 John Cupitt 7.8.7-2 +- hack to change default install prefix to /usr/local + +* Thu Jan 30 2003 John Cupitt 7.8.7-1 +- first stab at an rpm package for vips diff --git a/vipsCC-7.12.pc.in b/vipsCC-7.12.pc.in new file mode 100644 index 00000000..080a8e7b --- /dev/null +++ b/vipsCC-7.12.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: vipsCC-7.12 +Description: C++ API for vips image processing library +Version: @VERSION@ +Requires: vips-7.12 = @VERSION@ +Libs: -L${libdir} -lvipsCC diff --git a/win32/README b/win32/README new file mode 100644 index 00000000..3405ac06 --- /dev/null +++ b/win32/README @@ -0,0 +1,15 @@ +Build files for vips.dll with the MS toolchain + +proj/ + + Build system based on hand-written makefiles for MSVC 6, copy into + vips libsrc area and tweak files before building. No support, sorry. + +tmake/ + + Build system based on trolltech's tmake. + +msvc/ + + A set of project files for visual studio. You'll need to copy them + into the source area and edit them a bit. Version 6 only. diff --git a/win32/msvc/config.h b/win32/msvc/config.h new file mode 100644 index 00000000..462f91bb --- /dev/null +++ b/win32/msvc/config.h @@ -0,0 +1,243 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* define to open non-text files in binary mode */ +#define BINARY_OPEN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have fftw libraries and header files. */ +//#define HAVE_FFTW 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define to 1 if you have the `getwd' function. */ +/* #undef HAVE_GETWD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_IO_H 1 + +/* Define if you have jpeg libraries and header files. */ +#define HAVE_JPEG 1 + +/* Define if you have lcms libraries and header files. */ +//#define HAVE_LCMS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* define if you have the libMagick libraries and header files. */ +/* #undef HAVE_MAGICK */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mkstemp' function. */ +/* #undef HAVE_MKSTEMP */ + +/* Define to 1 if you have the `mktemp' function. */ +#define HAVE_MKTEMP 1 + +/* Define to 1 if you have a working `mmap' system call. */ +/* #undef HAVE_MMAP */ + +/* Define to 1 if you have the `munmap' function. */ +/* #undef HAVE_MUNMAP */ + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have png libraries and header files. */ +#define HAVE_PNG 1 + +/* Define if you have POSIX threads libraries and header files. */ +//#define HAVE_PTHREAD 1 + +/* have pthread_setconcurrency() */ +//#define HAVE_PTHREAD_SETCONCURRENCY 1 + +/* Define to 1 if you have the `putenv' function. */ +#define HAVE_PUTENV 1 + +/* Define to 1 if you have the `rand' function. */ +#define HAVE_RAND 1 + +/* Define to 1 if you have the `random' function. */ +/* #undef HAVE_RANDOM */ + +/* Define to 1 if you have the `realpath' function. */ +/* #undef HAVE_REALPATH */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strrchr' function. */ +#define HAVE_STRRCHR 1 + +/* Define to 1 if you have the `strspn' function. */ +#define HAVE_STRSPN 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have tiff libraries and header files. */ +#define HAVE_TIFF 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_UNISTD_H 1 + +/* have video4linux 1 */ +/* #undef HAVE_VIDEODEV */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 +#define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) + + + +/* Define to 1 if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have libz libraries and header files. */ +#define HAVE_ZIP 1 + +/* Name of package */ +#define PACKAGE "vips" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "7.8.7" + +/* Define if using the dmalloc debugging malloc package */ +/* #undef WITH_DMALLOC */ + +/* Define to 1 if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +#define mode_t int + +/* Define to `long' if does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +#define PATH_MAX 512 +#define R_OK 4 diff --git a/win32/msvc/vips.dsp b/win32/msvc/vips.dsp new file mode 100644 index 00000000..95348017 --- /dev/null +++ b/win32/msvc/vips.dsp @@ -0,0 +1,1327 @@ +# Microsoft Developer Studio Project File - Name="vips" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=vips - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "vips.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "vips.mak" CFG="vips - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "vips - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "vips - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "vips - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VIPS_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "." /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VIPS_EXPORTS" /D "HAVE_CONFIG_H" /YX /FD /I./include /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc0a /d "NDEBUG" +# ADD RSC /l 0xc0a /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ./lib/libtiff.lib ./lib/jpeg.lib ./lib/libpng.lib ./lib/zlib.lib /nologo /dll /machine:I386 /nodefaultlib:"libc.lib" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "vips - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VIPS_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "VIPS_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc0a /d "_DEBUG" +# ADD RSC /l 0xc0a /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "vips - Win32 Release" +# Name "vips - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "acquire" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\acquire\im_clamp.c +# End Source File +# End Group +# Begin Group "arithmetic" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\arithmetic\arith_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_abs.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_add.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_avg.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_ceil.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_cmulnorm.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_costra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_deviate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_divide.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_expntra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_fav4.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_floor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_gadd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_gaddim.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_gfadd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_invert.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_lintra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_litecor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_log10tra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_logtra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_max.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_maxpos.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_measure.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_min.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_minpos.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_multiply.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_powtra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_remainder.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_sign.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_sintra.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_stats.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_subtract.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\arithmetic\im_tantra.c +# End Source File +# End Group +# Begin Group "boolean" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\boolean\bool_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\boolean\boolean.c +# End Source File +# End Group +# Begin Group "colour" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\colour\colour.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\colour_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\derived.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_dE00_fromLab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_dE_fromLab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_dECMC_fromLab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_disp2XYZ.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_icc_transform.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Lab2LabQ.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Lab2LabS.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Lab2LCh.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Lab2XYZ.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_lab_morph.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabQ2disp.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabQ2Lab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabQ2LabS.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabS2Lab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LabS2LabQ.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LCh2Lab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_LCh2UCS.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_UCS2LCh.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_XYZ2disp.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_XYZ2Lab.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_XYZ2Yxy.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\colour\im_Yxy2XYZ.c +# End Source File +# End Group +# Begin Group "conversion" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\conversion\conver_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_bandjoin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_bernd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_black.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2amph.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2imag.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2ps.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2real.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_c2rect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_clip.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_copy.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_extract.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_falsecolour.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_fliphor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_flipver.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_gbandjoin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_insert.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_lrjoin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_magick2vips.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_mask2vips.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_ppm2vips.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_print.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_recomb.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_ri2c.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_rot180.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_rot270.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_rot90.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_scale.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_scaleps.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_slice.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_subsample.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_system.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_tbjoin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_thresh.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_tiff2vips.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_vips2mask.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_vips2ppm.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_vips2tiff.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\im_zoom.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\vips_jpeg.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\conversion\vips_png.c +# End Source File +# End Group +# Begin Group "convolution" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\convolution\convol_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_addgnoise.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_compass.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_conv.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_convf.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_convsep.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_convsepf.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_convsub.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_embed.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_fastcor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_gaussmasks.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_gaussnoise.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_gradient.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_lindetect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_logmasks.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_maxvalue.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_mpercent.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_rank.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_resize_linear.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_sharpen.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_shrink.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_spcor.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_stretch3.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\im_zerox.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\rotmask.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\convolution\rw_mask.c +# End Source File +# End Group +# Begin Group "freq_filt" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\freq_filt\fft_sp.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\fmask4th.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\fmaskcir.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\freq_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_disp_ps.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_fractsurf.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_freq_mask.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_freqflt.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_fwfft.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_invfft.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_invfftr.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\freq_filt\im_rotquad.c +# End Source File +# End Group +# Begin Group "histograms_lut" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\hist_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_gammacorrect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_heq.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_hist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histeq.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histgr.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histnD.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histplot.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_histspec.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_hsp.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_identity.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_invertlut.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_lhisteq.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_maplut.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\im_stdif.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\histograms_lut\tone.c +# End Source File +# End Group +# Begin Group "inplace" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\inplace\im_circle.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_flood.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_insertplace.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_line.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_paintrect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\im_plotmask.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\inplace_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\line_draw.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\plot_point.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\inplace\smudge_area.c +# End Source File +# End Group +# Begin Group "iofuncs" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\iofuncs\callback.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\debug.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\dispatch_types.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\error.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\error_exit.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_append_Hist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_binfile.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_close.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_cp_desc.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_cp_Hist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_crwrhd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_debugim.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_demand_hint.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_desc_hd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_generate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_guess_prefix.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_header.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_histlin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_image.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_init.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_initdesc.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_inithd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_iocheck.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_iterate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_makerw.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_mapfile.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_open.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_openin.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_openout.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_partial.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_piocheck.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_prepare.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_printdesc.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_printhd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_printlines.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_readhist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_setbox.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_setbuf.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_setupout.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_unmapfile.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_updatehist.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_wrapmany.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_wrapone.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\im_writeline.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\list.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\memory.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\package.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\predicate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\rect.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\region.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\thread.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\threadgroup.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\iofuncs\time.c +# End Source File +# End Group +# Begin Group "matrix" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\matrix\im_invmat.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\im_matcat.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\im_matinv.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\im_matmul.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\im_mattrn.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\matalloc.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\matrix\matrix_dispatch.c +# End Source File +# End Group +# Begin Group "morphology" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\morphology\im_cntlines.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\morphology\im_dilate.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\morphology\im_erode.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\morphology\im_profile.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\morphology\morph_dispatch.c +# End Source File +# End Group +# Begin Group "mosaicing" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\mosaicing\global_balance.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\global_balance.h +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_affine.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_avgdxdy.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_chkpair.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_clinear.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_improve.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_initialize.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_lrcalcon.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_lrmerge.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_lrmosaic.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_remosaic.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_tbcalcon.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_tbmerge.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\im_tbmosaic.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\match.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\merge.h +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\mosaic.h +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\mosaic1.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\mosaicing_dispatch.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\mosaicing\similarity.c +# End Source File +# End Group +# Begin Group "other" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\other\cooc_funcs.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\glds_funcs.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_dif_std.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_eye.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_grey.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_meanstd.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_simcontr.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_sines.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_spatres.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\im_zone.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\other\other_dispatch.c +# End Source File +# End Group +# Begin Group "relational" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\relational\im_blend.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\relational\im_ifthenelse.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\relational\relational.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\relational\relational_dispatch.c +# End Source File +# End Group +# Begin Group "video" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\libsrc\video\im_video_v4l1.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\video\video_dispatch.c +# End Source File +# End Group +# Begin Source File + +SOURCE=.\libsrc\dummy.c +# End Source File +# Begin Source File + +SOURCE=.\libsrc\vips.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Group "vips" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\include\vips\colour.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\debug.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\dispatch.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\fmask.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\history.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\list.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\mosaic.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\proto.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\rect.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\region.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\struct.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\thread.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\threadgroup.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\time.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\util.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\version.h +# End Source File +# Begin Source File + +SOURCE=.\include\vips\vips.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\config.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win32/msvc/vips.dsw b/win32/msvc/vips.dsw new file mode 100644 index 00000000..2df5c036 --- /dev/null +++ b/win32/msvc/vips.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "vips"=.\vips.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/win32/msvc/vips.opt b/win32/msvc/vips.opt new file mode 100644 index 0000000000000000000000000000000000000000..b0ab0d0678f48d235238306b7f6a155ee2f9e270 GIT binary patch literal 49664 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 literal 0 HcmV?d00001 diff --git a/win32/proj/README b/win32/proj/README new file mode 100644 index 00000000..32841b77 --- /dev/null +++ b/win32/proj/README @@ -0,0 +1,3 @@ +sample config and makefiles for building with the MSC toolchain + +experts only, no support! diff --git a/win32/proj/config.h.win32 b/win32/proj/config.h.win32 new file mode 100644 index 00000000..462f91bb --- /dev/null +++ b/win32/proj/config.h.win32 @@ -0,0 +1,243 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* define to open non-text files in binary mode */ +#define BINARY_OPEN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have fftw libraries and header files. */ +//#define HAVE_FFTW 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define to 1 if you have the `getwd' function. */ +/* #undef HAVE_GETWD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_IO_H 1 + +/* Define if you have jpeg libraries and header files. */ +#define HAVE_JPEG 1 + +/* Define if you have lcms libraries and header files. */ +//#define HAVE_LCMS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* define if you have the libMagick libraries and header files. */ +/* #undef HAVE_MAGICK */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mkstemp' function. */ +/* #undef HAVE_MKSTEMP */ + +/* Define to 1 if you have the `mktemp' function. */ +#define HAVE_MKTEMP 1 + +/* Define to 1 if you have a working `mmap' system call. */ +/* #undef HAVE_MMAP */ + +/* Define to 1 if you have the `munmap' function. */ +/* #undef HAVE_MUNMAP */ + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have png libraries and header files. */ +#define HAVE_PNG 1 + +/* Define if you have POSIX threads libraries and header files. */ +//#define HAVE_PTHREAD 1 + +/* have pthread_setconcurrency() */ +//#define HAVE_PTHREAD_SETCONCURRENCY 1 + +/* Define to 1 if you have the `putenv' function. */ +#define HAVE_PUTENV 1 + +/* Define to 1 if you have the `rand' function. */ +#define HAVE_RAND 1 + +/* Define to 1 if you have the `random' function. */ +/* #undef HAVE_RANDOM */ + +/* Define to 1 if you have the `realpath' function. */ +/* #undef HAVE_REALPATH */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strrchr' function. */ +#define HAVE_STRRCHR 1 + +/* Define to 1 if you have the `strspn' function. */ +#define HAVE_STRSPN 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have tiff libraries and header files. */ +#define HAVE_TIFF 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_UNISTD_H 1 + +/* have video4linux 1 */ +/* #undef HAVE_VIDEODEV */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 +#define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) + + + +/* Define to 1 if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have libz libraries and header files. */ +#define HAVE_ZIP 1 + +/* Name of package */ +#define PACKAGE "vips" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "7.8.7" + +/* Define if using the dmalloc debugging malloc package */ +/* #undef WITH_DMALLOC */ + +/* Define to 1 if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +#define mode_t int + +/* Define to `long' if does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +#define PATH_MAX 512 +#define R_OK 4 diff --git a/win32/proj/libsrc/acquire/makefile.msc b/win32/proj/libsrc/acquire/makefile.msc new file mode 100644 index 00000000..3d199b2b --- /dev/null +++ b/win32/proj/libsrc/acquire/makefile.msc @@ -0,0 +1,41 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = acquire +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_clamp.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/arithmetic/makefile.msc b/win32/proj/libsrc/arithmetic/makefile.msc new file mode 100644 index 00000000..3991a549 --- /dev/null +++ b/win32/proj/libsrc/arithmetic/makefile.msc @@ -0,0 +1,75 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = arithmetic +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + arith_dispatch.obj \ + im_abs.obj \ + im_add.obj \ + im_avg.obj \ + im_cmulnorm.obj \ + im_costra.obj \ + im_deviate.obj \ + im_divide.obj \ + im_ceil.obj \ + im_floor.obj \ + im_expntra.obj \ + im_fav4.obj \ + im_gadd.obj \ + im_gaddim.obj \ + im_gfadd.obj \ + im_invert.obj \ + im_lintra.obj \ + im_litecor.obj \ + im_log10tra.obj \ + im_logtra.obj \ + im_max.obj \ + im_maxpos.obj \ + im_measure.obj \ + im_min.obj \ + im_minpos.obj \ + im_multiply.obj \ + im_powtra.obj \ + im_remainder.obj \ + im_sign.obj \ + im_sintra.obj \ + im_stats.obj \ + im_subtract.obj \ + im_tantra.obj \ + +INCLUDES = \ + -I$(PRJ_TOP) -DHAVE_CONFIG_H \ + -I$(PRJ_TOP)/include + +# @VIPS_CFLAGS@ @VIPS_INCLUDES@ + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/boolean/makefile.msc b/win32/proj/libsrc/boolean/makefile.msc new file mode 100644 index 00000000..bb4b24bf --- /dev/null +++ b/win32/proj/libsrc/boolean/makefile.msc @@ -0,0 +1,42 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = boolean +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +INCLUDES = \ + -I$(PRJ_TOP) -DHAVE_CONFIG_H \ + -I$(PRJ_TOP)/include + +OBJECTS = \ + bool_dispatch.obj \ + boolean.obj \ + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/colour/makefile.msc b/win32/proj/libsrc/colour/makefile.msc new file mode 100644 index 00000000..9ce4485f --- /dev/null +++ b/win32/proj/libsrc/colour/makefile.msc @@ -0,0 +1,65 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = colour +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + colour.obj \ + colour_dispatch.obj \ + derived.obj \ + im_dE00_fromLab.obj \ + im_icc_transform.obj \ + im_LCh2Lab.obj \ + im_LCh2UCS.obj \ + im_Lab2LCh.obj \ + im_Lab2LabQ.obj \ + im_Lab2LabS.obj \ + im_Lab2XYZ.obj \ + im_LabQ2Lab.obj \ + im_LabQ2LabS.obj \ + im_LabQ2disp.obj \ + im_LabS2LabQ.obj \ + im_LabS2Lab.obj \ + im_lab_morph.obj \ + im_UCS2LCh.obj \ + im_XYZ2Lab.obj \ + im_XYZ2Yxy.obj \ + im_Yxy2XYZ.obj \ + im_XYZ2disp.obj \ + im_dECMC_fromLab.obj \ + im_dE_fromLab.obj \ + im_disp2XYZ.obj \ + +INCLUDES = \ + -I$(PRJ_TOP) -DHAVE_CONFIG_H \ + -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/conversion/makefile.msc b/win32/proj/libsrc/conversion/makefile.msc new file mode 100644 index 00000000..495e0a86 --- /dev/null +++ b/win32/proj/libsrc/conversion/makefile.msc @@ -0,0 +1,84 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = conversion +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include \ + $(TIFF_CFLAGS) \ + $(JPEG_CFLAGS) \ + $(PNG_CFLAGS) + +OBJECTS = \ + im_bernd.obj \ + im_vips2tiff.obj \ + im_tiff2vips.obj \ + conver_dispatch.obj \ + im_bandjoin.obj \ + im_black.obj \ + im_c2amph.obj \ + im_c2rect.obj \ + im_c2imag.obj \ + im_c2ps.obj \ + im_c2real.obj \ + im_clip.obj \ + im_copy.obj \ + im_extract.obj \ + im_falsecolour.obj \ + im_fliphor.obj \ + im_flipver.obj \ + im_gbandjoin.obj \ + im_insert.obj \ + im_lrjoin.obj \ + im_magick2vips.obj \ + im_mask2vips.obj \ + im_ppm2vips.obj \ + im_recomb.obj \ + im_ri2c.obj \ + im_rot180.obj \ + im_rot270.obj \ + im_rot90.obj \ + im_scale.obj \ + im_scaleps.obj \ + im_slice.obj \ + im_subsample.obj \ + im_system.obj \ + im_print.obj \ + im_tbjoin.obj \ + im_thresh.obj \ + im_vips2mask.obj \ + im_vips2ppm.obj \ + vips_jpeg.obj \ + im_zoom.obj \ + vips_png.obj \ + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/convolution/makefile.msc b/win32/proj/libsrc/convolution/makefile.msc new file mode 100644 index 00000000..7d4bc748 --- /dev/null +++ b/win32/proj/libsrc/convolution/makefile.msc @@ -0,0 +1,66 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = convolution +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + rotmask.obj \ + rw_mask.obj \ + convol_dispatch.obj \ + im_addgnoise.obj \ + im_compass.obj \ + im_conv.obj \ + im_convf.obj \ + im_convsep.obj \ + im_convsepf.obj \ + im_convsub.obj \ + im_embed.obj \ + im_fastcor.obj \ + im_gaussmasks.obj \ + im_gaussnoise.obj \ + im_gradient.obj \ + im_lindetect.obj \ + im_logmasks.obj \ + im_maxvalue.obj \ + im_mpercent.obj \ + im_rank.obj \ + im_resize_linear.obj \ + im_sharpen.obj \ + im_shrink.obj \ + im_spcor.obj \ + im_stretch3.obj \ + im_zerox.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/freq_filt/makefile.msc b/win32/proj/libsrc/freq_filt/makefile.msc new file mode 100644 index 00000000..3bb32e43 --- /dev/null +++ b/win32/proj/libsrc/freq_filt/makefile.msc @@ -0,0 +1,51 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = freq_filt +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + fft_sp.obj \ + fmask4th.obj \ + fmaskcir.obj \ + freq_dispatch.obj \ + im_disp_ps.obj \ + im_fractsurf.obj \ + im_freq_mask.obj \ + im_freqflt.obj \ + im_fwfft.obj \ + im_invfft.obj \ + im_rotquad.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/histrograms_lut/makefile.msc b/win32/proj/libsrc/histrograms_lut/makefile.msc new file mode 100644 index 00000000..fae4dd43 --- /dev/null +++ b/win32/proj/libsrc/histrograms_lut/makefile.msc @@ -0,0 +1,55 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = histograms_lut +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + hist_dispatch.obj \ + im_gammacorrect.obj \ + im_heq.obj \ + im_hist.obj \ + im_histeq.obj \ + im_histgr.obj \ + im_histplot.obj \ + im_histspec.obj \ + im_hsp.obj \ + im_identity.obj \ + im_invertlut.obj \ + im_lhisteq.obj \ + im_maplut.obj \ + im_stdif.obj \ + tone.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/inplace/makefile.msc b/win32/proj/libsrc/inplace/makefile.msc new file mode 100644 index 00000000..71bc9e7a --- /dev/null +++ b/win32/proj/libsrc/inplace/makefile.msc @@ -0,0 +1,50 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = inplace +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_circle.obj \ + im_flood.obj \ + im_insertplace.obj \ + im_line.obj \ + im_paintrect.obj \ + im_plotmask.obj \ + inplace_dispatch.obj \ + line_draw.obj \ + plot_point.obj \ + smudge_area.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/iofuncs/makefile.msc b/win32/proj/libsrc/iofuncs/makefile.msc new file mode 100644 index 00000000..9d9f8062 --- /dev/null +++ b/win32/proj/libsrc/iofuncs/makefile.msc @@ -0,0 +1,97 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = iofuncs +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include \ + -DHAVE_DIRENT_H $(DIRENT_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(TIFF_CFLAGS) \ + $(PNG_CFLAGS) + +OBJECTS = \ + callback.obj \ + debug.obj \ + dispatch_types.obj \ + error.obj \ + error_exit.obj \ + im_append_Hist.obj \ + im_binfile.obj \ + im_close.obj \ + im_cp_Hist.obj \ + im_cp_desc.obj \ + im_crwrhd.obj \ + im_debugim.obj \ + im_demand_hint.obj \ + im_desc_hd.obj \ + im_generate.obj \ + im_header.obj \ + im_histlin.obj \ + im_image.obj \ + im_init.obj \ + im_initdesc.obj \ + im_inithd.obj \ + im_iocheck.obj \ + im_iterate.obj \ + im_makerw.obj \ + im_mapfile.obj \ + im_openin.obj \ + im_open.obj \ + im_openout.obj \ + im_partial.obj \ + im_piocheck.obj \ + im_prepare.obj \ + im_printdesc.obj \ + im_printhd.obj \ + im_printlines.obj \ + im_readhist.obj \ + im_setbox.obj \ + im_setbuf.obj \ + im_setupout.obj \ + im_unmapfile.obj \ + im_updatehist.obj \ + im_guess_prefix.obj \ + im_wrapmany.obj \ + im_wrapone.obj \ + im_writeline.obj \ + list.obj \ + memory.obj \ + package.obj \ + predicate.obj \ + region.obj \ + rect.obj \ + thread.obj \ + threadgroup.obj \ + time.obj \ + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/makefile.msc b/win32/proj/libsrc/makefile.msc new file mode 100644 index 00000000..cdae8594 --- /dev/null +++ b/win32/proj/libsrc/makefile.msc @@ -0,0 +1,78 @@ +# autogenerated from automake.am with automake.py +TOP = ..\.. +PRJ_TOP = .. +PACKAGE = vips +PKG_VER = 7.8.7 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +!IFDEF DEBUG +LDFLAGS = $(LDFLAGS) /NODEFAULTLIB:msvcrt +!ENDIF + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) +SUBDIRS = acquire arithmetic boolean colour conversion convolution freq_filt histograms_lut inplace iofuncs matrix morphology mosaicing other relational video + +sub-all: + for %d in ($(SUBDIRS)) do nmake -nologo -f makefile.msc sub-one THIS=%d + +sub-one: + cd $(THIS) + nmake -nologo -f makefile.msc + cd .. + +PKG_CFLAGS = \ + $(GLIB_CFLAGS) + +PKG_LINK = \ + $(TIFF_LIBS) $(JPEG_LIBS) $(ZLIB_LIBS) $(PNG_LIBS) \ + $(GLIB_LIBS) + +OBJECTS = \ + dummy.obj \ + $(libvips_LIBS) + +libvips_LIBS = \ + acquire/acquire.lib \ + arithmetic/arithmetic.lib \ + boolean/boolean.lib \ + colour/colour.lib \ + conversion/conversion.lib \ + convolution/convolution.lib \ + freq_filt/freq_filt.lib \ + histograms_lut/histograms_lut.lib \ + inplace/inplace.lib \ + iofuncs/iofuncs.lib \ + matrix/matrix.lib \ + morphology/morphology.lib \ + mosaicing/mosaicing.lib \ + other/other.lib \ + relational/relational.lib \ + video/video.lib + +all : \ + $(PRJ_TOP)\config.h \ + sub-all \ + $(PACKAGE)-$(PKG_VER).dll + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib \ + $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/matrix/makefile.msc b/win32/proj/libsrc/matrix/makefile.msc new file mode 100644 index 00000000..9c95e05f --- /dev/null +++ b/win32/proj/libsrc/matrix/makefile.msc @@ -0,0 +1,47 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = matrix +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_invmat.obj \ + im_matcat.obj \ + im_matinv.obj \ + im_matmul.obj \ + im_mattrn.obj \ + matalloc.obj \ + matrix_dispatch.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/morphology/makefile.msc b/win32/proj/libsrc/morphology/makefile.msc new file mode 100644 index 00000000..3862175d --- /dev/null +++ b/win32/proj/libsrc/morphology/makefile.msc @@ -0,0 +1,45 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = morphology +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_cntlines.obj \ + im_dilate.obj \ + im_erode.obj \ + morph_dispatch.obj \ + im_profile.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/mosaicing/makefile.msc b/win32/proj/libsrc/mosaicing/makefile.msc new file mode 100644 index 00000000..773d39ee --- /dev/null +++ b/win32/proj/libsrc/mosaicing/makefile.msc @@ -0,0 +1,58 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = mosaicing +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_affine.obj \ + match.obj \ + mosaic1.obj \ + mosaicing_dispatch.obj \ + similarity.obj \ + global_balance.obj \ + im_avgdxdy.obj \ + im_chkpair.obj \ + im_clinear.obj \ + im_improve.obj \ + im_initialize.obj \ + im_lrcalcon.obj \ + im_lrmerge.obj \ + im_lrmosaic.obj \ + im_tbcalcon.obj \ + im_tbmerge.obj \ + im_remosaic.obj \ + im_tbmosaic.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/other/makefile.msc b/win32/proj/libsrc/other/makefile.msc new file mode 100644 index 00000000..fe16671c --- /dev/null +++ b/win32/proj/libsrc/other/makefile.msc @@ -0,0 +1,51 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = other +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + cooc_funcs.obj \ + glds_funcs.obj \ + im_dif_std.obj \ + im_eye.obj \ + im_grey.obj \ + im_meanstd.obj \ + im_simcontr.obj \ + im_sines.obj \ + im_spatres.obj \ + im_zone.obj \ + other_dispatch.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/relational/makefile.msc b/win32/proj/libsrc/relational/makefile.msc new file mode 100644 index 00000000..8c8face7 --- /dev/null +++ b/win32/proj/libsrc/relational/makefile.msc @@ -0,0 +1,44 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = relational +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + im_ifthenelse.obj \ + im_blend.obj \ + relational.obj \ + relational_dispatch.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/libsrc/video/makefile.msc b/win32/proj/libsrc/video/makefile.msc new file mode 100644 index 00000000..830e787e --- /dev/null +++ b/win32/proj/libsrc/video/makefile.msc @@ -0,0 +1,42 @@ +# autogenerated from automake.am with automake.py +TOP = ..\..\.. +PRJ_TOP = ..\.. +PACKAGE = video +PKG_VER = 7.8 +!INCLUDE $(TOP)\glib\build\win32\make.msc + +top_srcdir = $(PRJ_TOP) +top_builddir = $(PRJ_TOP) +includedir = $(PRJ_TOP) +LT_RELEASE = $(PKG_VER) + +OBJECTS = \ + video_dispatch.obj \ + im_video_v4l1.obj \ + +INCLUDES = \ + -DHAVE_CONFIG_H \ + -I../.. -I$(PRJ_TOP)/include + +all : \ + $(PRJ_TOP)\config.h \ + $(PACKAGE).lib + + +$(PRJ_TOP)\config.h: $(PRJ_TOP)\config.h.win32 + copy $(PRJ_TOP)\config.h.win32 $(PRJ_TOP)\config.h + +RESOURCE = $(PACKAGE).res + +$(PACKAGE).lib : $(OBJECTS) + lib /out:$(PACKAGE).lib $(OBJECTS) + +$(PACKAGE)-$(PKG_VER).dll : $(OBJECTS) $(PACKAGE).def + $(CC) $(CFLAGS) -LD -Fe$(PACKAGE)-$(PKG_VER).dll $(OBJECTS) $(PKG_LINK) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:$(PACKAGE).def + +$(PACKAGE).exe : $(OBJECTS) $(PACKAGE).def $(PACKAGE).res + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(PACKAGE).res $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib $(LDFLAGS) /def:$(PACKAGE).def + +.c.obj : + $(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $< diff --git a/win32/proj/src/iofuncs/makefile.msc b/win32/proj/src/iofuncs/makefile.msc new file mode 100644 index 00000000..71c5eb7b --- /dev/null +++ b/win32/proj/src/iofuncs/makefile.msc @@ -0,0 +1,42 @@ +TOP = ..\..\.. +PRJ_TOP = ..\.. +PKG_VER = 7.8.7 + +!IFNDEF APP + +APPS = header binfile debugim edvips printlines vips + +sub-all: + for %d in ($(APPS)) do nmake -nologo -f makefile.msc sub-one THIS=%d + +sub-one: + nmake -nologo -f makefile.msc $(THIS).exe APP=$(THIS) OBJ_$(THIS)=1 + + +all : \ + sub-all + +!ELSE +!INCLUDE $(TOP)\glib\build\win32\make.msc + +PACKAGE = $(APP) + +!IFNDEF OBJECTS +OBJECTS = $(APP).obj +!ENDIF + +PKG_CFLAGS = -FImsvc_recommended_pragmas.h \ + $(GLIB_CFLAGS) \ + -I $(PRJ_TOP)\include + +PKG_LINK = $(GLIB_LIBS) \ + $(PRJ_TOP)\libsrc\vips-$(PKG_VER).lib + +$(PACKAGE).exe : $(OBJECTS) + $(CC) $(CFLAGS) -Fe$(PACKAGE).exe $(OBJECTS) $(PKG_LINK) \ + user32.lib advapi32.lib shell32.lib wsock32.lib winspool.lib \ + $(LDFLAGS) + +CFLAGS = -I. -I$(PRJ_TOP) $(PKG_CFLAGS) -DHAVE_CONFIG_H + +!ENDIF \ No newline at end of file diff --git a/win32/tmake/README b/win32/tmake/README new file mode 100644 index 00000000..ac704046 --- /dev/null +++ b/win32/tmake/README @@ -0,0 +1,16 @@ +Build system based on trolltech's tmake. Makes a vips.dll on win32. + +Copy the .pro and Makefiles in this area into the main VIPS libsrc tree, +then either use the makefiles already there, (you'll need to edit them), +or install tmake and regenerate them from the .pro files. + + http://www.trolltech.com/download/tmake.html + + tmake todo.pro -o makefile_todo + nmake -f makefile_todo tmake_all + nmake -f makefile_todo all + tmake vips.pro -o Makefile + nmake -f Makefile all + +You might need to edit the .pro files a little as well. + diff --git a/win32/tmake/config.h b/win32/tmake/config.h new file mode 100644 index 00000000..462f91bb --- /dev/null +++ b/win32/tmake/config.h @@ -0,0 +1,243 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* define to open non-text files in binary mode */ +#define BINARY_OPEN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have fftw libraries and header files. */ +//#define HAVE_FFTW 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define to 1 if you have the `getwd' function. */ +/* #undef HAVE_GETWD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_IO_H 1 + +/* Define if you have jpeg libraries and header files. */ +#define HAVE_JPEG 1 + +/* Define if you have lcms libraries and header files. */ +//#define HAVE_LCMS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* define if you have the libMagick libraries and header files. */ +/* #undef HAVE_MAGICK */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mkstemp' function. */ +/* #undef HAVE_MKSTEMP */ + +/* Define to 1 if you have the `mktemp' function. */ +#define HAVE_MKTEMP 1 + +/* Define to 1 if you have a working `mmap' system call. */ +/* #undef HAVE_MMAP */ + +/* Define to 1 if you have the `munmap' function. */ +/* #undef HAVE_MUNMAP */ + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have png libraries and header files. */ +#define HAVE_PNG 1 + +/* Define if you have POSIX threads libraries and header files. */ +//#define HAVE_PTHREAD 1 + +/* have pthread_setconcurrency() */ +//#define HAVE_PTHREAD_SETCONCURRENCY 1 + +/* Define to 1 if you have the `putenv' function. */ +#define HAVE_PUTENV 1 + +/* Define to 1 if you have the `rand' function. */ +#define HAVE_RAND 1 + +/* Define to 1 if you have the `random' function. */ +/* #undef HAVE_RANDOM */ + +/* Define to 1 if you have the `realpath' function. */ +/* #undef HAVE_REALPATH */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strrchr' function. */ +#define HAVE_STRRCHR 1 + +/* Define to 1 if you have the `strspn' function. */ +#define HAVE_STRSPN 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have tiff libraries and header files. */ +#define HAVE_TIFF 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_UNISTD_H 1 + +/* have video4linux 1 */ +/* #undef HAVE_VIDEODEV */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 +#define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) + + + +/* Define to 1 if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have libz libraries and header files. */ +#define HAVE_ZIP 1 + +/* Name of package */ +#define PACKAGE "vips" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "7.8.7" + +/* Define if using the dmalloc debugging malloc package */ +/* #undef WITH_DMALLOC */ + +/* Define to 1 if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +#define mode_t int + +/* Define to `long' if does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +#define PATH_MAX 512 +#define R_OK 4 diff --git a/win32/tmake/libsrc/Makefile b/win32/tmake/libsrc/Makefile new file mode 100644 index 00000000..8b9b6870 --- /dev/null +++ b/win32/tmake/libsrc/Makefile @@ -0,0 +1,99 @@ +############################################################################# +# Makefile for building vips +# Generated by tmake at 16:03, 2003/06/11 +# Project: vips +# Template: app +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DNO_DEBUG +DESTDIR= ../Release +INCPATH = -I".." -I"..\include" -I"$(QTDIR)\include" +LINK = link +LFLAGS = /NOLOGO /SUBSYSTEM:windows /DLL /DEF:vips.def +LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib imm32.lib winmm.lib wsock32.lib ..\lib\libjpeg.lib ..\lib\libtiff.lib ..\lib\libpng.lib ..\lib\libz.lib +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\config.h +SOURCES = dummy.c +OBJECTS = dummy.obj \ + $(DESTDIR)/acquire.lib \ + $(DESTDIR)/arithmetic.lib \ + $(DESTDIR)/boolean.lib \ + $(DESTDIR)/colour.lib \ + $(DESTDIR)/conversion.lib \ + $(DESTDIR)/convolution.lib \ + $(DESTDIR)/freq_filt.lib \ + $(DESTDIR)/histograms_lut.lib \ + $(DESTDIR)/inplace.lib \ + $(DESTDIR)/iofuncs.lib \ + $(DESTDIR)/matrix.lib \ + $(DESTDIR)/morphology.lib \ + $(DESTDIR)/mosaicing.lib \ + $(DESTDIR)/other.lib \ + $(DESTDIR)/relational.lib \ + $(DESTDIR)/video.lib \ + +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\Release\vips.dll +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LINK) $(LFLAGS) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) $(LIBS) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: vips.pro + tmake vips.pro -o Makefile + +dist: + $(ZIP) vips.zip vips.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del dummy.obj + -del $(TARGET) + -del vips.lib + -del vips.exp + +####### Compile + +dummy.obj: dummy.c + diff --git a/win32/tmake/libsrc/acquire/Makefile b/win32/tmake/libsrc/acquire/Makefile new file mode 100644 index 00000000..5dcc7f28 --- /dev/null +++ b/win32/tmake/libsrc/acquire/Makefile @@ -0,0 +1,77 @@ +############################################################################# +# Makefile for building acquire +# Generated by tmake at 16:03, 2003/06/11 +# Project: acquire +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_clamp.c +OBJECTS = im_clamp.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\acquire.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: acquire.pro + tmake acquire.pro -o Makefile + +dist: + $(ZIP) acquire.zip acquire.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_clamp.obj + -del $(TARGET) + +####### Compile + +im_clamp.obj: im_clamp.c + diff --git a/win32/tmake/libsrc/acquire/acquire.pro b/win32/tmake/libsrc/acquire/acquire.pro new file mode 100644 index 00000000..76304799 --- /dev/null +++ b/win32/tmake/libsrc/acquire/acquire.pro @@ -0,0 +1,18 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +TARGET = acquire +SOURCES = im_clamp.c diff --git a/win32/tmake/libsrc/arithmetic/Makefile b/win32/tmake/libsrc/arithmetic/Makefile new file mode 100644 index 00000000..2aaaffdb --- /dev/null +++ b/win32/tmake/libsrc/arithmetic/Makefile @@ -0,0 +1,237 @@ +############################################################################# +# Makefile for building arithmetic +# Generated by tmake at 16:03, 2003/06/11 +# Project: arithmetic +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = arith_dispatch.c \ + im_abs.c \ + im_add.c \ + im_avg.c \ + im_cmulnorm.c \ + im_costra.c \ + im_deviate.c \ + im_divide.c \ + im_ceil.c \ + im_floor.c \ + im_expntra.c \ + im_fav4.c \ + im_gadd.c \ + im_gaddim.c \ + im_gfadd.c \ + im_invert.c \ + im_lintra.c \ + im_litecor.c \ + im_log10tra.c \ + im_logtra.c \ + im_max.c \ + im_maxpos.c \ + im_measure.c \ + im_min.c \ + im_minpos.c \ + im_multiply.c \ + im_powtra.c \ + im_remainder.c \ + im_sign.c \ + im_sintra.c \ + im_stats.c \ + im_subtract.c \ + im_tantra.c +OBJECTS = arith_dispatch.obj \ + im_abs.obj \ + im_add.obj \ + im_avg.obj \ + im_cmulnorm.obj \ + im_costra.obj \ + im_deviate.obj \ + im_divide.obj \ + im_ceil.obj \ + im_floor.obj \ + im_expntra.obj \ + im_fav4.obj \ + im_gadd.obj \ + im_gaddim.obj \ + im_gfadd.obj \ + im_invert.obj \ + im_lintra.obj \ + im_litecor.obj \ + im_log10tra.obj \ + im_logtra.obj \ + im_max.obj \ + im_maxpos.obj \ + im_measure.obj \ + im_min.obj \ + im_minpos.obj \ + im_multiply.obj \ + im_powtra.obj \ + im_remainder.obj \ + im_sign.obj \ + im_sintra.obj \ + im_stats.obj \ + im_subtract.obj \ + im_tantra.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\arithmetic.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: arithmetic.pro + tmake arithmetic.pro -o Makefile + +dist: + $(ZIP) arithmetic.zip arithmetic.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del arith_dispatch.obj + -del im_abs.obj + -del im_add.obj + -del im_avg.obj + -del im_cmulnorm.obj + -del im_costra.obj + -del im_deviate.obj + -del im_divide.obj + -del im_ceil.obj + -del im_floor.obj + -del im_expntra.obj + -del im_fav4.obj + -del im_gadd.obj + -del im_gaddim.obj + -del im_gfadd.obj + -del im_invert.obj + -del im_lintra.obj + -del im_litecor.obj + -del im_log10tra.obj + -del im_logtra.obj + -del im_max.obj + -del im_maxpos.obj + -del im_measure.obj + -del im_min.obj + -del im_minpos.obj + -del im_multiply.obj + -del im_powtra.obj + -del im_remainder.obj + -del im_sign.obj + -del im_sintra.obj + -del im_stats.obj + -del im_subtract.obj + -del im_tantra.obj + -del $(TARGET) + +####### Compile + +arith_dispatch.obj: arith_dispatch.c + +im_abs.obj: im_abs.c + +im_add.obj: im_add.c + +im_avg.obj: im_avg.c + +im_cmulnorm.obj: im_cmulnorm.c + +im_costra.obj: im_costra.c + +im_deviate.obj: im_deviate.c + +im_divide.obj: im_divide.c + +im_ceil.obj: im_ceil.c + +im_floor.obj: im_floor.c + +im_expntra.obj: im_expntra.c + +im_fav4.obj: im_fav4.c + +im_gadd.obj: im_gadd.c + +im_gaddim.obj: im_gaddim.c + +im_gfadd.obj: im_gfadd.c + +im_invert.obj: im_invert.c + +im_lintra.obj: im_lintra.c + +im_litecor.obj: im_litecor.c + +im_log10tra.obj: im_log10tra.c + +im_logtra.obj: im_logtra.c + +im_max.obj: im_max.c + +im_maxpos.obj: im_maxpos.c + +im_measure.obj: im_measure.c + +im_min.obj: im_min.c + +im_minpos.obj: im_minpos.c + +im_multiply.obj: im_multiply.c + +im_powtra.obj: im_powtra.c + +im_remainder.obj: im_remainder.c + +im_sign.obj: im_sign.c + +im_sintra.obj: im_sintra.c + +im_stats.obj: im_stats.c + +im_subtract.obj: im_subtract.c + +im_tantra.obj: im_tantra.c + diff --git a/win32/tmake/libsrc/arithmetic/arithmetic.pro b/win32/tmake/libsrc/arithmetic/arithmetic.pro new file mode 100644 index 00000000..c3052089 --- /dev/null +++ b/win32/tmake/libsrc/arithmetic/arithmetic.pro @@ -0,0 +1,51 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + arith_dispatch.c \ + im_abs.c \ + im_add.c \ + im_avg.c \ + im_cmulnorm.c \ + im_costra.c \ + im_deviate.c \ + im_divide.c \ + im_ceil.c \ + im_floor.c \ + im_expntra.c \ + im_fav4.c \ + im_gadd.c \ + im_gaddim.c \ + im_gfadd.c \ + im_invert.c \ + im_lintra.c \ + im_litecor.c \ + im_log10tra.c \ + im_logtra.c \ + im_max.c \ + im_maxpos.c \ + im_measure.c \ + im_min.c \ + im_minpos.c \ + im_multiply.c \ + im_powtra.c \ + im_remainder.c \ + im_sign.c \ + im_sintra.c \ + im_stats.c \ + im_subtract.c \ + im_tantra.c +TARGET = arithmetic diff --git a/win32/tmake/libsrc/boolean/Makefile b/win32/tmake/libsrc/boolean/Makefile new file mode 100644 index 00000000..c7b5f5c2 --- /dev/null +++ b/win32/tmake/libsrc/boolean/Makefile @@ -0,0 +1,82 @@ +############################################################################# +# Makefile for building boolean +# Generated by tmake at 16:03, 2003/06/11 +# Project: boolean +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = bool_dispatch.c \ + boolean.c +OBJECTS = bool_dispatch.obj \ + boolean.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\boolean.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: boolean.pro + tmake boolean.pro -o Makefile + +dist: + $(ZIP) boolean.zip boolean.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del bool_dispatch.obj + -del boolean.obj + -del $(TARGET) + +####### Compile + +bool_dispatch.obj: bool_dispatch.c + +boolean.obj: boolean.c + diff --git a/win32/tmake/libsrc/boolean/boolean.pro b/win32/tmake/libsrc/boolean/boolean.pro new file mode 100644 index 00000000..ea9e4cdc --- /dev/null +++ b/win32/tmake/libsrc/boolean/boolean.pro @@ -0,0 +1,20 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + bool_dispatch.c \ + boolean.c +TARGET = boolean diff --git a/win32/tmake/libsrc/colour/Makefile b/win32/tmake/libsrc/colour/Makefile new file mode 100644 index 00000000..7dba2966 --- /dev/null +++ b/win32/tmake/libsrc/colour/Makefile @@ -0,0 +1,197 @@ +############################################################################# +# Makefile for building colour +# Generated by tmake at 16:03, 2003/06/11 +# Project: colour +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = colour.c \ + colour_dispatch.c \ + derived.c \ + im_icc_transform.c \ + im_LCh2Lab.c \ + im_LCh2UCS.c \ + im_Lab2LCh.c \ + im_Lab2LabQ.c \ + im_Lab2LabS.c \ + im_Lab2XYZ.c \ + im_LabQ2Lab.c \ + im_LabQ2LabS.c \ + im_LabQ2disp.c \ + im_LabS2LabQ.c \ + im_LabS2Lab.c \ + im_lab_morph.c \ + im_UCS2LCh.c \ + im_XYZ2Lab.c \ + im_XYZ2Yxy.c \ + im_Yxy2XYZ.c \ + im_XYZ2disp.c \ + im_dE00_fromLab.c \ + im_dECMC_fromLab.c \ + im_dE_fromLab.c \ + im_disp2XYZ.c +OBJECTS = colour.obj \ + colour_dispatch.obj \ + derived.obj \ + im_icc_transform.obj \ + im_LCh2Lab.obj \ + im_LCh2UCS.obj \ + im_Lab2LCh.obj \ + im_Lab2LabQ.obj \ + im_Lab2LabS.obj \ + im_Lab2XYZ.obj \ + im_LabQ2Lab.obj \ + im_LabQ2LabS.obj \ + im_LabQ2disp.obj \ + im_LabS2LabQ.obj \ + im_LabS2Lab.obj \ + im_lab_morph.obj \ + im_UCS2LCh.obj \ + im_XYZ2Lab.obj \ + im_XYZ2Yxy.obj \ + im_Yxy2XYZ.obj \ + im_XYZ2disp.obj \ + im_dE00_fromLab.obj \ + im_dECMC_fromLab.obj \ + im_dE_fromLab.obj \ + im_disp2XYZ.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\colour.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: colour.pro + tmake colour.pro -o Makefile + +dist: + $(ZIP) colour.zip colour.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del colour.obj + -del colour_dispatch.obj + -del derived.obj + -del im_icc_transform.obj + -del im_LCh2Lab.obj + -del im_LCh2UCS.obj + -del im_Lab2LCh.obj + -del im_Lab2LabQ.obj + -del im_Lab2LabS.obj + -del im_Lab2XYZ.obj + -del im_LabQ2Lab.obj + -del im_LabQ2LabS.obj + -del im_LabQ2disp.obj + -del im_LabS2LabQ.obj + -del im_LabS2Lab.obj + -del im_lab_morph.obj + -del im_UCS2LCh.obj + -del im_XYZ2Lab.obj + -del im_XYZ2Yxy.obj + -del im_Yxy2XYZ.obj + -del im_XYZ2disp.obj + -del im_dE00_fromLab.obj + -del im_dECMC_fromLab.obj + -del im_dE_fromLab.obj + -del im_disp2XYZ.obj + -del $(TARGET) + +####### Compile + +colour.obj: colour.c + +colour_dispatch.obj: colour_dispatch.c + +derived.obj: derived.c + +im_icc_transform.obj: im_icc_transform.c + +im_LCh2Lab.obj: im_LCh2Lab.c + +im_LCh2UCS.obj: im_LCh2UCS.c + +im_Lab2LCh.obj: im_Lab2LCh.c + +im_Lab2LabQ.obj: im_Lab2LabQ.c + +im_Lab2LabS.obj: im_Lab2LabS.c + +im_Lab2XYZ.obj: im_Lab2XYZ.c + +im_LabQ2Lab.obj: im_LabQ2Lab.c + +im_LabQ2LabS.obj: im_LabQ2LabS.c + +im_LabQ2disp.obj: im_LabQ2disp.c + +im_LabS2LabQ.obj: im_LabS2LabQ.c + +im_LabS2Lab.obj: im_LabS2Lab.c + +im_lab_morph.obj: im_lab_morph.c + +im_UCS2LCh.obj: im_UCS2LCh.c + +im_XYZ2Lab.obj: im_XYZ2Lab.c + +im_XYZ2Yxy.obj: im_XYZ2Yxy.c + +im_Yxy2XYZ.obj: im_Yxy2XYZ.c + +im_XYZ2disp.obj: im_XYZ2disp.c + +im_dE00_fromLab.obj: im_dE00_fromLab.c + +im_dECMC_fromLab.obj: im_dECMC_fromLab.c + +im_dE_fromLab.obj: im_dE_fromLab.c + +im_disp2XYZ.obj: im_disp2XYZ.c + diff --git a/win32/tmake/libsrc/colour/colour.pro b/win32/tmake/libsrc/colour/colour.pro new file mode 100644 index 00000000..6f590679 --- /dev/null +++ b/win32/tmake/libsrc/colour/colour.pro @@ -0,0 +1,43 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + colour.c \ + colour_dispatch.c \ + derived.c \ + im_icc_transform.c \ + im_LCh2Lab.c \ + im_LCh2UCS.c \ + im_Lab2LCh.c \ + im_Lab2LabQ.c \ + im_Lab2LabS.c \ + im_Lab2XYZ.c \ + im_LabQ2Lab.c \ + im_LabQ2LabS.c \ + im_LabQ2disp.c \ + im_LabS2LabQ.c \ + im_LabS2Lab.c \ + im_lab_morph.c \ + im_UCS2LCh.c \ + im_XYZ2Lab.c \ + im_XYZ2Yxy.c \ + im_Yxy2XYZ.c \ + im_XYZ2disp.c \ + im_dE00_fromLab.c \ + im_dECMC_fromLab.c \ + im_dE_fromLab.c \ + im_disp2XYZ.c +TARGET = colour diff --git a/win32/tmake/libsrc/conversion/Makefile b/win32/tmake/libsrc/conversion/Makefile new file mode 100644 index 00000000..dc886880 --- /dev/null +++ b/win32/tmake/libsrc/conversion/Makefile @@ -0,0 +1,277 @@ +############################################################################# +# Makefile for building conversion +# Generated by tmake at 16:03, 2003/06/11 +# Project: conversion +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_bernd.c \ + im_vips2tiff.c \ + im_tiff2vips.c \ + conver_dispatch.c \ + im_bandjoin.c \ + im_black.c \ + im_c2amph.c \ + im_c2rect.c \ + im_c2imag.c \ + im_c2ps.c \ + im_c2real.c \ + im_clip.c \ + im_copy.c \ + im_extract.c \ + im_falsecolour.c \ + im_fliphor.c \ + im_flipver.c \ + im_gbandjoin.c \ + im_insert.c \ + im_lrjoin.c \ + im_magick2vips.c \ + im_mask2vips.c \ + im_ppm2vips.c \ + im_recomb.c \ + im_ri2c.c \ + im_rot180.c \ + im_rot270.c \ + im_rot90.c \ + im_scale.c \ + im_scaleps.c \ + im_slice.c \ + im_subsample.c \ + im_system.c \ + im_print.c \ + im_tbjoin.c \ + im_thresh.c \ + im_vips2mask.c \ + im_vips2ppm.c \ + vips_jpeg.c \ + vips_png.c \ + im_zoom.c +OBJECTS = im_bernd.obj \ + im_vips2tiff.obj \ + im_tiff2vips.obj \ + conver_dispatch.obj \ + im_bandjoin.obj \ + im_black.obj \ + im_c2amph.obj \ + im_c2rect.obj \ + im_c2imag.obj \ + im_c2ps.obj \ + im_c2real.obj \ + im_clip.obj \ + im_copy.obj \ + im_extract.obj \ + im_falsecolour.obj \ + im_fliphor.obj \ + im_flipver.obj \ + im_gbandjoin.obj \ + im_insert.obj \ + im_lrjoin.obj \ + im_magick2vips.obj \ + im_mask2vips.obj \ + im_ppm2vips.obj \ + im_recomb.obj \ + im_ri2c.obj \ + im_rot180.obj \ + im_rot270.obj \ + im_rot90.obj \ + im_scale.obj \ + im_scaleps.obj \ + im_slice.obj \ + im_subsample.obj \ + im_system.obj \ + im_print.obj \ + im_tbjoin.obj \ + im_thresh.obj \ + im_vips2mask.obj \ + im_vips2ppm.obj \ + vips_jpeg.obj \ + vips_png.obj \ + im_zoom.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\conversion.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: conversion.pro + tmake conversion.pro -o Makefile + +dist: + $(ZIP) conversion.zip conversion.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_bernd.obj + -del im_vips2tiff.obj + -del im_tiff2vips.obj + -del conver_dispatch.obj + -del im_bandjoin.obj + -del im_black.obj + -del im_c2amph.obj + -del im_c2rect.obj + -del im_c2imag.obj + -del im_c2ps.obj + -del im_c2real.obj + -del im_clip.obj + -del im_copy.obj + -del im_extract.obj + -del im_falsecolour.obj + -del im_fliphor.obj + -del im_flipver.obj + -del im_gbandjoin.obj + -del im_insert.obj + -del im_lrjoin.obj + -del im_magick2vips.obj + -del im_mask2vips.obj + -del im_ppm2vips.obj + -del im_recomb.obj + -del im_ri2c.obj + -del im_rot180.obj + -del im_rot270.obj + -del im_rot90.obj + -del im_scale.obj + -del im_scaleps.obj + -del im_slice.obj + -del im_subsample.obj + -del im_system.obj + -del im_print.obj + -del im_tbjoin.obj + -del im_thresh.obj + -del im_vips2mask.obj + -del im_vips2ppm.obj + -del vips_jpeg.obj + -del vips_png.obj + -del im_zoom.obj + -del $(TARGET) + +####### Compile + +im_bernd.obj: im_bernd.c + +im_vips2tiff.obj: im_vips2tiff.c + +im_tiff2vips.obj: im_tiff2vips.c + +conver_dispatch.obj: conver_dispatch.c + +im_bandjoin.obj: im_bandjoin.c + +im_black.obj: im_black.c + +im_c2amph.obj: im_c2amph.c + +im_c2rect.obj: im_c2rect.c + +im_c2imag.obj: im_c2imag.c + +im_c2ps.obj: im_c2ps.c + +im_c2real.obj: im_c2real.c + +im_clip.obj: im_clip.c + +im_copy.obj: im_copy.c + +im_extract.obj: im_extract.c + +im_falsecolour.obj: im_falsecolour.c + +im_fliphor.obj: im_fliphor.c + +im_flipver.obj: im_flipver.c + +im_gbandjoin.obj: im_gbandjoin.c + +im_insert.obj: im_insert.c + +im_lrjoin.obj: im_lrjoin.c + +im_magick2vips.obj: im_magick2vips.c + +im_mask2vips.obj: im_mask2vips.c + +im_ppm2vips.obj: im_ppm2vips.c + +im_recomb.obj: im_recomb.c + +im_ri2c.obj: im_ri2c.c + +im_rot180.obj: im_rot180.c + +im_rot270.obj: im_rot270.c + +im_rot90.obj: im_rot90.c + +im_scale.obj: im_scale.c + +im_scaleps.obj: im_scaleps.c + +im_slice.obj: im_slice.c + +im_subsample.obj: im_subsample.c + +im_system.obj: im_system.c + +im_print.obj: im_print.c + +im_tbjoin.obj: im_tbjoin.c + +im_thresh.obj: im_thresh.c + +im_vips2mask.obj: im_vips2mask.c + +im_vips2ppm.obj: im_vips2ppm.c + +vips_jpeg.obj: vips_jpeg.c + +vips_png.obj: vips_png.c + +im_zoom.obj: im_zoom.c + diff --git a/win32/tmake/libsrc/conversion/conversion.pro b/win32/tmake/libsrc/conversion/conversion.pro new file mode 100644 index 00000000..6bebb88d --- /dev/null +++ b/win32/tmake/libsrc/conversion/conversion.pro @@ -0,0 +1,60 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_bernd.c \ + im_vips2tiff.c \ + im_tiff2vips.c \ + conver_dispatch.c \ + im_bandjoin.c \ + im_black.c \ + im_c2amph.c \ + im_c2rect.c \ + im_c2imag.c \ + im_c2ps.c \ + im_c2real.c \ + im_clip.c \ + im_copy.c \ + im_extract.c \ + im_falsecolour.c \ + im_fliphor.c \ + im_flipver.c \ + im_gbandjoin.c \ + im_insert.c \ + im_lrjoin.c \ + im_magick2vips.c \ + im_mask2vips.c \ + im_ppm2vips.c \ + im_recomb.c \ + im_ri2c.c \ + im_rot180.c \ + im_rot270.c \ + im_rot90.c \ + im_scale.c \ + im_scaleps.c \ + im_slice.c \ + im_subsample.c \ + im_system.c \ + im_print.c \ + im_tbjoin.c \ + im_thresh.c \ + im_vips2mask.c \ + im_vips2ppm.c \ + vips_jpeg.c \ + vips_png.c \ + im_zoom.c +TARGET = conversion + diff --git a/win32/tmake/libsrc/convolution/Makefile b/win32/tmake/libsrc/convolution/Makefile new file mode 100644 index 00000000..2c5c76b0 --- /dev/null +++ b/win32/tmake/libsrc/convolution/Makefile @@ -0,0 +1,202 @@ +############################################################################# +# Makefile for building convolution +# Generated by tmake at 16:03, 2003/06/11 +# Project: convolution +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = rotmask.c \ + rw_mask.c \ + convol_dispatch.c \ + im_addgnoise.c \ + im_compass.c \ + im_conv.c \ + im_convf.c \ + im_convsep.c \ + im_convsepf.c \ + im_convsub.c \ + im_embed.c \ + im_fastcor.c \ + im_gaussmasks.c \ + im_gaussnoise.c \ + im_gradient.c \ + im_lindetect.c \ + im_logmasks.c \ + im_maxvalue.c \ + im_mpercent.c \ + im_rank.c \ + im_resize_linear.c \ + im_sharpen.c \ + im_shrink.c \ + im_spcor.c \ + im_stretch3.c \ + im_zerox.c +OBJECTS = rotmask.obj \ + rw_mask.obj \ + convol_dispatch.obj \ + im_addgnoise.obj \ + im_compass.obj \ + im_conv.obj \ + im_convf.obj \ + im_convsep.obj \ + im_convsepf.obj \ + im_convsub.obj \ + im_embed.obj \ + im_fastcor.obj \ + im_gaussmasks.obj \ + im_gaussnoise.obj \ + im_gradient.obj \ + im_lindetect.obj \ + im_logmasks.obj \ + im_maxvalue.obj \ + im_mpercent.obj \ + im_rank.obj \ + im_resize_linear.obj \ + im_sharpen.obj \ + im_shrink.obj \ + im_spcor.obj \ + im_stretch3.obj \ + im_zerox.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\convolution.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: convolution.pro + tmake convolution.pro -o Makefile + +dist: + $(ZIP) convolution.zip convolution.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del rotmask.obj + -del rw_mask.obj + -del convol_dispatch.obj + -del im_addgnoise.obj + -del im_compass.obj + -del im_conv.obj + -del im_convf.obj + -del im_convsep.obj + -del im_convsepf.obj + -del im_convsub.obj + -del im_embed.obj + -del im_fastcor.obj + -del im_gaussmasks.obj + -del im_gaussnoise.obj + -del im_gradient.obj + -del im_lindetect.obj + -del im_logmasks.obj + -del im_maxvalue.obj + -del im_mpercent.obj + -del im_rank.obj + -del im_resize_linear.obj + -del im_sharpen.obj + -del im_shrink.obj + -del im_spcor.obj + -del im_stretch3.obj + -del im_zerox.obj + -del $(TARGET) + +####### Compile + +rotmask.obj: rotmask.c + +rw_mask.obj: rw_mask.c + +convol_dispatch.obj: convol_dispatch.c + +im_addgnoise.obj: im_addgnoise.c + +im_compass.obj: im_compass.c + +im_conv.obj: im_conv.c + +im_convf.obj: im_convf.c + +im_convsep.obj: im_convsep.c + +im_convsepf.obj: im_convsepf.c + +im_convsub.obj: im_convsub.c + +im_embed.obj: im_embed.c + +im_fastcor.obj: im_fastcor.c + +im_gaussmasks.obj: im_gaussmasks.c + +im_gaussnoise.obj: im_gaussnoise.c + +im_gradient.obj: im_gradient.c + +im_lindetect.obj: im_lindetect.c + +im_logmasks.obj: im_logmasks.c + +im_maxvalue.obj: im_maxvalue.c + +im_mpercent.obj: im_mpercent.c + +im_rank.obj: im_rank.c + +im_resize_linear.obj: im_resize_linear.c + +im_sharpen.obj: im_sharpen.c + +im_shrink.obj: im_shrink.c + +im_spcor.obj: im_spcor.c + +im_stretch3.obj: im_stretch3.c + +im_zerox.obj: im_zerox.c + diff --git a/win32/tmake/libsrc/convolution/convolution.pro b/win32/tmake/libsrc/convolution/convolution.pro new file mode 100644 index 00000000..d93463f4 --- /dev/null +++ b/win32/tmake/libsrc/convolution/convolution.pro @@ -0,0 +1,45 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + rotmask.c \ + rw_mask.c \ + convol_dispatch.c \ + im_addgnoise.c \ + im_compass.c \ + im_conv.c \ + im_convf.c \ + im_convsep.c \ + im_convsepf.c \ + im_convsub.c \ + im_embed.c \ + im_fastcor.c \ + im_gaussmasks.c \ + im_gaussnoise.c \ + im_gradient.c \ + im_lindetect.c \ + im_logmasks.c \ + im_maxvalue.c \ + im_mpercent.c \ + im_rank.c \ + im_resize_linear.c \ + im_sharpen.c \ + im_shrink.c \ + im_spcor.c \ + im_stretch3.c \ + im_zerox.c +TARGET = convolution + diff --git a/win32/tmake/libsrc/freq_filt/Makefile b/win32/tmake/libsrc/freq_filt/Makefile new file mode 100644 index 00000000..f519153e --- /dev/null +++ b/win32/tmake/libsrc/freq_filt/Makefile @@ -0,0 +1,132 @@ +############################################################################# +# Makefile for building freq_filt +# Generated by tmake at 16:03, 2003/06/11 +# Project: freq_filt +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = fft_sp.c \ + fmask4th.c \ + fmaskcir.c \ + freq_dispatch.c \ + im_disp_ps.c \ + im_fractsurf.c \ + im_freq_mask.c \ + im_freqflt.c \ + im_fwfft.c \ + im_invfft.c \ + im_invfftr.c \ + im_rotquad.c +OBJECTS = fft_sp.obj \ + fmask4th.obj \ + fmaskcir.obj \ + freq_dispatch.obj \ + im_disp_ps.obj \ + im_fractsurf.obj \ + im_freq_mask.obj \ + im_freqflt.obj \ + im_fwfft.obj \ + im_invfft.obj \ + im_invfftr.obj \ + im_rotquad.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\freq_filt.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: freq_filt.pro + tmake freq_filt.pro -o Makefile + +dist: + $(ZIP) freq_filt.zip freq_filt.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del fft_sp.obj + -del fmask4th.obj + -del fmaskcir.obj + -del freq_dispatch.obj + -del im_disp_ps.obj + -del im_fractsurf.obj + -del im_freq_mask.obj + -del im_freqflt.obj + -del im_fwfft.obj + -del im_invfft.obj + -del im_invfftr.obj + -del im_rotquad.obj + -del $(TARGET) + +####### Compile + +fft_sp.obj: fft_sp.c + +fmask4th.obj: fmask4th.c + +fmaskcir.obj: fmaskcir.c + +freq_dispatch.obj: freq_dispatch.c + +im_disp_ps.obj: im_disp_ps.c + +im_fractsurf.obj: im_fractsurf.c + +im_freq_mask.obj: im_freq_mask.c + +im_freqflt.obj: im_freqflt.c + +im_fwfft.obj: im_fwfft.c + +im_invfft.obj: im_invfft.c + +im_invfftr.obj: im_invfftr.c + +im_rotquad.obj: im_rotquad.c + diff --git a/win32/tmake/libsrc/freq_filt/freq_filt.pro b/win32/tmake/libsrc/freq_filt/freq_filt.pro new file mode 100644 index 00000000..0b6fa30e --- /dev/null +++ b/win32/tmake/libsrc/freq_filt/freq_filt.pro @@ -0,0 +1,31 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + fft_sp.c \ + fmask4th.c \ + fmaskcir.c \ + freq_dispatch.c \ + im_disp_ps.c \ + im_fractsurf.c \ + im_freq_mask.c \ + im_freqflt.c \ + im_fwfft.c \ + im_invfft.c \ + im_invfftr.c \ + im_rotquad.c +TARGET = freq_filt + diff --git a/win32/tmake/libsrc/histograms_lut/Makefile b/win32/tmake/libsrc/histograms_lut/Makefile new file mode 100644 index 00000000..c72093d8 --- /dev/null +++ b/win32/tmake/libsrc/histograms_lut/Makefile @@ -0,0 +1,147 @@ +############################################################################# +# Makefile for building histograms_lut +# Generated by tmake at 16:03, 2003/06/11 +# Project: histograms_lut +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = hist_dispatch.c \ + im_gammacorrect.c \ + im_heq.c \ + im_hist.c \ + im_histeq.c \ + im_histgr.c \ + im_histplot.c \ + im_histspec.c \ + im_hsp.c \ + im_identity.c \ + im_invertlut.c \ + im_lhisteq.c \ + im_maplut.c \ + im_stdif.c \ + tone.c +OBJECTS = hist_dispatch.obj \ + im_gammacorrect.obj \ + im_heq.obj \ + im_hist.obj \ + im_histeq.obj \ + im_histgr.obj \ + im_histplot.obj \ + im_histspec.obj \ + im_hsp.obj \ + im_identity.obj \ + im_invertlut.obj \ + im_lhisteq.obj \ + im_maplut.obj \ + im_stdif.obj \ + tone.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\histograms_lut.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: histograms_lut.pro + tmake histograms_lut.pro -o Makefile + +dist: + $(ZIP) histograms_lut.zip histograms_lut.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del hist_dispatch.obj + -del im_gammacorrect.obj + -del im_heq.obj + -del im_hist.obj + -del im_histeq.obj + -del im_histgr.obj + -del im_histplot.obj + -del im_histspec.obj + -del im_hsp.obj + -del im_identity.obj + -del im_invertlut.obj + -del im_lhisteq.obj + -del im_maplut.obj + -del im_stdif.obj + -del tone.obj + -del $(TARGET) + +####### Compile + +hist_dispatch.obj: hist_dispatch.c + +im_gammacorrect.obj: im_gammacorrect.c + +im_heq.obj: im_heq.c + +im_hist.obj: im_hist.c + +im_histeq.obj: im_histeq.c + +im_histgr.obj: im_histgr.c + +im_histplot.obj: im_histplot.c + +im_histspec.obj: im_histspec.c + +im_hsp.obj: im_hsp.c + +im_identity.obj: im_identity.c + +im_invertlut.obj: im_invertlut.c + +im_lhisteq.obj: im_lhisteq.c + +im_maplut.obj: im_maplut.c + +im_stdif.obj: im_stdif.c + +tone.obj: tone.c + diff --git a/win32/tmake/libsrc/histograms_lut/histograms_lut.pro b/win32/tmake/libsrc/histograms_lut/histograms_lut.pro new file mode 100644 index 00000000..98355189 --- /dev/null +++ b/win32/tmake/libsrc/histograms_lut/histograms_lut.pro @@ -0,0 +1,34 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + hist_dispatch.c \ + im_gammacorrect.c \ + im_heq.c \ + im_hist.c \ + im_histeq.c \ + im_histgr.c \ + im_histnD.c \ + im_histplot.c \ + im_histspec.c \ + im_hsp.c \ + im_identity.c \ + im_invertlut.c \ + im_lhisteq.c \ + im_maplut.c \ + im_stdif.c \ + tone.c +TARGET = histograms_lut diff --git a/win32/tmake/libsrc/inplace/Makefile b/win32/tmake/libsrc/inplace/Makefile new file mode 100644 index 00000000..d89859a2 --- /dev/null +++ b/win32/tmake/libsrc/inplace/Makefile @@ -0,0 +1,122 @@ +############################################################################# +# Makefile for building inplace +# Generated by tmake at 16:03, 2003/06/11 +# Project: inplace +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_circle.c \ + im_flood.c \ + im_insertplace.c \ + im_line.c \ + im_paintrect.c \ + im_plotmask.c \ + inplace_dispatch.c \ + line_draw.c \ + plot_point.c \ + smudge_area.c +OBJECTS = im_circle.obj \ + im_flood.obj \ + im_insertplace.obj \ + im_line.obj \ + im_paintrect.obj \ + im_plotmask.obj \ + inplace_dispatch.obj \ + line_draw.obj \ + plot_point.obj \ + smudge_area.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\inplace.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: inplace.pro + tmake inplace.pro -o Makefile + +dist: + $(ZIP) inplace.zip inplace.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_circle.obj + -del im_flood.obj + -del im_insertplace.obj + -del im_line.obj + -del im_paintrect.obj + -del im_plotmask.obj + -del inplace_dispatch.obj + -del line_draw.obj + -del plot_point.obj + -del smudge_area.obj + -del $(TARGET) + +####### Compile + +im_circle.obj: im_circle.c + +im_flood.obj: im_flood.c + +im_insertplace.obj: im_insertplace.c + +im_line.obj: im_line.c + +im_paintrect.obj: im_paintrect.c + +im_plotmask.obj: im_plotmask.c + +inplace_dispatch.obj: inplace_dispatch.c + +line_draw.obj: line_draw.c + +plot_point.obj: plot_point.c + +smudge_area.obj: smudge_area.c + diff --git a/win32/tmake/libsrc/inplace/inplace.pro b/win32/tmake/libsrc/inplace/inplace.pro new file mode 100644 index 00000000..4718cacd --- /dev/null +++ b/win32/tmake/libsrc/inplace/inplace.pro @@ -0,0 +1,28 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_circle.c \ + im_flood.c \ + im_insertplace.c \ + im_line.c \ + im_paintrect.c \ + im_plotmask.c \ + inplace_dispatch.c \ + line_draw.c \ + plot_point.c \ + smudge_area.c +TARGET = inplace diff --git a/win32/tmake/libsrc/iofuncs/Makefile b/win32/tmake/libsrc/iofuncs/Makefile new file mode 100644 index 00000000..322e9bd7 --- /dev/null +++ b/win32/tmake/libsrc/iofuncs/Makefile @@ -0,0 +1,337 @@ +############################################################################# +# Makefile for building iofuncs +# Generated by tmake at 16:03, 2003/06/11 +# Project: iofuncs +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = callback.c \ + debug.c \ + dispatch_types.c \ + error.c \ + error_exit.c \ + im_append_Hist.c \ + im_binfile.c \ + im_close.c \ + im_cp_Hist.c \ + im_cp_desc.c \ + im_crwrhd.c \ + im_debugim.c \ + im_demand_hint.c \ + im_desc_hd.c \ + im_generate.c \ + im_header.c \ + im_histlin.c \ + im_image.c \ + im_init.c \ + im_initdesc.c \ + im_inithd.c \ + im_iocheck.c \ + im_iterate.c \ + im_makerw.c \ + im_mapfile.c \ + im_openin.c \ + im_open.c \ + im_openout.c \ + im_partial.c \ + im_piocheck.c \ + im_prepare.c \ + im_printdesc.c \ + im_printhd.c \ + im_printlines.c \ + im_readhist.c \ + im_setbox.c \ + im_setbuf.c \ + im_setupout.c \ + im_unmapfile.c \ + im_updatehist.c \ + im_guess_prefix.c \ + im_wrapmany.c \ + im_wrapone.c \ + im_writeline.c \ + list.c \ + memory.c \ + package.c \ + predicate.c \ + region.c \ + rect.c \ + thread.c \ + threadgroup.c \ + time.c +OBJECTS = callback.obj \ + debug.obj \ + dispatch_types.obj \ + error.obj \ + error_exit.obj \ + im_append_Hist.obj \ + im_binfile.obj \ + im_close.obj \ + im_cp_Hist.obj \ + im_cp_desc.obj \ + im_crwrhd.obj \ + im_debugim.obj \ + im_demand_hint.obj \ + im_desc_hd.obj \ + im_generate.obj \ + im_header.obj \ + im_histlin.obj \ + im_image.obj \ + im_init.obj \ + im_initdesc.obj \ + im_inithd.obj \ + im_iocheck.obj \ + im_iterate.obj \ + im_makerw.obj \ + im_mapfile.obj \ + im_openin.obj \ + im_open.obj \ + im_openout.obj \ + im_partial.obj \ + im_piocheck.obj \ + im_prepare.obj \ + im_printdesc.obj \ + im_printhd.obj \ + im_printlines.obj \ + im_readhist.obj \ + im_setbox.obj \ + im_setbuf.obj \ + im_setupout.obj \ + im_unmapfile.obj \ + im_updatehist.obj \ + im_guess_prefix.obj \ + im_wrapmany.obj \ + im_wrapone.obj \ + im_writeline.obj \ + list.obj \ + memory.obj \ + package.obj \ + predicate.obj \ + region.obj \ + rect.obj \ + thread.obj \ + threadgroup.obj \ + time.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\iofuncs.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: iofuncs.pro + tmake iofuncs.pro -o Makefile + +dist: + $(ZIP) iofuncs.zip iofuncs.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del callback.obj + -del debug.obj + -del dispatch_types.obj + -del error.obj + -del error_exit.obj + -del im_append_Hist.obj + -del im_binfile.obj + -del im_close.obj + -del im_cp_Hist.obj + -del im_cp_desc.obj + -del im_crwrhd.obj + -del im_debugim.obj + -del im_demand_hint.obj + -del im_desc_hd.obj + -del im_generate.obj + -del im_header.obj + -del im_histlin.obj + -del im_image.obj + -del im_init.obj + -del im_initdesc.obj + -del im_inithd.obj + -del im_iocheck.obj + -del im_iterate.obj + -del im_makerw.obj + -del im_mapfile.obj + -del im_openin.obj + -del im_open.obj + -del im_openout.obj + -del im_partial.obj + -del im_piocheck.obj + -del im_prepare.obj + -del im_printdesc.obj + -del im_printhd.obj + -del im_printlines.obj + -del im_readhist.obj + -del im_setbox.obj + -del im_setbuf.obj + -del im_setupout.obj + -del im_unmapfile.obj + -del im_updatehist.obj + -del im_guess_prefix.obj + -del im_wrapmany.obj + -del im_wrapone.obj + -del im_writeline.obj + -del list.obj + -del memory.obj + -del package.obj + -del predicate.obj + -del region.obj + -del rect.obj + -del thread.obj + -del threadgroup.obj + -del time.obj + -del $(TARGET) + +####### Compile + +callback.obj: callback.c + +debug.obj: debug.c + +dispatch_types.obj: dispatch_types.c + +error.obj: error.c + +error_exit.obj: error_exit.c + +im_append_Hist.obj: im_append_Hist.c + +im_binfile.obj: im_binfile.c + +im_close.obj: im_close.c + +im_cp_Hist.obj: im_cp_Hist.c + +im_cp_desc.obj: im_cp_desc.c + +im_crwrhd.obj: im_crwrhd.c + +im_debugim.obj: im_debugim.c + +im_demand_hint.obj: im_demand_hint.c + +im_desc_hd.obj: im_desc_hd.c + +im_generate.obj: im_generate.c + +im_header.obj: im_header.c + +im_histlin.obj: im_histlin.c + +im_image.obj: im_image.c + +im_init.obj: im_init.c + +im_initdesc.obj: im_initdesc.c + +im_inithd.obj: im_inithd.c + +im_iocheck.obj: im_iocheck.c + +im_iterate.obj: im_iterate.c + +im_makerw.obj: im_makerw.c + +im_mapfile.obj: im_mapfile.c + +im_openin.obj: im_openin.c + +im_open.obj: im_open.c + +im_openout.obj: im_openout.c + +im_partial.obj: im_partial.c + +im_piocheck.obj: im_piocheck.c + +im_prepare.obj: im_prepare.c + +im_printdesc.obj: im_printdesc.c + +im_printhd.obj: im_printhd.c + +im_printlines.obj: im_printlines.c + +im_readhist.obj: im_readhist.c + +im_setbox.obj: im_setbox.c + +im_setbuf.obj: im_setbuf.c + +im_setupout.obj: im_setupout.c + +im_unmapfile.obj: im_unmapfile.c + +im_updatehist.obj: im_updatehist.c + +im_guess_prefix.obj: im_guess_prefix.c + +im_wrapmany.obj: im_wrapmany.c + +im_wrapone.obj: im_wrapone.c + +im_writeline.obj: im_writeline.c + +list.obj: list.c + +memory.obj: memory.c + +package.obj: package.c + +predicate.obj: predicate.c + +region.obj: region.c + +rect.obj: rect.c + +thread.obj: thread.c + +threadgroup.obj: threadgroup.c + +time.obj: time.c + diff --git a/win32/tmake/libsrc/iofuncs/iofuncs.pro b/win32/tmake/libsrc/iofuncs/iofuncs.pro new file mode 100644 index 00000000..9d758b8a --- /dev/null +++ b/win32/tmake/libsrc/iofuncs/iofuncs.pro @@ -0,0 +1,72 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + callback.c \ + debug.c \ + dispatch_types.c \ + error.c \ + error_exit.c \ + im_append_Hist.c \ + im_binfile.c \ + im_close.c \ + im_cp_Hist.c \ + im_cp_desc.c \ + im_crwrhd.c \ + im_debugim.c \ + im_demand_hint.c \ + im_desc_hd.c \ + im_generate.c \ + im_header.c \ + im_histlin.c \ + im_image.c \ + im_init.c \ + im_initdesc.c \ + im_inithd.c \ + im_iocheck.c \ + im_iterate.c \ + im_makerw.c \ + im_mapfile.c \ + im_openin.c \ + im_open.c \ + im_openout.c \ + im_partial.c \ + im_piocheck.c \ + im_prepare.c \ + im_printdesc.c \ + im_printhd.c \ + im_printlines.c \ + im_readhist.c \ + im_setbox.c \ + im_setbuf.c \ + im_setupout.c \ + im_unmapfile.c \ + im_updatehist.c \ + im_guess_prefix.c \ + im_wrapmany.c \ + im_wrapone.c \ + im_writeline.c \ + list.c \ + memory.c \ + package.c \ + predicate.c \ + region.c \ + rect.c \ + thread.c \ + threadgroup.c \ + time.c +TARGET = iofuncs + diff --git a/win32/tmake/libsrc/makefile_todo b/win32/tmake/libsrc/makefile_todo new file mode 100644 index 00000000..12fde75b --- /dev/null +++ b/win32/tmake/libsrc/makefile_todo @@ -0,0 +1,222 @@ +############################################################################# +# Makefile for building targets in sub directories. +# Generated by tmake at 13:14, 2003/06/11 +# Project: todo +# Template: subdirs +############################################################################# + +MAKEFILE= Makefile +TMAKE = tmake + +SUBDIRS = acquire \ + arithmetic \ + boolean \ + colour \ + conversion \ + convolution \ + freq_filt \ + histograms_lut \ + inplace \ + iofuncs \ + matrix \ + morphology \ + mosaicing \ + other \ + relational \ + video \ + . + +all: $(SUBDIRS) vips + +acquire: FORCE + cd acquire + $(MAKE) + @cd .. + +arithmetic: FORCE + cd arithmetic + $(MAKE) + @cd .. + +boolean: FORCE + cd boolean + $(MAKE) + @cd .. + +colour: FORCE + cd colour + $(MAKE) + @cd .. + +conversion: FORCE + cd conversion + $(MAKE) + @cd .. + +convolution: FORCE + cd convolution + $(MAKE) + @cd .. + +freq_filt: FORCE + cd freq_filt + $(MAKE) + @cd .. + +histograms_lut: FORCE + cd histograms_lut + $(MAKE) + @cd .. + +inplace: FORCE + cd inplace + $(MAKE) + @cd .. + +iofuncs: FORCE + cd iofuncs + $(MAKE) + @cd .. + +matrix: FORCE + cd matrix + $(MAKE) + @cd .. + +morphology: FORCE + cd morphology + $(MAKE) + @cd .. + +mosaicing: FORCE + cd mosaicing + $(MAKE) + @cd .. + +other: FORCE + cd other + $(MAKE) + @cd .. + +relational: FORCE + cd relational + $(MAKE) + @cd .. + +video: FORCE + cd video + $(MAKE) + @cd .. + +vips: FORCE + $(MAKE) + + +tmake: makefile_todo + +makefile_todo: todo.pro + tmake todo.pro -o makefile_todo + +tmake_all: + cd acquire + $(TMAKE) acquire.pro -o $(MAKEFILE) + @cd .. + cd arithmetic + $(TMAKE) arithmetic.pro -o $(MAKEFILE) + @cd .. + cd boolean + $(TMAKE) boolean.pro -o $(MAKEFILE) + @cd .. + cd colour + $(TMAKE) colour.pro -o $(MAKEFILE) + @cd .. + cd conversion + $(TMAKE) conversion.pro -o $(MAKEFILE) + @cd .. + cd convolution + $(TMAKE) convolution.pro -o $(MAKEFILE) + @cd .. + cd freq_filt + $(TMAKE) freq_filt.pro -o $(MAKEFILE) + @cd .. + cd histograms_lut + $(TMAKE) histograms_lut.pro -o $(MAKEFILE) + @cd .. + cd inplace + $(TMAKE) inplace.pro -o $(MAKEFILE) + @cd .. + cd iofuncs + $(TMAKE) iofuncs.pro -o $(MAKEFILE) + @cd .. + cd matrix + $(TMAKE) matrix.pro -o $(MAKEFILE) + @cd .. + cd morphology + $(TMAKE) morphology.pro -o $(MAKEFILE) + @cd .. + cd mosaicing + $(TMAKE) mosaicing.pro -o $(MAKEFILE) + @cd .. + cd other + $(TMAKE) other.pro -o $(MAKEFILE) + @cd .. + cd relational + $(TMAKE) relational.pro -o $(MAKEFILE) + @cd .. + cd video + $(TMAKE) video.pro -o $(MAKEFILE) + @cd .. + $(TMAKE) vips.pro -o $(MAKEFILE) + +clean: + cd acquire + $(MAKE) clean + @cd .. + cd arithmetic + $(MAKE) clean + @cd .. + cd boolean + $(MAKE) clean + @cd .. + cd colour + $(MAKE) clean + @cd .. + cd conversion + $(MAKE) clean + @cd .. + cd convolution + $(MAKE) clean + @cd .. + cd freq_filt + $(MAKE) clean + @cd .. + cd histograms_lut + $(MAKE) clean + @cd .. + cd inplace + $(MAKE) clean + @cd .. + cd iofuncs + $(MAKE) clean + @cd .. + cd matrix + $(MAKE) clean + @cd .. + cd morphology + $(MAKE) clean + @cd .. + cd mosaicing + $(MAKE) clean + @cd .. + cd other + $(MAKE) clean + @cd .. + cd relational + $(MAKE) clean + @cd .. + cd video + $(MAKE) clean + @cd .. + $(MAKE) clean + +FORCE: diff --git a/win32/tmake/libsrc/matrix/Makefile b/win32/tmake/libsrc/matrix/Makefile new file mode 100644 index 00000000..9c7ab386 --- /dev/null +++ b/win32/tmake/libsrc/matrix/Makefile @@ -0,0 +1,107 @@ +############################################################################# +# Makefile for building matrix +# Generated by tmake at 16:03, 2003/06/11 +# Project: matrix +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_invmat.c \ + im_matcat.c \ + im_matinv.c \ + im_matmul.c \ + im_mattrn.c \ + matalloc.c \ + matrix_dispatch.c +OBJECTS = im_invmat.obj \ + im_matcat.obj \ + im_matinv.obj \ + im_matmul.obj \ + im_mattrn.obj \ + matalloc.obj \ + matrix_dispatch.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\matrix.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: matrix.pro + tmake matrix.pro -o Makefile + +dist: + $(ZIP) matrix.zip matrix.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_invmat.obj + -del im_matcat.obj + -del im_matinv.obj + -del im_matmul.obj + -del im_mattrn.obj + -del matalloc.obj + -del matrix_dispatch.obj + -del $(TARGET) + +####### Compile + +im_invmat.obj: im_invmat.c + +im_matcat.obj: im_matcat.c + +im_matinv.obj: im_matinv.c + +im_matmul.obj: im_matmul.c + +im_mattrn.obj: im_mattrn.c + +matalloc.obj: matalloc.c + +matrix_dispatch.obj: matrix_dispatch.c + diff --git a/win32/tmake/libsrc/matrix/matrix.pro b/win32/tmake/libsrc/matrix/matrix.pro new file mode 100644 index 00000000..ba23c8e8 --- /dev/null +++ b/win32/tmake/libsrc/matrix/matrix.pro @@ -0,0 +1,26 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_invmat.c \ + im_matcat.c \ + im_matinv.c \ + im_matmul.c \ + im_mattrn.c \ + matalloc.c \ + matrix_dispatch.c +TARGET = matrix + diff --git a/win32/tmake/libsrc/morphology/Makefile b/win32/tmake/libsrc/morphology/Makefile new file mode 100644 index 00000000..4dfb484b --- /dev/null +++ b/win32/tmake/libsrc/morphology/Makefile @@ -0,0 +1,97 @@ +############################################################################# +# Makefile for building morphology +# Generated by tmake at 16:03, 2003/06/11 +# Project: morphology +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_cntlines.c \ + im_dilate.c \ + im_erode.c \ + morph_dispatch.c \ + im_profile.c +OBJECTS = im_cntlines.obj \ + im_dilate.obj \ + im_erode.obj \ + morph_dispatch.obj \ + im_profile.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\morphology.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: morphology.pro + tmake morphology.pro -o Makefile + +dist: + $(ZIP) morphology.zip morphology.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_cntlines.obj + -del im_dilate.obj + -del im_erode.obj + -del morph_dispatch.obj + -del im_profile.obj + -del $(TARGET) + +####### Compile + +im_cntlines.obj: im_cntlines.c + +im_dilate.obj: im_dilate.c + +im_erode.obj: im_erode.c + +morph_dispatch.obj: morph_dispatch.c + +im_profile.obj: im_profile.c + diff --git a/win32/tmake/libsrc/morphology/morphology.pro b/win32/tmake/libsrc/morphology/morphology.pro new file mode 100644 index 00000000..1b177773 --- /dev/null +++ b/win32/tmake/libsrc/morphology/morphology.pro @@ -0,0 +1,24 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_cntlines.c \ + im_dilate.c \ + im_erode.c \ + morph_dispatch.c \ + im_profile.c +TARGET = morphology + diff --git a/win32/tmake/libsrc/mosaicing/Makefile b/win32/tmake/libsrc/mosaicing/Makefile new file mode 100644 index 00000000..972d16b9 --- /dev/null +++ b/win32/tmake/libsrc/mosaicing/Makefile @@ -0,0 +1,185 @@ +############################################################################# +# Makefile for building mosaicing +# Generated by tmake at 16:03, 2003/06/11 +# Project: mosaicing +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h \ + merge.h \ + global_balance.h \ + mosaic.h +SOURCES = im_affine.c \ + match.c \ + mosaic1.c \ + mosaicing_dispatch.c \ + similarity.c \ + global_balance.c \ + im_avgdxdy.c \ + im_chkpair.c \ + im_clinear.c \ + im_improve.c \ + im_initialize.c \ + im_lrcalcon.c \ + im_lrmerge.c \ + im_lrmosaic.c \ + im_tbcalcon.c \ + im_tbmerge.c \ + im_remosaic.c \ + im_tbmosaic.c +OBJECTS = im_affine.obj \ + match.obj \ + mosaic1.obj \ + mosaicing_dispatch.obj \ + similarity.obj \ + global_balance.obj \ + im_avgdxdy.obj \ + im_chkpair.obj \ + im_clinear.obj \ + im_improve.obj \ + im_initialize.obj \ + im_lrcalcon.obj \ + im_lrmerge.obj \ + im_lrmosaic.obj \ + im_tbcalcon.obj \ + im_tbmerge.obj \ + im_remosaic.obj \ + im_tbmosaic.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\mosaicing.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: mosaicing.pro + tmake mosaicing.pro -o Makefile + +dist: + $(ZIP) mosaicing.zip mosaicing.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_affine.obj + -del match.obj + -del mosaic1.obj + -del mosaicing_dispatch.obj + -del similarity.obj + -del global_balance.obj + -del im_avgdxdy.obj + -del im_chkpair.obj + -del im_clinear.obj + -del im_improve.obj + -del im_initialize.obj + -del im_lrcalcon.obj + -del im_lrmerge.obj + -del im_lrmosaic.obj + -del im_tbcalcon.obj + -del im_tbmerge.obj + -del im_remosaic.obj + -del im_tbmosaic.obj + -del $(TARGET) + +####### Compile + +im_affine.obj: im_affine.c \ + merge.h + +match.obj: match.c \ + mosaic.h + +mosaic1.obj: mosaic1.c \ + mosaic.h \ + merge.h + +mosaicing_dispatch.obj: mosaicing_dispatch.c + +similarity.obj: similarity.c \ + merge.h + +global_balance.obj: global_balance.c \ + merge.h \ + global_balance.h + +im_avgdxdy.obj: im_avgdxdy.c \ + mosaic.h + +im_chkpair.obj: im_chkpair.c \ + mosaic.h + +im_clinear.obj: im_clinear.c \ + mosaic.h + +im_improve.obj: im_improve.c \ + mosaic.h + +im_initialize.obj: im_initialize.c \ + mosaic.h + +im_lrcalcon.obj: im_lrcalcon.c \ + mosaic.h + +im_lrmerge.obj: im_lrmerge.c \ + merge.h + +im_lrmosaic.obj: im_lrmosaic.c \ + mosaic.h + +im_tbcalcon.obj: im_tbcalcon.c \ + mosaic.h + +im_tbmerge.obj: im_tbmerge.c \ + merge.h + +im_remosaic.obj: im_remosaic.c \ + merge.h \ + global_balance.h + +im_tbmosaic.obj: im_tbmosaic.c \ + mosaic.h + diff --git a/win32/tmake/libsrc/mosaicing/mosaicing.pro b/win32/tmake/libsrc/mosaicing/mosaicing.pro new file mode 100644 index 00000000..207151c5 --- /dev/null +++ b/win32/tmake/libsrc/mosaicing/mosaicing.pro @@ -0,0 +1,37 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_affine.c \ + match.c \ + mosaic1.c \ + mosaicing_dispatch.c \ + similarity.c \ + global_balance.c \ + im_avgdxdy.c \ + im_chkpair.c \ + im_clinear.c \ + im_improve.c \ + im_initialize.c \ + im_lrcalcon.c \ + im_lrmerge.c \ + im_lrmosaic.c \ + im_tbcalcon.c \ + im_tbmerge.c \ + im_remosaic.c \ + im_tbmosaic.c +TARGET = mosaicing + diff --git a/win32/tmake/libsrc/other/Makefile b/win32/tmake/libsrc/other/Makefile new file mode 100644 index 00000000..bbda705a --- /dev/null +++ b/win32/tmake/libsrc/other/Makefile @@ -0,0 +1,127 @@ +############################################################################# +# Makefile for building other +# Generated by tmake at 16:03, 2003/06/11 +# Project: other +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = cooc_funcs.c \ + glds_funcs.c \ + im_dif_std.c \ + im_eye.c \ + im_grey.c \ + im_meanstd.c \ + im_simcontr.c \ + im_sines.c \ + im_spatres.c \ + im_zone.c \ + other_dispatch.c +OBJECTS = cooc_funcs.obj \ + glds_funcs.obj \ + im_dif_std.obj \ + im_eye.obj \ + im_grey.obj \ + im_meanstd.obj \ + im_simcontr.obj \ + im_sines.obj \ + im_spatres.obj \ + im_zone.obj \ + other_dispatch.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\other.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: other.pro + tmake other.pro -o Makefile + +dist: + $(ZIP) other.zip other.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del cooc_funcs.obj + -del glds_funcs.obj + -del im_dif_std.obj + -del im_eye.obj + -del im_grey.obj + -del im_meanstd.obj + -del im_simcontr.obj + -del im_sines.obj + -del im_spatres.obj + -del im_zone.obj + -del other_dispatch.obj + -del $(TARGET) + +####### Compile + +cooc_funcs.obj: cooc_funcs.c + +glds_funcs.obj: glds_funcs.c + +im_dif_std.obj: im_dif_std.c + +im_eye.obj: im_eye.c + +im_grey.obj: im_grey.c + +im_meanstd.obj: im_meanstd.c + +im_simcontr.obj: im_simcontr.c + +im_sines.obj: im_sines.c + +im_spatres.obj: im_spatres.c + +im_zone.obj: im_zone.c + +other_dispatch.obj: other_dispatch.c + diff --git a/win32/tmake/libsrc/other/other.pro b/win32/tmake/libsrc/other/other.pro new file mode 100644 index 00000000..c65ad353 --- /dev/null +++ b/win32/tmake/libsrc/other/other.pro @@ -0,0 +1,30 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + cooc_funcs.c \ + glds_funcs.c \ + im_dif_std.c \ + im_eye.c \ + im_grey.c \ + im_meanstd.c \ + im_simcontr.c \ + im_sines.c \ + im_spatres.c \ + im_zone.c \ + other_dispatch.c +TARGET = other + diff --git a/win32/tmake/libsrc/relational/Makefile b/win32/tmake/libsrc/relational/Makefile new file mode 100644 index 00000000..73ae58a9 --- /dev/null +++ b/win32/tmake/libsrc/relational/Makefile @@ -0,0 +1,92 @@ +############################################################################# +# Makefile for building relational +# Generated by tmake at 16:03, 2003/06/11 +# Project: relational +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = im_ifthenelse.c \ + im_blend.c \ + relational.c \ + relational_dispatch.c +OBJECTS = im_ifthenelse.obj \ + im_blend.obj \ + relational.obj \ + relational_dispatch.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\relational.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: relational.pro + tmake relational.pro -o Makefile + +dist: + $(ZIP) relational.zip relational.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del im_ifthenelse.obj + -del im_blend.obj + -del relational.obj + -del relational_dispatch.obj + -del $(TARGET) + +####### Compile + +im_ifthenelse.obj: im_ifthenelse.c + +im_blend.obj: im_blend.c + +relational.obj: relational.c + +relational_dispatch.obj: relational_dispatch.c + diff --git a/win32/tmake/libsrc/relational/relational.pro b/win32/tmake/libsrc/relational/relational.pro new file mode 100644 index 00000000..e1d477e3f --- /dev/null +++ b/win32/tmake/libsrc/relational/relational.pro @@ -0,0 +1,23 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + im_ifthenelse.c \ + im_blend.c \ + relational.c \ + relational_dispatch.c +TARGET = relational + diff --git a/win32/tmake/libsrc/todo.pro b/win32/tmake/libsrc/todo.pro new file mode 100644 index 00000000..83fcd569 --- /dev/null +++ b/win32/tmake/libsrc/todo.pro @@ -0,0 +1,19 @@ +TEMPLATE = subdirs +MAKE = nmake +SUBDIRS = \ + acquire \ + arithmetic \ + boolean \ + colour \ + conversion \ + convolution \ + freq_filt \ + histograms_lut \ + inplace \ + iofuncs \ + matrix \ + morphology \ + mosaicing \ + other \ + relational \ + video diff --git a/win32/tmake/libsrc/video/Makefile b/win32/tmake/libsrc/video/Makefile new file mode 100644 index 00000000..9615ae18 --- /dev/null +++ b/win32/tmake/libsrc/video/Makefile @@ -0,0 +1,82 @@ +############################################################################# +# Makefile for building video +# Generated by tmake at 16:03, 2003/06/11 +# Project: video +# Template: lib +############################################################################# + +####### Compiler, tools and options + +CC = cl +CXX = cl +CFLAGS = -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +CXXFLAGS= -nologo -W3 -O1 -DHAVE_CONFIG_H -DNO_DEBUG +INCPATH = -I"..\.." -I"..\..\include" -I"$(QTDIR)\include" +LIB = lib /NOLOGO +MOC = moc +UIC = uic + +ZIP = zip -r -9 + +####### Files + +HEADERS = ..\..\config.h +SOURCES = video_dispatch.c \ + im_video_v4l1.c +OBJECTS = video_dispatch.obj \ + im_video_v4l1.obj +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = +OBJMOC = +DIST = +TARGET = ..\..\Release\video.lib +INTERFACE_DECL_PATH = . + +####### Implicit rules + +.SUFFIXES: .cpp .cxx .cc .c + +.cpp.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cxx.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.cc.obj: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $< + +.c.obj: + $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $< + +####### Build rules + +all: $(TARGET) + +$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) + $(LIB) /OUT:$(TARGET) @<< + $(OBJECTS) $(OBJMOC) +<< + +moc: $(SRCMOC) + +tmake: Makefile + +Makefile: video.pro + tmake video.pro -o Makefile + +dist: + $(ZIP) video.zip video.pro $(SOURCES) $(HEADERS) $(DIST) $(INTERFACES) + +clean: + -del video_dispatch.obj + -del im_video_v4l1.obj + -del $(TARGET) + +####### Compile + +video_dispatch.obj: video_dispatch.c + +im_video_v4l1.obj: im_video_v4l1.c + diff --git a/win32/tmake/libsrc/video/video.pro b/win32/tmake/libsrc/video/video.pro new file mode 100644 index 00000000..cd428d8a --- /dev/null +++ b/win32/tmake/libsrc/video/video.pro @@ -0,0 +1,21 @@ +TEMPLATE = lib +DIR_LIB = ../../lib +CONFIG = release staticlib +win32:CONFIG = windows +TMAKEFLAGS = -nologo +DEFINES = HAVE_CONFIG_H +INCLUDEPATH = ../.. ../../include +HEADERS = ../../config.h +win32:LIBS = \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DESTDIR = ../../Release +VERSION = 7.8.10 + +SOURCES = \ + video_dispatch.c \ + im_video_v4l1.c +TARGET = video + diff --git a/win32/tmake/libsrc/vips.pro b/win32/tmake/libsrc/vips.pro new file mode 100644 index 00000000..42f9e463 --- /dev/null +++ b/win32/tmake/libsrc/vips.pro @@ -0,0 +1,21 @@ +TEMPLATE = app +DIR_LIB = ../lib +CONFIG = release dll +win32:CONFIG += windows +TMAKEFLAGS = -nologo +INCLUDEPATH = .. ../include +HEADERS = ../config.h +SOURCES = dummy.c +DESTDIR = ../Release +unix:LIBS += \ + $$DESTDIR/*.a +win32:LIBS += \ + $$DESTDIR/*.lib \ + $$DIR_LIB/libjpeg.lib \ + $$DIR_LIB/libtiff.lib \ + $$DIR_LIB/libpng.lib \ + $$DIR_LIB/libz.lib +DEF_FILE = vips.def +TARGET = vips +VERSION = 7.8.10 +CLEAN = $$DESTDIR/vips.exp $$DESTDIR/vips.lib