From 4e76695e19d649c6bbc30fcdf8ade0fa3fda3712 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 16 Aug 2009 15:00:08 +0000 Subject: [PATCH] merge gtkdoc branch --- ChangeLog | 5 + Makefile.am | 15 +- TODO | 54 +- bootstrap.sh | 10 +- configure.in | 86 +- contrib/mitsub/Makefile.am | 4 +- contrib/vdump/Makefile.am | 4 +- contrib/vips2dj/Makefile.am | 4 +- doc/Makefile | 13 - doc/Makefile.am | 10 + doc/README | 2 +- doc/reference/Makefile.am | 101 + doc/reference/VIPS-docs.sgml | 50 + doc/reference/VIPS-docs.sgml.in | 50 + gtk-doc.make | 177 + libvips/Makefile.am | 67 + libvips/acquire/Makefile.am | 7 + libvips/acquire/im_clamp.c | 127 + libvips/arithmetic/Makefile.am | 45 + libvips/arithmetic/arith_dispatch.c | 1539 ++ libvips/arithmetic/im_abs.c | 272 + libvips/arithmetic/im_add.c | 381 + libvips/arithmetic/im_avg.c | 207 + libvips/arithmetic/im_bandmean.c | 161 + libvips/arithmetic/im_ceil.c | 114 + libvips/arithmetic/im_cmulnorm.c | 75 + libvips/arithmetic/im_costra.c | 239 + libvips/arithmetic/im_cross_phase.c | 158 + libvips/arithmetic/im_deviate.c | 226 + libvips/arithmetic/im_divide.c | 234 + libvips/arithmetic/im_expntra.c | 249 + libvips/arithmetic/im_fav4.c | 95 + libvips/arithmetic/im_floor.c | 128 + libvips/arithmetic/im_gadd.c | 127 + libvips/arithmetic/im_gaddim.c | 241 + libvips/arithmetic/im_gfadd.c | 351 + libvips/arithmetic/im_invert.c | 151 + libvips/arithmetic/im_linreg.c | 433 + libvips/arithmetic/im_lintra.c | 383 + libvips/arithmetic/im_litecor.c | 321 + libvips/arithmetic/im_log10tra.c | 167 + libvips/arithmetic/im_logtra.c | 167 + libvips/arithmetic/im_max.c | 249 + libvips/arithmetic/im_maxpos.c | 151 + libvips/arithmetic/im_maxpos_avg.c | 201 + libvips/arithmetic/im_maxpos_vec.c | 470 + libvips/arithmetic/im_measure.c | 210 + libvips/arithmetic/im_min.c | 246 + libvips/arithmetic/im_minpos.c | 143 + libvips/arithmetic/im_multiply.c | 262 + libvips/arithmetic/im_point_bilinear.c | 127 + libvips/arithmetic/im_powtra.c | 235 + libvips/arithmetic/im_remainder.c | 278 + libvips/arithmetic/im_rint.c | 114 + libvips/arithmetic/im_sign.c | 162 + libvips/arithmetic/im_sintra.c | 239 + libvips/arithmetic/im_stats.c | 280 + libvips/arithmetic/im_subtract.c | 233 + libvips/arithmetic/im_tantra.c | 240 + libvips/boolean/Makefile.am | 7 + libvips/boolean/bool_dispatch.c | 316 + libvips/boolean/boolean.c | 664 + libvips/cimg/CImg.h | 22226 ++++++++++++++++++++ libvips/cimg/Makefile.am | 24 + libvips/cimg/cimg.cpp | 288 + libvips/cimg/cimg_dispatch.c | 164 + libvips/colour/Makefile.am | 32 + libvips/colour/colour.c | 1125 + libvips/colour/colour_dispatch.c | 1052 + libvips/colour/derived.c | 214 + libvips/colour/im_LCh2Lab.c | 108 + libvips/colour/im_LCh2UCS.c | 99 + libvips/colour/im_Lab2LCh.c | 102 + libvips/colour/im_Lab2LabQ.c | 158 + libvips/colour/im_Lab2LabS.c | 92 + libvips/colour/im_Lab2XYZ.c | 147 + libvips/colour/im_LabQ2Lab.c | 133 + libvips/colour/im_LabQ2LabS.c | 125 + libvips/colour/im_LabQ2disp.c | 209 + libvips/colour/im_LabS2Lab.c | 96 + libvips/colour/im_LabS2LabQ.c | 151 + libvips/colour/im_UCS2LCh.c | 109 + libvips/colour/im_XYZ2Lab.c | 190 + libvips/colour/im_XYZ2Yxy.c | 101 + libvips/colour/im_XYZ2disp.c | 165 + libvips/colour/im_Yxy2XYZ.c | 102 + libvips/colour/im_dE00_fromLab.c | 110 + libvips/colour/im_dECMC_fromLab.c | 110 + libvips/colour/im_dE_fromLab.c | 116 + libvips/colour/im_disp2XYZ.c | 116 + libvips/colour/im_float2rad.c | 207 + libvips/colour/im_icc_transform.c | 821 + libvips/colour/im_lab_morph.c | 257 + libvips/colour/im_rad2float.c | 194 + libvips/conversion/Makefile.am | 45 + libvips/conversion/conver_dispatch.c | 1631 ++ libvips/conversion/im_bandjoin.c | 162 + libvips/conversion/im_bernd.c | 95 + libvips/conversion/im_black.c | 118 + libvips/conversion/im_c2amph.c | 142 + libvips/conversion/im_c2imag.c | 117 + libvips/conversion/im_c2ps.c | 64 + libvips/conversion/im_c2real.c | 122 + libvips/conversion/im_c2rect.c | 108 + libvips/conversion/im_clip.c | 531 + libvips/conversion/im_copy.c | 558 + libvips/conversion/im_extract.c | 269 + libvips/conversion/im_falsecolour.c | 355 + libvips/conversion/im_fliphor.c | 156 + libvips/conversion/im_flipver.c | 147 + libvips/conversion/im_gbandjoin.c | 231 + libvips/conversion/im_grid.c | 182 + libvips/conversion/im_insert.c | 387 + libvips/conversion/im_lrjoin.c | 89 + libvips/conversion/im_mask2vips.c | 97 + libvips/conversion/im_msb.c | 346 + libvips/conversion/im_print.c | 52 + libvips/conversion/im_recomb.c | 167 + libvips/conversion/im_replicate.c | 158 + libvips/conversion/im_ri2c.c | 170 + libvips/conversion/im_rightshift_size.c | 300 + libvips/conversion/im_rot180.c | 166 + libvips/conversion/im_rot270.c | 170 + libvips/conversion/im_rot90.c | 170 + libvips/conversion/im_scale.c | 98 + libvips/conversion/im_scaleps.c | 98 + libvips/conversion/im_slice.c | 157 + libvips/conversion/im_subsample.c | 249 + libvips/conversion/im_system.c | 226 + libvips/conversion/im_tbjoin.c | 89 + libvips/conversion/im_text.c | 240 + libvips/conversion/im_thresh.c | 134 + libvips/conversion/im_vips2mask.c | 126 + libvips/conversion/im_wrap.c | 127 + libvips/conversion/im_zoom.c | 378 + libvips/convolution/Makefile.am | 32 + libvips/convolution/convol_dispatch.c | 1415 ++ libvips/convolution/im_addgnoise.c | 90 + libvips/convolution/im_compass.c | 148 + libvips/convolution/im_contrast_surface.c | 276 + libvips/convolution/im_conv.c | 587 + libvips/convolution/im_convf.c | 352 + libvips/convolution/im_convsep.c | 445 + libvips/convolution/im_convsepf.c | 357 + libvips/convolution/im_convsub.c | 264 + libvips/convolution/im_embed.c | 550 + libvips/convolution/im_fastcor.c | 211 + libvips/convolution/im_gaussmasks.c | 217 + libvips/convolution/im_gaussnoise.c | 158 + libvips/convolution/im_gradcor.c | 557 + libvips/convolution/im_logmasks.c | 220 + libvips/convolution/im_mpercent.c | 101 + libvips/convolution/im_phasecor_fft.c | 63 + libvips/convolution/im_rank.c | 426 + libvips/convolution/im_rank_image.c | 330 + libvips/convolution/im_resize_linear.c | 194 + libvips/convolution/im_sharpen.c | 308 + libvips/convolution/im_shrink.c | 323 + libvips/convolution/im_spcor.c | 333 + libvips/convolution/im_stretch3.c | 328 + libvips/convolution/im_zerox.c | 181 + libvips/convolution/rotmask.c | 356 + libvips/convolution/rw_mask.c | 727 + libvips/dummy.c | 3 + libvips/dummy2.cc | 4 + libvips/format/Makefile.am | 26 + libvips/format/dbh.h | 98 + libvips/format/format.c | 339 + libvips/format/format_dispatch.c | 417 + libvips/format/im_analyze2vips.c | 623 + libvips/format/im_csv2vips.c | 346 + libvips/format/im_exr2vips.c | 493 + libvips/format/im_file2vips.c | 815 + libvips/format/im_jpeg2vips.c | 761 + libvips/format/im_magick2vips.c | 699 + libvips/format/im_png2vips.c | 400 + libvips/format/im_ppm2vips.c | 510 + libvips/format/im_raw2vips.c | 64 + libvips/format/im_tiff2vips.c | 1585 ++ libvips/format/im_tile_cache.c | 391 + libvips/format/im_vips2csv.c | 146 + libvips/format/im_vips2jpeg.c | 897 + libvips/format/im_vips2png.c | 316 + libvips/format/im_vips2ppm.c | 287 + libvips/format/im_vips2raw.c | 126 + libvips/format/im_vips2tiff.c | 1679 ++ libvips/format/matlab.c | 309 + libvips/format/radiance.c | 1202 ++ libvips/freq_filt/Makefile.am | 17 + libvips/freq_filt/fft_sp.c | 213 + libvips/freq_filt/fmask4th.c | 800 + libvips/freq_filt/fmaskcir.c | 664 + libvips/freq_filt/freq_dispatch.c | 311 + libvips/freq_filt/im_disp_ps.c | 107 + libvips/freq_filt/im_fractsurf.c | 87 + libvips/freq_filt/im_freq_mask.c | 237 + libvips/freq_filt/im_freqflt.c | 124 + libvips/freq_filt/im_fwfft.c | 643 + libvips/freq_filt/im_invfft.c | 286 + libvips/freq_filt/im_invfftr.c | 335 + libvips/freq_filt/im_rotquad.c | 103 + libvips/histograms_lut/Makefile.am | 23 + libvips/histograms_lut/hist_dispatch.c | 811 + libvips/histograms_lut/im_buildlut.c | 247 + libvips/histograms_lut/im_gammacorrect.c | 82 + libvips/histograms_lut/im_heq.c | 79 + libvips/histograms_lut/im_hist.c | 75 + libvips/histograms_lut/im_histeq.c | 214 + libvips/histograms_lut/im_histgr.c | 384 + libvips/histograms_lut/im_histnD.c | 272 + libvips/histograms_lut/im_histplot.c | 342 + libvips/histograms_lut/im_histspec.c | 217 + libvips/histograms_lut/im_hsp.c | 77 + libvips/histograms_lut/im_identity.c | 163 + libvips/histograms_lut/im_invertlut.c | 268 + libvips/histograms_lut/im_lhisteq.c | 221 + libvips/histograms_lut/im_maplut.c | 637 + libvips/histograms_lut/im_project.c | 303 + libvips/histograms_lut/im_stdif.c | 262 + libvips/histograms_lut/tone.c | 539 + libvips/include/Makefile.am | 2 + libvips/include/vips/Makefile.am | 33 + libvips/include/vips/almostdeprecated.h | 74 + libvips/include/vips/arithmetic.h | 96 + libvips/include/vips/buf.h | 86 + libvips/include/vips/colour.h | 240 + libvips/include/vips/debug.h | 50 + libvips/include/vips/deprecated.h | 314 + libvips/include/vips/dispatch.h | 295 + libvips/include/vips/fmask.h | 86 + libvips/include/vips/format.h | 124 + libvips/include/vips/image.h | 268 + libvips/include/vips/inlines.h | 70 + libvips/include/vips/internal.h | 178 + libvips/include/vips/interpolate.h | 123 + libvips/include/vips/intl.h | 50 + libvips/include/vips/meta.h | 89 + libvips/include/vips/mosaic.h | 86 + libvips/include/vips/object.h | 250 + libvips/include/vips/private.h | 89 + libvips/include/vips/proto.h | 712 + libvips/include/vips/r_access.h | 88 + libvips/include/vips/rect.h | 64 + libvips/include/vips/region.h | 259 + libvips/include/vips/semaphore.h | 64 + libvips/include/vips/struct.h | 44 + libvips/include/vips/thread.h | 77 + libvips/include/vips/threadgroup.h | 131 + libvips/include/vips/transform.h | 69 + libvips/include/vips/type.h | 148 + libvips/include/vips/util.h | 270 + libvips/include/vips/version.h.in | 15 + libvips/include/vips/vips.h | 149 + libvips/inplace/Makefile.am | 15 + libvips/inplace/im_circle.c | 134 + libvips/inplace/im_flood.c | 429 + libvips/inplace/im_insertplace.c | 129 + libvips/inplace/im_line.c | 153 + libvips/inplace/im_paintrect.c | 105 + libvips/inplace/im_plotmask.c | 239 + libvips/inplace/inplace_dispatch.c | 288 + libvips/inplace/line_draw.c | 449 + libvips/inplace/plot_point.c | 119 + libvips/inplace/smudge_area.c | 319 + libvips/iofuncs/Makefile.am | 61 + libvips/iofuncs/base64.c | 288 + libvips/iofuncs/base64.h | 25 + libvips/iofuncs/buf.c | 583 + libvips/iofuncs/buffer.c | 591 + libvips/iofuncs/callback.c | 171 + libvips/iofuncs/debug.c | 156 + libvips/iofuncs/dispatch_types.c | 904 + libvips/iofuncs/error.c | 253 + libvips/iofuncs/error_exit.c | 75 + libvips/iofuncs/im_binfile.c | 186 + libvips/iofuncs/im_bits_of_fmt.c | 64 + libvips/iofuncs/im_close.c | 310 + libvips/iofuncs/im_cp_desc.c | 152 + libvips/iofuncs/im_debugim.c | 138 + libvips/iofuncs/im_demand_hint.c | 189 + libvips/iofuncs/im_generate.c | 509 + libvips/iofuncs/im_guess_prefix.c | 428 + libvips/iofuncs/im_header.c | 269 + libvips/iofuncs/im_histlin.c | 120 + libvips/iofuncs/im_image.c | 92 + libvips/iofuncs/im_init.c | 185 + libvips/iofuncs/im_init_world.c | 240 + libvips/iofuncs/im_initdesc.c | 81 + libvips/iofuncs/im_iocheck.c | 320 + libvips/iofuncs/im_iterate.c | 240 + libvips/iofuncs/im_makerw.c | 86 + libvips/iofuncs/im_mapfile.c | 347 + libvips/iofuncs/im_open.c | 498 + libvips/iofuncs/im_open_vips.c | 1267 ++ libvips/iofuncs/im_partial.c | 64 + libvips/iofuncs/im_piocheck.c | 196 + libvips/iofuncs/im_prepare.c | 388 + libvips/iofuncs/im_printdesc.c | 355 + libvips/iofuncs/im_printlines.c | 150 + libvips/iofuncs/im_render.c | 1096 + libvips/iofuncs/im_setbox.c | 64 + libvips/iofuncs/im_setbuf.c | 73 + libvips/iofuncs/im_setupout.c | 163 + libvips/iofuncs/im_unmapfile.c | 71 + libvips/iofuncs/im_updatehist.c | 80 + libvips/iofuncs/im_wbuffer.c | 428 + libvips/iofuncs/im_wrapmany.c | 259 + libvips/iofuncs/im_wraptwo.c | 103 + libvips/iofuncs/im_writeline.c | 128 + libvips/iofuncs/memory.c | 209 + libvips/iofuncs/meta.c | 942 + libvips/iofuncs/object.c | 1046 + libvips/iofuncs/package.c | 1089 + libvips/iofuncs/predicate.c | 358 + libvips/iofuncs/rect.c | 167 + libvips/iofuncs/region.c | 631 + libvips/iofuncs/semaphore.c | 146 + libvips/iofuncs/threadgroup.c | 678 + libvips/iofuncs/time.c | 177 + libvips/iofuncs/type.c | 352 + libvips/iofuncs/util.c | 1393 ++ libvips/iofuncs/window.c | 399 + libvips/matrix/Makefile.am | 11 + libvips/matrix/im_matcat.c | 88 + libvips/matrix/im_matinv.c | 504 + libvips/matrix/im_matmul.c | 107 + libvips/matrix/im_mattrn.c | 87 + libvips/matrix/matalloc.c | 241 + libvips/matrix/matrix_dispatch.c | 181 + libvips/morphology/Makefile.am | 10 + libvips/morphology/im_cntlines.c | 131 + libvips/morphology/im_dilate.c | 329 + libvips/morphology/im_erode.c | 325 + libvips/morphology/im_profile.c | 132 + libvips/morphology/morph_dispatch.c | 213 + libvips/mosaicing/Makefile.am | 26 + libvips/mosaicing/global_balance.c | 1750 ++ libvips/mosaicing/global_balance.h | 126 + libvips/mosaicing/im_align_bands.c | 87 + libvips/mosaicing/im_avgdxdy.c | 85 + libvips/mosaicing/im_chkpair.c | 241 + libvips/mosaicing/im_clinear.c | 183 + libvips/mosaicing/im_improve.c | 193 + libvips/mosaicing/im_initialize.c | 100 + libvips/mosaicing/im_lrcalcon.c | 318 + libvips/mosaicing/im_lrmerge.c | 1138 + libvips/mosaicing/im_lrmosaic.c | 475 + libvips/mosaicing/im_maxpos_subpel.c | 157 + libvips/mosaicing/im_remosaic.c | 137 + libvips/mosaicing/im_tbcalcon.c | 137 + libvips/mosaicing/im_tbmerge.c | 734 + libvips/mosaicing/im_tbmosaic.c | 309 + libvips/mosaicing/match.c | 154 + libvips/mosaicing/merge.h | 111 + libvips/mosaicing/mosaic.h | 97 + libvips/mosaicing/mosaic1.c | 456 + libvips/mosaicing/mosaicing_dispatch.c | 760 + libvips/other/Makefile.am | 18 + libvips/other/cooc_funcs.c | 472 + libvips/other/glds_funcs.c | 248 + libvips/other/im_benchmark.c | 287 + libvips/other/im_dif_std.c | 101 + libvips/other/im_eye.c | 134 + libvips/other/im_grey.c | 146 + libvips/other/im_make_xy.c | 116 + libvips/other/im_meanstd.c | 136 + libvips/other/im_simcontr.c | 133 + libvips/other/im_sines.c | 138 + libvips/other/im_spatres.c | 146 + libvips/other/im_zone.c | 127 + libvips/other/other_dispatch.c | 332 + libvips/relational/Makefile.am | 9 + libvips/relational/im_blend.c | 366 + libvips/relational/im_ifthenelse.c | 213 + libvips/relational/relational.c | 738 + libvips/relational/relational_dispatch.c | 513 + libvips/resample/Makefile.am | 16 + libvips/resample/bicubic.cpp | 457 + libvips/resample/im_affine.c | 520 + libvips/resample/interpolate.c | 493 + libvips/resample/nohalo1.cpp | 665 + libvips/resample/nohalo2.cpp | 1112 + libvips/resample/resample_dispatch.c | 281 + libvips/resample/similarity.c | 122 + libvips/resample/snohalo1.cpp | 606 + libvips/resample/templates.h | 255 + libvips/resample/transform.c | 242 + libvips/resample/yafrsmooth.cpp | 803 + libvips/video/Makefile.am | 8 + libvips/video/im_video_test.c | 51 + libvips/video/im_video_v4l1.c | 679 + libvips/video/im_video_v4l1.h | 76 + libvips/video/video_dispatch.c | 115 + libvipsCC/Makefile.am | 24 + libvipsCC/VDisplay.cc | 189 + libvipsCC/VError.cc | 99 + libvipsCC/VImage.cc | 513 + libvipsCC/VMask.cc | 661 + libvipsCC/include/Makefile.am | 2 + libvipsCC/include/vipsCC/Makefile.am | 11 + libvipsCC/include/vipsCC/VDisplay.h | 113 + libvipsCC/include/vipsCC/VError.h | 82 + libvipsCC/include/vipsCC/VImage.h | 447 + libvipsCC/include/vipsCC/VMask.h | 410 + libvipsCC/include/vipsCC/vips | 109 + libvipsCC/include/vipsCC/vipsc++.h | 315 + libvipsCC/include/vipsCC/vipscpp.h | 39 + libvipsCC/vipsc++.cc | 5502 +++++ swig/Makefile.am | 5 + swig/test/bench_pil.py | 24 + swig/test/bench_vips.py | 24 + swig/test/pilvips.py | 37 + swig/test/testvipsCC.py | 44 + swig/vipsCC/Makefile.am | 58 + swig/vipsCC/VDisplay.i | 15 + swig/vipsCC/VError.i | 19 + swig/vipsCC/VImage.i | 335 + swig/vipsCC/VMask.i | 35 + swig/vipsCC/__init__.py | 1 + tools/Makefile.am | 6 + tools/iofuncs/Makefile.am | 27 + tools/iofuncs/binfile.c | 81 + tools/iofuncs/debugim.c | 69 + tools/iofuncs/edvips.c | 206 + tools/iofuncs/header.c | 192 + tools/iofuncs/printlines.c | 70 + tools/iofuncs/vips.c | 978 + tools/mosaicing/Makefile.am | 12 + tools/mosaicing/find_mosaic.c | 430 + tools/mosaicing/mergeup.c | 559 + tools/other/Makefile.am | 23 + tools/other/cooc.c | 88 + tools/other/cooc_features.c | 86 + tools/other/glds.c | 86 + tools/other/glds_features.c | 83 + tools/other/simcontr.c | 78 + tools/other/sines.c | 86 + tools/other/spatres.c | 83 + tools/other/squares.c | 87 + tools/scripts/Makefile.am | 25 + tools/scripts/batch_crop.in | 45 + tools/scripts/batch_image_convert.in | 43 + tools/scripts/batch_rubber_sheet.in | 38 + tools/scripts/light_correct.in | 57 + tools/scripts/post_install | 5 + tools/scripts/shrink_width.in | 18 + tools/scripts/vips-7.19 | 116 + 447 files changed, 142507 insertions(+), 70 deletions(-) delete mode 100644 doc/Makefile create mode 100644 doc/Makefile.am create mode 100644 doc/reference/Makefile.am create mode 100644 doc/reference/VIPS-docs.sgml create mode 100644 doc/reference/VIPS-docs.sgml.in create mode 100644 gtk-doc.make create mode 100644 libvips/Makefile.am create mode 100644 libvips/acquire/Makefile.am create mode 100644 libvips/acquire/im_clamp.c create mode 100644 libvips/arithmetic/Makefile.am create mode 100644 libvips/arithmetic/arith_dispatch.c create mode 100644 libvips/arithmetic/im_abs.c create mode 100644 libvips/arithmetic/im_add.c create mode 100644 libvips/arithmetic/im_avg.c create mode 100644 libvips/arithmetic/im_bandmean.c create mode 100644 libvips/arithmetic/im_ceil.c create mode 100644 libvips/arithmetic/im_cmulnorm.c create mode 100644 libvips/arithmetic/im_costra.c create mode 100644 libvips/arithmetic/im_cross_phase.c create mode 100644 libvips/arithmetic/im_deviate.c create mode 100644 libvips/arithmetic/im_divide.c create mode 100644 libvips/arithmetic/im_expntra.c create mode 100644 libvips/arithmetic/im_fav4.c create mode 100644 libvips/arithmetic/im_floor.c create mode 100644 libvips/arithmetic/im_gadd.c create mode 100644 libvips/arithmetic/im_gaddim.c create mode 100644 libvips/arithmetic/im_gfadd.c create mode 100644 libvips/arithmetic/im_invert.c create mode 100644 libvips/arithmetic/im_linreg.c create mode 100644 libvips/arithmetic/im_lintra.c create mode 100644 libvips/arithmetic/im_litecor.c create mode 100644 libvips/arithmetic/im_log10tra.c create mode 100644 libvips/arithmetic/im_logtra.c create mode 100644 libvips/arithmetic/im_max.c create mode 100644 libvips/arithmetic/im_maxpos.c create mode 100644 libvips/arithmetic/im_maxpos_avg.c create mode 100644 libvips/arithmetic/im_maxpos_vec.c create mode 100644 libvips/arithmetic/im_measure.c create mode 100644 libvips/arithmetic/im_min.c create mode 100644 libvips/arithmetic/im_minpos.c create mode 100644 libvips/arithmetic/im_multiply.c create mode 100644 libvips/arithmetic/im_point_bilinear.c create mode 100644 libvips/arithmetic/im_powtra.c create mode 100644 libvips/arithmetic/im_remainder.c create mode 100644 libvips/arithmetic/im_rint.c create mode 100644 libvips/arithmetic/im_sign.c create mode 100644 libvips/arithmetic/im_sintra.c create mode 100644 libvips/arithmetic/im_stats.c create mode 100644 libvips/arithmetic/im_subtract.c create mode 100644 libvips/arithmetic/im_tantra.c create mode 100644 libvips/boolean/Makefile.am create mode 100644 libvips/boolean/bool_dispatch.c create mode 100644 libvips/boolean/boolean.c create mode 100644 libvips/cimg/CImg.h create mode 100644 libvips/cimg/Makefile.am create mode 100644 libvips/cimg/cimg.cpp create mode 100644 libvips/cimg/cimg_dispatch.c create mode 100644 libvips/colour/Makefile.am create mode 100644 libvips/colour/colour.c create mode 100644 libvips/colour/colour_dispatch.c create mode 100644 libvips/colour/derived.c create mode 100644 libvips/colour/im_LCh2Lab.c create mode 100644 libvips/colour/im_LCh2UCS.c create mode 100644 libvips/colour/im_Lab2LCh.c create mode 100644 libvips/colour/im_Lab2LabQ.c create mode 100644 libvips/colour/im_Lab2LabS.c create mode 100644 libvips/colour/im_Lab2XYZ.c create mode 100644 libvips/colour/im_LabQ2Lab.c create mode 100644 libvips/colour/im_LabQ2LabS.c create mode 100644 libvips/colour/im_LabQ2disp.c create mode 100644 libvips/colour/im_LabS2Lab.c create mode 100644 libvips/colour/im_LabS2LabQ.c create mode 100644 libvips/colour/im_UCS2LCh.c create mode 100644 libvips/colour/im_XYZ2Lab.c create mode 100644 libvips/colour/im_XYZ2Yxy.c create mode 100644 libvips/colour/im_XYZ2disp.c create mode 100644 libvips/colour/im_Yxy2XYZ.c create mode 100644 libvips/colour/im_dE00_fromLab.c create mode 100644 libvips/colour/im_dECMC_fromLab.c create mode 100644 libvips/colour/im_dE_fromLab.c create mode 100644 libvips/colour/im_disp2XYZ.c create mode 100644 libvips/colour/im_float2rad.c create mode 100644 libvips/colour/im_icc_transform.c create mode 100644 libvips/colour/im_lab_morph.c create mode 100644 libvips/colour/im_rad2float.c create mode 100644 libvips/conversion/Makefile.am create mode 100644 libvips/conversion/conver_dispatch.c create mode 100644 libvips/conversion/im_bandjoin.c create mode 100644 libvips/conversion/im_bernd.c create mode 100644 libvips/conversion/im_black.c create mode 100644 libvips/conversion/im_c2amph.c create mode 100644 libvips/conversion/im_c2imag.c create mode 100644 libvips/conversion/im_c2ps.c create mode 100644 libvips/conversion/im_c2real.c create mode 100644 libvips/conversion/im_c2rect.c create mode 100644 libvips/conversion/im_clip.c create mode 100644 libvips/conversion/im_copy.c create mode 100644 libvips/conversion/im_extract.c create mode 100644 libvips/conversion/im_falsecolour.c create mode 100644 libvips/conversion/im_fliphor.c create mode 100644 libvips/conversion/im_flipver.c create mode 100644 libvips/conversion/im_gbandjoin.c create mode 100644 libvips/conversion/im_grid.c create mode 100644 libvips/conversion/im_insert.c create mode 100644 libvips/conversion/im_lrjoin.c create mode 100644 libvips/conversion/im_mask2vips.c create mode 100644 libvips/conversion/im_msb.c create mode 100644 libvips/conversion/im_print.c create mode 100644 libvips/conversion/im_recomb.c create mode 100644 libvips/conversion/im_replicate.c create mode 100644 libvips/conversion/im_ri2c.c create mode 100644 libvips/conversion/im_rightshift_size.c create mode 100644 libvips/conversion/im_rot180.c create mode 100644 libvips/conversion/im_rot270.c create mode 100644 libvips/conversion/im_rot90.c create mode 100644 libvips/conversion/im_scale.c create mode 100644 libvips/conversion/im_scaleps.c create mode 100644 libvips/conversion/im_slice.c create mode 100644 libvips/conversion/im_subsample.c create mode 100644 libvips/conversion/im_system.c create mode 100644 libvips/conversion/im_tbjoin.c create mode 100644 libvips/conversion/im_text.c create mode 100644 libvips/conversion/im_thresh.c create mode 100644 libvips/conversion/im_vips2mask.c create mode 100644 libvips/conversion/im_wrap.c create mode 100644 libvips/conversion/im_zoom.c create mode 100644 libvips/convolution/Makefile.am create mode 100644 libvips/convolution/convol_dispatch.c create mode 100644 libvips/convolution/im_addgnoise.c create mode 100644 libvips/convolution/im_compass.c create mode 100644 libvips/convolution/im_contrast_surface.c create mode 100644 libvips/convolution/im_conv.c create mode 100644 libvips/convolution/im_convf.c create mode 100644 libvips/convolution/im_convsep.c create mode 100644 libvips/convolution/im_convsepf.c create mode 100644 libvips/convolution/im_convsub.c create mode 100644 libvips/convolution/im_embed.c create mode 100644 libvips/convolution/im_fastcor.c create mode 100644 libvips/convolution/im_gaussmasks.c create mode 100644 libvips/convolution/im_gaussnoise.c create mode 100644 libvips/convolution/im_gradcor.c create mode 100644 libvips/convolution/im_logmasks.c create mode 100644 libvips/convolution/im_mpercent.c create mode 100644 libvips/convolution/im_phasecor_fft.c create mode 100644 libvips/convolution/im_rank.c create mode 100644 libvips/convolution/im_rank_image.c create mode 100644 libvips/convolution/im_resize_linear.c create mode 100644 libvips/convolution/im_sharpen.c create mode 100644 libvips/convolution/im_shrink.c create mode 100644 libvips/convolution/im_spcor.c create mode 100644 libvips/convolution/im_stretch3.c create mode 100644 libvips/convolution/im_zerox.c create mode 100644 libvips/convolution/rotmask.c create mode 100644 libvips/convolution/rw_mask.c create mode 100644 libvips/dummy.c create mode 100644 libvips/dummy2.cc create mode 100644 libvips/format/Makefile.am create mode 100644 libvips/format/dbh.h create mode 100644 libvips/format/format.c create mode 100644 libvips/format/format_dispatch.c create mode 100644 libvips/format/im_analyze2vips.c create mode 100644 libvips/format/im_csv2vips.c create mode 100644 libvips/format/im_exr2vips.c create mode 100644 libvips/format/im_file2vips.c create mode 100644 libvips/format/im_jpeg2vips.c create mode 100644 libvips/format/im_magick2vips.c create mode 100644 libvips/format/im_png2vips.c create mode 100644 libvips/format/im_ppm2vips.c create mode 100644 libvips/format/im_raw2vips.c create mode 100644 libvips/format/im_tiff2vips.c create mode 100644 libvips/format/im_tile_cache.c create mode 100644 libvips/format/im_vips2csv.c create mode 100644 libvips/format/im_vips2jpeg.c create mode 100644 libvips/format/im_vips2png.c create mode 100644 libvips/format/im_vips2ppm.c create mode 100644 libvips/format/im_vips2raw.c create mode 100644 libvips/format/im_vips2tiff.c create mode 100644 libvips/format/matlab.c create mode 100644 libvips/format/radiance.c create mode 100644 libvips/freq_filt/Makefile.am create mode 100644 libvips/freq_filt/fft_sp.c create mode 100644 libvips/freq_filt/fmask4th.c create mode 100644 libvips/freq_filt/fmaskcir.c create mode 100644 libvips/freq_filt/freq_dispatch.c create mode 100644 libvips/freq_filt/im_disp_ps.c create mode 100644 libvips/freq_filt/im_fractsurf.c create mode 100644 libvips/freq_filt/im_freq_mask.c create mode 100644 libvips/freq_filt/im_freqflt.c create mode 100644 libvips/freq_filt/im_fwfft.c create mode 100644 libvips/freq_filt/im_invfft.c create mode 100644 libvips/freq_filt/im_invfftr.c create mode 100644 libvips/freq_filt/im_rotquad.c create mode 100644 libvips/histograms_lut/Makefile.am create mode 100644 libvips/histograms_lut/hist_dispatch.c create mode 100644 libvips/histograms_lut/im_buildlut.c create mode 100644 libvips/histograms_lut/im_gammacorrect.c create mode 100644 libvips/histograms_lut/im_heq.c create mode 100644 libvips/histograms_lut/im_hist.c create mode 100644 libvips/histograms_lut/im_histeq.c create mode 100644 libvips/histograms_lut/im_histgr.c create mode 100644 libvips/histograms_lut/im_histnD.c create mode 100644 libvips/histograms_lut/im_histplot.c create mode 100644 libvips/histograms_lut/im_histspec.c create mode 100644 libvips/histograms_lut/im_hsp.c create mode 100644 libvips/histograms_lut/im_identity.c create mode 100644 libvips/histograms_lut/im_invertlut.c create mode 100644 libvips/histograms_lut/im_lhisteq.c create mode 100644 libvips/histograms_lut/im_maplut.c create mode 100644 libvips/histograms_lut/im_project.c create mode 100644 libvips/histograms_lut/im_stdif.c create mode 100644 libvips/histograms_lut/tone.c create mode 100644 libvips/include/Makefile.am create mode 100644 libvips/include/vips/Makefile.am create mode 100644 libvips/include/vips/almostdeprecated.h create mode 100644 libvips/include/vips/arithmetic.h create mode 100644 libvips/include/vips/buf.h create mode 100644 libvips/include/vips/colour.h create mode 100644 libvips/include/vips/debug.h create mode 100644 libvips/include/vips/deprecated.h create mode 100644 libvips/include/vips/dispatch.h create mode 100644 libvips/include/vips/fmask.h create mode 100644 libvips/include/vips/format.h create mode 100644 libvips/include/vips/image.h create mode 100644 libvips/include/vips/inlines.h create mode 100644 libvips/include/vips/internal.h create mode 100644 libvips/include/vips/interpolate.h create mode 100644 libvips/include/vips/intl.h create mode 100644 libvips/include/vips/meta.h create mode 100644 libvips/include/vips/mosaic.h create mode 100644 libvips/include/vips/object.h create mode 100644 libvips/include/vips/private.h create mode 100644 libvips/include/vips/proto.h create mode 100644 libvips/include/vips/r_access.h create mode 100644 libvips/include/vips/rect.h create mode 100644 libvips/include/vips/region.h create mode 100644 libvips/include/vips/semaphore.h create mode 100644 libvips/include/vips/struct.h create mode 100644 libvips/include/vips/thread.h create mode 100644 libvips/include/vips/threadgroup.h create mode 100644 libvips/include/vips/transform.h create mode 100644 libvips/include/vips/type.h create mode 100644 libvips/include/vips/util.h create mode 100644 libvips/include/vips/version.h.in create mode 100644 libvips/include/vips/vips.h create mode 100644 libvips/inplace/Makefile.am create mode 100644 libvips/inplace/im_circle.c create mode 100644 libvips/inplace/im_flood.c create mode 100644 libvips/inplace/im_insertplace.c create mode 100644 libvips/inplace/im_line.c create mode 100644 libvips/inplace/im_paintrect.c create mode 100644 libvips/inplace/im_plotmask.c create mode 100644 libvips/inplace/inplace_dispatch.c create mode 100644 libvips/inplace/line_draw.c create mode 100644 libvips/inplace/plot_point.c create mode 100644 libvips/inplace/smudge_area.c create mode 100644 libvips/iofuncs/Makefile.am create mode 100644 libvips/iofuncs/base64.c create mode 100644 libvips/iofuncs/base64.h create mode 100644 libvips/iofuncs/buf.c create mode 100644 libvips/iofuncs/buffer.c create mode 100644 libvips/iofuncs/callback.c create mode 100644 libvips/iofuncs/debug.c create mode 100644 libvips/iofuncs/dispatch_types.c create mode 100644 libvips/iofuncs/error.c create mode 100644 libvips/iofuncs/error_exit.c create mode 100644 libvips/iofuncs/im_binfile.c create mode 100644 libvips/iofuncs/im_bits_of_fmt.c create mode 100644 libvips/iofuncs/im_close.c create mode 100644 libvips/iofuncs/im_cp_desc.c create mode 100644 libvips/iofuncs/im_debugim.c create mode 100644 libvips/iofuncs/im_demand_hint.c create mode 100644 libvips/iofuncs/im_generate.c create mode 100644 libvips/iofuncs/im_guess_prefix.c create mode 100644 libvips/iofuncs/im_header.c create mode 100644 libvips/iofuncs/im_histlin.c create mode 100644 libvips/iofuncs/im_image.c create mode 100644 libvips/iofuncs/im_init.c create mode 100644 libvips/iofuncs/im_init_world.c create mode 100644 libvips/iofuncs/im_initdesc.c create mode 100644 libvips/iofuncs/im_iocheck.c create mode 100644 libvips/iofuncs/im_iterate.c create mode 100644 libvips/iofuncs/im_makerw.c create mode 100644 libvips/iofuncs/im_mapfile.c create mode 100644 libvips/iofuncs/im_open.c create mode 100644 libvips/iofuncs/im_open_vips.c create mode 100644 libvips/iofuncs/im_partial.c create mode 100644 libvips/iofuncs/im_piocheck.c create mode 100644 libvips/iofuncs/im_prepare.c create mode 100644 libvips/iofuncs/im_printdesc.c create mode 100644 libvips/iofuncs/im_printlines.c create mode 100644 libvips/iofuncs/im_render.c create mode 100644 libvips/iofuncs/im_setbox.c create mode 100644 libvips/iofuncs/im_setbuf.c create mode 100644 libvips/iofuncs/im_setupout.c create mode 100644 libvips/iofuncs/im_unmapfile.c create mode 100644 libvips/iofuncs/im_updatehist.c create mode 100644 libvips/iofuncs/im_wbuffer.c create mode 100644 libvips/iofuncs/im_wrapmany.c create mode 100644 libvips/iofuncs/im_wraptwo.c create mode 100644 libvips/iofuncs/im_writeline.c create mode 100644 libvips/iofuncs/memory.c create mode 100644 libvips/iofuncs/meta.c create mode 100644 libvips/iofuncs/object.c create mode 100644 libvips/iofuncs/package.c create mode 100644 libvips/iofuncs/predicate.c create mode 100644 libvips/iofuncs/rect.c create mode 100644 libvips/iofuncs/region.c create mode 100644 libvips/iofuncs/semaphore.c create mode 100644 libvips/iofuncs/threadgroup.c create mode 100644 libvips/iofuncs/time.c create mode 100644 libvips/iofuncs/type.c create mode 100644 libvips/iofuncs/util.c create mode 100644 libvips/iofuncs/window.c create mode 100644 libvips/matrix/Makefile.am create mode 100644 libvips/matrix/im_matcat.c create mode 100644 libvips/matrix/im_matinv.c create mode 100644 libvips/matrix/im_matmul.c create mode 100644 libvips/matrix/im_mattrn.c create mode 100644 libvips/matrix/matalloc.c create mode 100644 libvips/matrix/matrix_dispatch.c create mode 100644 libvips/morphology/Makefile.am create mode 100644 libvips/morphology/im_cntlines.c create mode 100644 libvips/morphology/im_dilate.c create mode 100644 libvips/morphology/im_erode.c create mode 100644 libvips/morphology/im_profile.c create mode 100644 libvips/morphology/morph_dispatch.c create mode 100644 libvips/mosaicing/Makefile.am create mode 100644 libvips/mosaicing/global_balance.c create mode 100644 libvips/mosaicing/global_balance.h create mode 100644 libvips/mosaicing/im_align_bands.c create mode 100644 libvips/mosaicing/im_avgdxdy.c create mode 100644 libvips/mosaicing/im_chkpair.c create mode 100644 libvips/mosaicing/im_clinear.c create mode 100644 libvips/mosaicing/im_improve.c create mode 100644 libvips/mosaicing/im_initialize.c create mode 100644 libvips/mosaicing/im_lrcalcon.c create mode 100644 libvips/mosaicing/im_lrmerge.c create mode 100644 libvips/mosaicing/im_lrmosaic.c create mode 100644 libvips/mosaicing/im_maxpos_subpel.c create mode 100644 libvips/mosaicing/im_remosaic.c create mode 100644 libvips/mosaicing/im_tbcalcon.c create mode 100644 libvips/mosaicing/im_tbmerge.c create mode 100644 libvips/mosaicing/im_tbmosaic.c create mode 100644 libvips/mosaicing/match.c create mode 100644 libvips/mosaicing/merge.h create mode 100644 libvips/mosaicing/mosaic.h create mode 100644 libvips/mosaicing/mosaic1.c create mode 100644 libvips/mosaicing/mosaicing_dispatch.c create mode 100644 libvips/other/Makefile.am create mode 100644 libvips/other/cooc_funcs.c create mode 100644 libvips/other/glds_funcs.c create mode 100644 libvips/other/im_benchmark.c create mode 100644 libvips/other/im_dif_std.c create mode 100644 libvips/other/im_eye.c create mode 100644 libvips/other/im_grey.c create mode 100644 libvips/other/im_make_xy.c create mode 100644 libvips/other/im_meanstd.c create mode 100644 libvips/other/im_simcontr.c create mode 100644 libvips/other/im_sines.c create mode 100644 libvips/other/im_spatres.c create mode 100644 libvips/other/im_zone.c create mode 100644 libvips/other/other_dispatch.c create mode 100644 libvips/relational/Makefile.am create mode 100644 libvips/relational/im_blend.c create mode 100644 libvips/relational/im_ifthenelse.c create mode 100644 libvips/relational/relational.c create mode 100644 libvips/relational/relational_dispatch.c create mode 100644 libvips/resample/Makefile.am create mode 100644 libvips/resample/bicubic.cpp create mode 100644 libvips/resample/im_affine.c create mode 100644 libvips/resample/interpolate.c create mode 100644 libvips/resample/nohalo1.cpp create mode 100644 libvips/resample/nohalo2.cpp create mode 100644 libvips/resample/resample_dispatch.c create mode 100644 libvips/resample/similarity.c create mode 100644 libvips/resample/snohalo1.cpp create mode 100644 libvips/resample/templates.h create mode 100644 libvips/resample/transform.c create mode 100644 libvips/resample/yafrsmooth.cpp create mode 100644 libvips/video/Makefile.am create mode 100644 libvips/video/im_video_test.c create mode 100644 libvips/video/im_video_v4l1.c create mode 100644 libvips/video/im_video_v4l1.h create mode 100644 libvips/video/video_dispatch.c create mode 100644 libvipsCC/Makefile.am create mode 100644 libvipsCC/VDisplay.cc create mode 100644 libvipsCC/VError.cc create mode 100644 libvipsCC/VImage.cc create mode 100644 libvipsCC/VMask.cc create mode 100644 libvipsCC/include/Makefile.am create mode 100644 libvipsCC/include/vipsCC/Makefile.am create mode 100644 libvipsCC/include/vipsCC/VDisplay.h create mode 100644 libvipsCC/include/vipsCC/VError.h create mode 100644 libvipsCC/include/vipsCC/VImage.h create mode 100644 libvipsCC/include/vipsCC/VMask.h create mode 100644 libvipsCC/include/vipsCC/vips create mode 100644 libvipsCC/include/vipsCC/vipsc++.h create mode 100644 libvipsCC/include/vipsCC/vipscpp.h create mode 100644 libvipsCC/vipsc++.cc create mode 100644 swig/Makefile.am create mode 100755 swig/test/bench_pil.py create mode 100755 swig/test/bench_vips.py create mode 100755 swig/test/pilvips.py create mode 100755 swig/test/testvipsCC.py create mode 100644 swig/vipsCC/Makefile.am create mode 100644 swig/vipsCC/VDisplay.i create mode 100644 swig/vipsCC/VError.i create mode 100644 swig/vipsCC/VImage.i create mode 100644 swig/vipsCC/VMask.i create mode 100644 swig/vipsCC/__init__.py create mode 100644 tools/Makefile.am create mode 100644 tools/iofuncs/Makefile.am create mode 100644 tools/iofuncs/binfile.c create mode 100644 tools/iofuncs/debugim.c create mode 100644 tools/iofuncs/edvips.c create mode 100644 tools/iofuncs/header.c create mode 100644 tools/iofuncs/printlines.c create mode 100644 tools/iofuncs/vips.c create mode 100644 tools/mosaicing/Makefile.am create mode 100644 tools/mosaicing/find_mosaic.c create mode 100644 tools/mosaicing/mergeup.c create mode 100644 tools/other/Makefile.am create mode 100644 tools/other/cooc.c create mode 100644 tools/other/cooc_features.c create mode 100644 tools/other/glds.c create mode 100644 tools/other/glds_features.c create mode 100644 tools/other/simcontr.c create mode 100644 tools/other/sines.c create mode 100644 tools/other/spatres.c create mode 100644 tools/other/squares.c create mode 100644 tools/scripts/Makefile.am create mode 100644 tools/scripts/batch_crop.in create mode 100644 tools/scripts/batch_image_convert.in create mode 100644 tools/scripts/batch_rubber_sheet.in create mode 100644 tools/scripts/light_correct.in create mode 100755 tools/scripts/post_install create mode 100644 tools/scripts/shrink_width.in create mode 100755 tools/scripts/vips-7.19 diff --git a/ChangeLog b/ChangeLog index 4c5f5675..9ae3b73a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,11 @@ - now (more or less) passes -Wextra - added "fail" option to im_jpeg2vips: fail with an error on any warning (thank you Ole) +- started gtk-doc changes +- renamed im_meta_get_type() and im_header_get_type() as + im_meta_get_typeof() and im_header_get_typeof() to prevent confusion with + GObject type definers (was breaking gtkdoc object scan) +- revised more names, limited documented API - im_buildlut() could segv for non-zero based tables (thanks Jack) - VIPS_BUF_STATIC() does not take length arg - check for SetImageOption() so we work with GraphicsMagick too diff --git a/Makefile.am b/Makefile.am index c3da22fa..fe2dae28 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,24 +1,24 @@ # only build in the python dir if we can if HAVE_PYTHON -P_COMPILE_DIR = python +P_COMPILE_DIR = swig P_DIST_DIR = else P_COMPILE_DIR = -P_DIST_DIR = python +P_DIST_DIR = swig endif SUBDIRS = \ - libsrc \ - src \ - include \ - libsrcCC \ + libvips \ + tools \ + libvipsCC \ contrib \ man \ po \ + doc \ $(P_COMPILE_DIR) EXTRA_DIST = \ - doc \ + m4 \ benchmark \ bootstrap.sh \ vipsCC-7.${IM_MINOR_VERSION}.pc.in \ @@ -45,3 +45,4 @@ uninstall-hook: -chmod -R u+w ${DESTDIR}$(datadir)/doc/vips -rm -rf ${DESTDIR}$(datadir)/doc/vips +DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc diff --git a/TODO b/TODO index a4da98eb..c6526f8c 100644 --- a/TODO +++ b/TODO @@ -36,7 +36,59 @@ - COPYInG file should be lgpl2.1+? seems to be plain 2.1 atm -- look at gtk-doc? installed now +- need a separate sectrion in ref docs for vips, vipsCC (and python?) + +- try the new liboil thing: + + http://www.schleef.org/orc/ + + pick something like abs and time it + + +- im__cast_and_call() no longer does + + out->Bbits = im_bits_of_fmt( out->BandFmt ); + + make sure we do this for each use of im__cast_and_call() + +- im__cast_and_call() no longer casts input to output type, it casts inputs + up to the smallest common type + + so we need to change all the buffer processors from + + T op T -> T + + to + + T1 op T1 -> T2 + + + + + + +- split proto.h into headers for arithmetic etc. + +- move headers into libsrc/arithmetic, can we get them installed into the + right prefix? + +- move VImage.h into libsrcCC + +- rename libsrc as libvips + +- rename src as vipstools + +- rename libsrcCC as libvipsCC + +- rename python as SWIG + +- rename vipsCC in SWIG as pyvips + + + + + + - try the new liboil thing: diff --git a/bootstrap.sh b/bootstrap.sh index 1ed3e0b7..d5b9a7fb 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -8,10 +8,13 @@ rm -rf autom4te.cache rm -f config.* configure depcomp rm -f install-sh intltool-* libtool ltmain.sh missing mkinstalldirs rm -f stamp-* vipsCC-7.19.pc vips-7.19.spec vips-7.19.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 swig/vipsCC/*.cxx +rm -f swig/vipsCC/VImage.h +rm -f swig/vipsCC/VImage.py python/vipsCC/VError.py python/vipsCC/VMask.py python/vipsCC/Display.py rm -f benchmark/temp* +( cd doc ; mkdir poop ; mv reference/VIPS-docs.sgml.in poop ; mv reference/Makefile.am poop ; rm -rf reference/* ; mv poop/* reference ; rmdir poop ) + +gtkdocize --copy --docdir doc/reference --flavour no-tmpl || exit 1 # some systems need libtoolize, some glibtoolize ... how annoying echo testing for glibtoolize ... @@ -32,4 +35,3 @@ autoconf autoheader $LIBTOOLIZE --copy --force --automake automake --add-missing --copy - diff --git a/configure.in b/configure.in index 72841f2f..39e8c53d 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,7 @@ # Process this file with autoconf to produce a configure script. -AC_INIT(include/vips/colour.h) +AC_INIT(libvips/include/vips/colour.h) AM_CONFIG_HEADER(config.h) +AC_CONFIG_MACRO_DIR(m4) # user-visible library versioning IM_MAJOR_VERSION=7 @@ -204,6 +205,9 @@ if test x"$enable_threads" != "xno"; then enable_threads=yes fi +# check for gtk-doc +GTK_DOC_CHECK(1.9) + # optional supporting libraries # we can wrap fftw3 and fftw2 ... but just look for fftw3, since we can do @@ -494,41 +498,42 @@ AC_OUTPUT([ vipsCC-7.19.pc vips-7.19.spec Makefile - include/vips/version.h - include/Makefile - include/vips/Makefile - libsrc/Makefile - libsrc/acquire/Makefile - libsrc/arithmetic/Makefile - libsrc/boolean/Makefile - libsrc/cimg/Makefile - libsrc/colour/Makefile - libsrc/conversion/Makefile - libsrc/convolution/Makefile - libsrc/format/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/resample/Makefile - libsrc/video/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 - man/Makefile + libvips/include/vips/version.h + libvips/include/Makefile + libvips/include/vips/Makefile + libvips/Makefile + libvips/acquire/Makefile + libvips/arithmetic/Makefile + libvips/boolean/Makefile + libvips/cimg/Makefile + libvips/colour/Makefile + libvips/conversion/Makefile + libvips/convolution/Makefile + libvips/format/Makefile + libvips/freq_filt/Makefile + libvips/histograms_lut/Makefile + libvips/inplace/Makefile + libvips/iofuncs/Makefile + libvips/matrix/Makefile + libvips/morphology/Makefile + libvips/mosaicing/Makefile + libvips/other/Makefile + libvips/relational/Makefile + libvips/resample/Makefile + libvips/video/Makefile + libvipsCC/include/Makefile + libvipsCC/include/vipsCC/Makefile + libvipsCC/Makefile + tools/Makefile + tools/iofuncs/Makefile + tools/mosaicing/Makefile + tools/other/Makefile + tools/scripts/Makefile + tools/scripts/batch_crop + tools/scripts/batch_image_convert + tools/scripts/batch_rubber_sheet + tools/scripts/light_correct + tools/scripts/shrink_width contrib/Makefile contrib/vips2dj/Makefile contrib/vips2dj/share/Makefile @@ -538,8 +543,12 @@ AC_OUTPUT([ contrib/vips2dj/share/vips2dj/mono/Makefile contrib/vdump/Makefile contrib/mitsub/Makefile - python/Makefile - python/vipsCC/Makefile + swig/Makefile + swig/vipsCC/Makefile + man/Makefile + doc/Makefile + doc/reference/Makefile + doc/reference/VIPS-docs.sgml po/Makefile.in ]) @@ -549,6 +558,7 @@ native win32: $vips_os_win32 open files in binary mode: $vips_binary_open evaluate with threads: $enable_threads make symlinks for commands in bin: $enable_links +build docs with gtkdoc $enable_gtk_doc * optional packages and modules use fftw3 for FFT: $with_fftw3 diff --git a/contrib/mitsub/Makefile.am b/contrib/mitsub/Makefile.am index 79ccae56..624727ea 100644 --- a/contrib/mitsub/Makefile.am +++ b/contrib/mitsub/Makefile.am @@ -2,6 +2,6 @@ bin_PROGRAMS = mitsub mitsub_SOURCES = mitsub.c -INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ AM_LDFLAGS = @LDFLAGS@ -LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libvips/libvips.la @VIPS_LIBS@ diff --git a/contrib/vdump/Makefile.am b/contrib/vdump/Makefile.am index 49113dd0..392477b0 100644 --- a/contrib/vdump/Makefile.am +++ b/contrib/vdump/Makefile.am @@ -8,8 +8,8 @@ man_MANS = \ pkgdata_DATA = \ vdump.pro -INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ AM_LDFLAGS = @LDFLAGS@ -LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libvips/libvips.la @VIPS_LIBS@ EXTRA_DIST = $(pkgdata_DATA) $(man_MANS) diff --git a/contrib/vips2dj/Makefile.am b/contrib/vips2dj/Makefile.am index 7c982e3c..81a4307a 100644 --- a/contrib/vips2dj/Makefile.am +++ b/contrib/vips2dj/Makefile.am @@ -9,9 +9,9 @@ vips2dj_SOURCES = \ vips2ah.c \ vips2dj.c -INCLUDES = -I${top_srcdir}/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ AM_LDFLAGS = @LDFLAGS@ -LDADD = @VIPS_CFLAGS@ ${top_builddir}/libsrc/libvips.la @VIPS_LIBS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libvips/libvips.la @VIPS_LIBS@ EXTRA_DIST = ${vips2dj_DEPENDENCIES} diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index c2f953ee..00000000 --- a/doc/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# - -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/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..bb4cabcc --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,10 @@ + +SUBDIRS = reference + +EXTRA_DIST = \ + html \ + pdf \ + src \ + makefile.tex \ + Makehtmlman \ + README diff --git a/doc/README b/doc/README index 9054eb38..7adb4c11 100644 --- a/doc/README +++ b/doc/README @@ -1,6 +1,6 @@ VIPS documentation -edit Makefile and change "prefix" to be your install prefix (eg. +edit makefile.tex 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 diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am new file mode 100644 index 00000000..83dc8192 --- /dev/null +++ b/doc/reference/Makefile.am @@ -0,0 +1,101 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=VIPS + +# Uncomment for versioned docs and specify the version of the module, e.g. '2'. +DOC_MODULE_VERSION=7 + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=$(top_srcdir)/libvips + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS=--rebuild-types + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-mkhtml +MKHTML_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(top_srcdir)/libvips/include/vips/*.h +CFILE_GLOB=$(top_srcdir)/libvips/*/*.c + +# Extra header to include when scanning, which are not under DOC_SOURCE_DIR +# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h +EXTRA_HFILES= + +# Header files to ignore when scanning. Use base file name, no paths +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES=merge.h debug.h internal.h intl.h CImg.h im_video_v4l1.h global_balance.h dbh.h base64.h templates.h mosaic.h deprecated.h thread.h private.h internal.h almostdeprecated.h fmask.h inlines.h r_access.h struct.h + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files= + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +# (use VIPS_CFLAGS/VIPS_LIBS/VIPS_LIBS so we pick up the glib-object.h that +# the scanner uses) +GTKDOC_CFLAGS = @VIPS_CFLAGS@ @VIPS_INCLUDES@ +GTKDOC_LIBS = @VIPS_CFLAGS@ ${top_builddir}/libvips/libvips.la @VIPS_LIBS@ + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +#EXTRA_DIST += + +# Files not to distribute +# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types +# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt +DISTCLEANFILES = VIPS.types + +# Comment this out if you want your docs-status tested during 'make check' +if ENABLE_GTK_DOC +#TESTS_ENVIRONMENT = cd $(srcsrc) +#TESTS = $(GTKDOC_CHECK) +endif diff --git a/doc/reference/VIPS-docs.sgml b/doc/reference/VIPS-docs.sgml new file mode 100644 index 00000000..befddfbe --- /dev/null +++ b/doc/reference/VIPS-docs.sgml @@ -0,0 +1,50 @@ + + +]> + + + VIPS Reference Manual + + for VIPS 7.19.0 + The latest version of this documentation can be found on-line at + http://www.vips.ecs.soton.ac.uk/index.php?title=Documentation. + + + + + API by section + + + + + + + + + + + + + + + + + + + + + + Object Hierarchy + + + + API Index + + + + + diff --git a/doc/reference/VIPS-docs.sgml.in b/doc/reference/VIPS-docs.sgml.in new file mode 100644 index 00000000..9906a391 --- /dev/null +++ b/doc/reference/VIPS-docs.sgml.in @@ -0,0 +1,50 @@ + + +]> + + + VIPS Reference Manual + + for VIPS @IM_VERSION@ + The latest version of this documentation can be found on-line at + http://www.vips.ecs.soton.ac.uk/index.php?title=Documentation. + + + + + API by section + + + + + + + + + + + + + + + + + + + + + + Object Hierarchy + + + + API Index + + + + + diff --git a/gtk-doc.make b/gtk-doc.make new file mode 100644 index 00000000..ed29f94d --- /dev/null +++ b/gtk-doc.make @@ -0,0 +1,177 @@ +# -*- mode: makefile -*- + +#################################### +# Everything below here is generic # +#################################### + +if GTK_DOC_USE_LIBTOOL +GTKDOC_CC = $(LIBTOOL) --mode=compile $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +GTKDOC_LD = $(LIBTOOL) --mode=link $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) +GTKDOC_RUN = $(LIBTOOL) --mode=execute +else +GTKDOC_CC = $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +GTKDOC_LD = $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) +GTKDOC_RUN = sh -c +endif + +# We set GPATH here; this gives us semantics for GNU make +# which are more like other make's VPATH, when it comes to +# whether a source that is a target of one rule is then +# searched for in VPATH/GPATH. +# +GPATH = $(srcdir) + +TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE) + +EXTRA_DIST = \ + $(content_files) \ + $(HTML_IMAGES) \ + $(DOC_MAIN_SGML_FILE) \ + $(DOC_MODULE)-sections.txt \ + $(DOC_MODULE)-overrides.txt + +DOC_STAMPS=scan-build.stamp sgml-build.stamp html-build.stamp \ + $(srcdir)/sgml.stamp $(srcdir)/html.stamp + +SCANOBJ_FILES = \ + $(DOC_MODULE).args \ + $(DOC_MODULE).hierarchy \ + $(DOC_MODULE).interfaces \ + $(DOC_MODULE).prerequisites \ + $(DOC_MODULE).signals + +REPORT_FILES = \ + $(DOC_MODULE)-undocumented.txt \ + $(DOC_MODULE)-undeclared.txt \ + $(DOC_MODULE)-unused.txt + +CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) + +if ENABLE_GTK_DOC +all-local: html-build.stamp +else +all-local: +endif + +docs: html-build.stamp + +$(REPORT_FILES): sgml-build.stamp + +#### scan #### + +scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB) + @echo 'gtk-doc: Scanning header files' + @-chmod -R u+w $(srcdir) + cd $(srcdir) && \ + gtkdoc-scan --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --ignore-headers="$(IGNORE_HFILES)" $(SCAN_OPTIONS) $(EXTRA_HFILES) + if grep -l '^..*$$' $(srcdir)/$(DOC_MODULE).types > /dev/null 2>&1 ; then \ + CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" gtkdoc-scangobj $(SCANGOBJ_OPTIONS) --module=$(DOC_MODULE) --output-dir=$(srcdir) ; \ + else \ + cd $(srcdir) ; \ + for i in $(SCANOBJ_FILES) ; do \ + test -f $$i || touch $$i ; \ + done \ + fi + touch scan-build.stamp + +$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp + @true + +#### xml #### + +sgml-build.stamp: $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt $(expand_content_files) + @echo 'gtk-doc: Building XML' + @-chmod -R u+w $(srcdir) + cd $(srcdir) && \ + gtkdoc-mkdb --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $(MKDB_OPTIONS) + touch sgml-build.stamp + +sgml.stamp: sgml-build.stamp + @true + +#### html #### + +html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) + @echo 'gtk-doc: Building HTML' + @-chmod -R u+w $(srcdir) + rm -rf $(srcdir)/html + mkdir $(srcdir)/html + mkhtml_options=""; \ + gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \ + if test "$(?)" = "0"; then \ + mkhtml_options=--path="$(srcdir)"; \ + fi + cd $(srcdir)/html && gtkdoc-mkhtml $(mkhtml_options) $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE) + test "x$(HTML_IMAGES)" = "x" || ( cd $(srcdir) && cp $(HTML_IMAGES) html ) + @echo 'gtk-doc: Fixing cross-references' + cd $(srcdir) && gtkdoc-fixxref --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS) + touch html-build.stamp + +############## + +clean-local: + rm -f *~ *.bak + rm -rf .libs + +distclean-local: + cd $(srcdir) && \ + rm -rf xml $(REPORT_FILES) \ + $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt + +maintainer-clean-local: clean + cd $(srcdir) && rm -rf html + +install-data-local: + installfiles=`echo $(srcdir)/html/*`; \ + if test "$$installfiles" = '$(srcdir)/html/*'; \ + then echo '-- Nothing to install' ; \ + else \ + if test -n "$(DOC_MODULE_VERSION)"; then \ + installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ + else \ + installdir="$(DESTDIR)$(TARGET_DIR)"; \ + fi; \ + $(mkinstalldirs) $${installdir} ; \ + for i in $$installfiles; do \ + echo '-- Installing '$$i ; \ + $(INSTALL_DATA) $$i $${installdir}; \ + done; \ + if test -n "$(DOC_MODULE_VERSION)"; then \ + mv -f $${installdir}/$(DOC_MODULE).devhelp2 \ + $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \ + mv -f $${installdir}/$(DOC_MODULE).devhelp \ + $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp; \ + fi; \ + ! which gtkdoc-rebase >/dev/null 2>&1 || \ + gtkdoc-rebase --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir} ; \ + fi + +uninstall-local: + if test -n "$(DOC_MODULE_VERSION)"; then \ + installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ + else \ + installdir="$(DESTDIR)$(TARGET_DIR)"; \ + fi; \ + rm -rf $${installdir} + +# +# Require gtk-doc when making dist +# +if ENABLE_GTK_DOC +dist-check-gtkdoc: +else +dist-check-gtkdoc: + @echo "*** gtk-doc must be installed and enabled in order to make dist" + @false +endif + +dist-hook: dist-check-gtkdoc dist-hook-local + mkdir $(distdir)/html + cp $(srcdir)/html/* $(distdir)/html + -cp $(srcdir)/$(DOC_MODULE).types $(distdir)/ + -cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/ + cd $(distdir) && rm -f $(DISTCLEANFILES) + ! which gtkdoc-rebase >/dev/null 2>&1 || \ + gtkdoc-rebase --online --relative --html-dir=$(distdir)/html + +.PHONY : dist-hook-local docs diff --git a/libvips/Makefile.am b/libvips/Makefile.am new file mode 100644 index 00000000..42f08c4d --- /dev/null +++ b/libvips/Makefile.am @@ -0,0 +1,67 @@ +# only build in the cimg dir if it's enabled +if WITH_CIMG +C_COMPILE_DIR = cimg +C_DIST_DIR = +C_LIB = cimg/libcimg.la +else +C_COMPILE_DIR = +C_DIST_DIR = cimg +C_LIB = +endif + +SUBDIRS = \ + include \ + acquire \ + arithmetic \ + resample \ + boolean \ + colour \ + conversion \ + convolution \ + $(C_COMPILE_DIR) \ + format \ + freq_filt \ + histograms_lut \ + inplace \ + iofuncs \ + matrix \ + morphology \ + mosaicing \ + other \ + relational \ + video \ + . + +lib_LTLIBRARIES = libvips.la + +libvips_la_SOURCES = dummy.c dummy2.cc + +# DLLs need dependant libs there too ... put @VIPS_LIBS@ at the end +libvips_la_LIBADD = \ + acquire/libacquire.la \ + resample/libresample.la \ + arithmetic/libarithmetic.la \ + boolean/libboolean.la \ + colour/libcolour.la \ + conversion/libconversion.la \ + convolution/libconvolution.la \ + $(C_LIB) \ + format/libformat.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@ + +libvips_la_LDFLAGS = \ + -no-undefined \ + -version-info @LIBRARY_CURRENT@:@LIBRARY_REVISION@:@LIBRARY_AGE@ + +EXTRA_DIST = \ + $(C_DIST_DIR) diff --git a/libvips/acquire/Makefile.am b/libvips/acquire/Makefile.am new file mode 100644 index 00000000..849f96a3 --- /dev/null +++ b/libvips/acquire/Makefile.am @@ -0,0 +1,7 @@ +noinst_LTLIBRARIES = libacquire.la + +libacquire_la_SOURCES = \ + im_clamp.c + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ + diff --git a/libvips/acquire/im_clamp.c b/libvips/acquire/im_clamp.c new file mode 100644 index 00000000..86df7570 --- /dev/null +++ b/libvips/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", "%s", _( "bad input format" ) ); + return( -1 ); +} +if( black->Bbits != 8 || + black->Coding != IM_CODING_NONE || black->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_clamp", "%s", _( "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/libvips/arithmetic/Makefile.am b/libvips/arithmetic/Makefile.am new file mode 100644 index 00000000..8b1d1c98 --- /dev/null +++ b/libvips/arithmetic/Makefile.am @@ -0,0 +1,45 @@ +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_cross_phase.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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/arithmetic/arith_dispatch.c b/libvips/arithmetic/arith_dispatch.c new file mode 100644 index 00000000..b5ad1657 --- /dev/null +++ b/libvips/arithmetic/arith_dispatch.c @@ -0,0 +1,1539 @@ +/* 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*/ + +/** + * SECTION: arithmetic + * @short_description: operations which perform pixel arithmetic + * + * @see_also: iofuncs + * @stability: Stable + * @include: vips/vips.h + * + * These operations perform pixel arithmetic, that is, they perform an + * arithmetic operation, such as addition, on every pixel in an image or a + * pair of images. All (except in a few cases noted below) will work with + * images of any type (or any mixture of types), of any size and of any number + * of bands. + * + * For binary operations, if the number of bands differs, one of the images + * must have one band. So you can add a 1 band image to a 3 band image, for + * example, and the 1 band image will be added 3 times, but you can't add a + * 2 band image to a 3 band image. + * + * 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 + * %IM_BANDFMT_UCHAR images together, for example, produces a + * %IM_BANDFMT_USHORT image, and taking the im_costra() of a + * %IM_BANDFMT_USHORT image produces %IM_BANDFMT_FLOAT image. + * + * For binary arithmetic operations, type promotion occurs in two stages. + * First, the two input images are cast up to the smallest common format, + * that is, the type with the smallest range that can represent the full + * range of both inputs. This conversion can be represented as a table: + * + * + * Smallest common format + * + * + * + * @in2/@in1 + * uchar + * char + * ushort + * short + * uint + * int + * float + * double + * complex + * double complex + * + * + * + * + * uchar + * ushort + * short + * ushort + * short + * uint + * int + * float + * double + * complex + * double complex + * + * + * char + * short + * short + * short + * short + * int + * int + * float + * double + * complex + * double complex + * + * + * ushort + * ushort + * short + * ushort + * short + * uint + * int + * float + * double + * complex + * double complex + * + * + * short + * short + * short + * short + * short + * int + * int + * float + * double + * complex + * double complex + * + * + * uint + * uint + * int + * uint + * int + * uint + * int + * float + * double + * complex + * double complex + * + * + * int + * int + * int + * int + * int + * int + * int + * float + * double + * complex + * double complex + * + * + * float + * float + * float + * float + * float + * float + * float + * float + * double + * complex + * double complex + * + * + * double + * double + * double + * double + * double + * double + * double + * double + * double + * double complex + * double complex + * + * + * complex + * complex + * complex + * complex + * complex + * complex + * complex + * complex + * double complex + * complex + * double complex + * + * + * double complex + * double complex + * double complex + * double complex + * double complex + * double complex + * double complex + * double complex + * double complex + * double complex + * double complex + * + * + * + *
+ * + * In the second stage, the operation is performed between the two identical + * types to form the output. The details vary between operations, but + * generally the principle is that the output type should be large enough to + * represent the whole rage of possible values, except that int never becomes + * float. + */ + +/* 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", + "%s", _( "vectors not equal 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 to im_min/maxpos_vec. + */ +static im_arg_desc maxpos_vec_args[] = { + IM_INPUT_IMAGE ("in"), + IM_INPUT_INT ("n"), + IM_OUTPUT_INTVEC("xes"), + IM_OUTPUT_INTVEC("yes"), + IM_OUTPUT_DOUBLEVEC("maxima") +}; + +/* Call im_maxpos_vec via arg vector. + */ +static int +maxpos_vec_vec( im_object *argv ) +{ + int n = *((int *) argv[1]); + im_intvec_object *xes = argv[2]; + im_intvec_object *yes = argv[3]; + im_doublevec_object *maxima = argv[4]; + + xes->vec = IM_ARRAY( NULL, n, int ); + xes->n = n; + yes->vec = IM_ARRAY( NULL, n, int ); + yes->n = n; + maxima->vec = IM_ARRAY( NULL, n, double ); + maxima->n = n; + if( !xes->vec || !yes->vec || !maxima->vec || + im_maxpos_vec( argv[0], xes->vec, yes->vec, maxima->vec, n ) ) + return -1; + + return 0; +} + +/* Description of im_maxpos_vec. + */ +static im_function maxpos_vec_desc = { + "im_maxpos_vec", + "position and value of n maxima of image", + IM_FN_PIO, + maxpos_vec_vec, + IM_NUMBER( maxpos_vec_args ), + maxpos_vec_args +}; + +/* Call im_minpos_vec via arg vector. + */ +static int +minpos_vec_vec( im_object *argv ) +{ + int n = *((int *) argv[1]); + im_intvec_object *xes = argv[2]; + im_intvec_object *yes = argv[3]; + im_doublevec_object *minima = argv[4]; + + xes->vec = IM_ARRAY( NULL, n, int ); + xes->n = n; + yes->vec = IM_ARRAY( NULL, n, int ); + yes->n = n; + minima->vec = IM_ARRAY( NULL, n, double ); + minima->n = n; + if( !xes->vec || !yes->vec || !minima->vec || + im_minpos_vec( argv[0], xes->vec, yes->vec, minima->vec, n ) ) + return -1; + + return 0; +} + +/* Description of im_minpos_vec. + */ +static im_function minpos_vec_desc = { + "im_minpos_vec", + "position and value of n minima of image", + IM_FN_PIO, + minpos_vec_vec, + IM_NUMBER( maxpos_vec_args ), + maxpos_vec_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 */ +}; + +/* Call im_cross_phase via arg vector. + */ +static int +cross_phase_vec( im_object *argv ) +{ + return( im_cross_phase( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_cross_phase. + */ +static im_function cross_phase_desc = { + "im_cross_phase", /* Name */ + N_( "phase of cross power spectrum of two complex images" ), /* Description */ + IM_FN_PIO | IM_FN_PTOP, /* Flags */ + cross_phase_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 *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, + &cross_phase_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, + &maxpos_vec_desc, + &measure_desc, + &min_desc, + &minpos_desc, + &minpos_vec_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/libvips/arithmetic/im_abs.c b/libvips/arithmetic/im_abs.c new file mode 100644 index 00000000..7305ddd3 --- /dev/null +++ b/libvips/arithmetic/im_abs.c @@ -0,0 +1,272 @@ +/* im_abs() + * + * 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; \ + int x; \ + \ + 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;\ + int x; \ + \ + for( x = 0; x < sz; x++ )\ + q[x] = fabs( p[x] );\ + } + +/* Complex abs operation: calculate modulus. + */ + +#ifdef HAVE_HYPOT + +#define complexabs(TYPE) { \ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + int i; \ + \ + for( i = 0; i < sz; i++ ) { \ + q[i] = hypot( p[0], p[1] ); \ + p += 2; \ + } \ + } + +#else /*HAVE_HYPOT*/ + +#define complexabs(TYPE) { \ + TYPE *p = (TYPE *) in; \ + TYPE *q = (TYPE *) out; \ + TYPE *q_stop = q + sz; \ + \ + while( q < q_stop ){ \ + double rp = *p++; \ + double ip = *p++; \ + double abs_rp= fabs( rp ); \ + double abs_ip= fabs( ip ); \ + \ + if( abs_rp > abs_ip ){ \ + double temp= ip / rp; \ + *q++= abs_rp * sqrt( 1.0 + temp * temp ); \ + } \ + else { \ + double temp= rp / ip; \ + *q++= abs_ip * sqrt( 1.0 + temp * temp ); \ + } \ + } \ + } + +#endif /*HAVE_HYPOT*/ + +/* Abs a buffer of PELs. + */ +static void +abs_gen( PEL *in, PEL *out, int width, IMAGE *im ) +{ + 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 ); + } +} + +/** + * im_abs: + * @in: input #IMAGE + * @out: output #IMAGE + * + * This operation finds the absolute value of an image. It does a copy for + * unsigned integer types, negate for negative values in + * signed integer types, fabs(3) for + * float types, and calculate modulus for complex + * types. + * + * See also: im_exp10tra(), im_sign(). + * + * Returns: 0 on success, -1 on error + */ +int +im_abs( IMAGE *in, IMAGE *out ) +{ + /* Check args. + */ + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_abs", "%s", _( "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", "%s", _( "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/libvips/arithmetic/im_add.c b/libvips/arithmetic/im_add.c new file mode 100644 index 00000000..a7ae3351 --- /dev/null +++ b/libvips/arithmetic/im_add.c @@ -0,0 +1,381 @@ +/* im_add.c + * + * 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(IN, OUT) { \ + IN *p1 = (IN *) in[0]; \ + IN *p2 = (IN *) in[1]; \ + OUT *q = (OUT *) 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. Kep types here in sync with bandfmt_add[] below. + */ + switch( im->BandFmt ) { + case IM_BANDFMT_UCHAR: rloop( unsigned char, unsigned short ); break; + case IM_BANDFMT_CHAR: rloop( signed char, signed short ); break; + case IM_BANDFMT_USHORT: rloop( unsigned short, unsigned int ); break; + case IM_BANDFMT_SHORT: rloop( signed short, signed int ); break; + case IM_BANDFMT_UINT: rloop( unsigned int, unsigned int ); break; + case IM_BANDFMT_INT: rloop( signed int, signed 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, float ); +#endif /*HAVE_LIBOIL*/ + break; + + case IM_BANDFMT_DOUBLE: rloop( double, 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 + +/* For two integer types, the "largest", ie. one which can represent the + * full range of both. + */ +static int bandfmt_largest[6][6] = { + /* UC C US S UI I */ +/* UC */ { UC, S, US, S, UI, I }, +/* C */ { S, C, I, S, I, I }, +/* US */ { US, I, US, I, UI, I }, +/* S */ { S, S, I, S, I, I }, +/* UI */ { UI, I, UI, I, UI, I }, +/* I */ { I, I, I, I, I, I } +}; + +/* For two formats, find one which can represent the full range of both. + */ +static VipsBandFmt +im__format_common( IMAGE *in1, IMAGE *in2 ) +{ + 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. + */ + return( IM_BANDFMT_DPCOMPLEX ); + else + return( 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 ) + return( IM_BANDFMT_DOUBLE ); + else + return( IM_BANDFMT_FLOAT ); + } + else + /* Must be int+int -> int. + */ + return( bandfmt_largest[in1->BandFmt][in2->BandFmt] ); +} + +/* 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", "%s", _( "bad bands" ) ); + return( -1 ); + } + + for( i = 0; i < n; i++ ) + bands[i] = in; + + return( im_gbandjoin( bands, out, n ) ); +} + +/* Cast in1 and in2 up to a common type and number of bands, then 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 ) +{ + VipsBandFmt fmt; + IMAGE *t[5]; + + if( im_open_local_array( out, t, 4, "type cast:1", "p" ) ) + return( -1 ); + + /* Cast our input images up to a common type. + */ + fmt = im__format_common( in1, in2 ); + if( im_clip2fmt( in1, t[0], fmt ) || + im_clip2fmt( in2, t[1], fmt ) ) + 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 process! + */ + t[4] = NULL; + if( im_wrapmany( t + 2, out, fn, out, a ) ) + return( -1 ); + + return( 0 ); +} + +/* Type promotion for addition. Sign and value preserving. Make sure these + * match the case statement in add_buffer() above. + */ +static int bandfmt_add[10] = { +/* UC C US S UI I F M D DM */ + US, S, UI, I, UI, I, F, M, D, DM +}; + +/** + * im_add: + * @in1: input image 1 + * @in2: input image 2 + * @out: output image + * + * This operation adds corresponding pixels in images @in1 and + * @in2 and writes the result to the image descriptor @out. The images must be + * the same size, but may have any type. If one of the images has a single + * band, it is added to every band of the other image. + * + * The two input images are cast up to the smallest common type (see table + * Smallest common format in + * arithmetic), then the + * following table is used to determine the output type: + * + * + * im_add() type promotion + * + * + * + * input type + * output type + * + * + * + * + * uchar + * ushort + * + * + * char + * short + * + * + * ushort + * uint + * + * + * short + * int + * + * + * uint + * uint + * + * + * int + * int + * + * + * float + * float + * + * + * double + * double + * + * + * complex + * complex + * + * + * double complex + * double complex + * + * + * + *
+ * + * In other words, the output type is just large enough to hold the whole + * range of possible values. + * + * See also: im_subtract(), im_lintra(). + * + * Returns: 0 on success, -1 on error + */ +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", "%s", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_add", "%s", _( "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. + */ + out->BandFmt = bandfmt_add[im__format_common( in1, in2 )]; + out->Bbits = im_bits_of_fmt( out->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/libvips/arithmetic/im_avg.c b/libvips/arithmetic/im_avg.c new file mode 100644 index 00000000..21aac595 --- /dev/null +++ b/libvips/arithmetic/im_avg.c @@ -0,0 +1,207 @@ +/* @(#) 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, void *a, void *b ) +{ + 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( void *seq, void *a, void *b ) +{ + double *tmp = (double *) seq; + double *sum = (double *) a; + + *sum += *tmp; + + return( 0 ); +} + +/* Loop over region, accumulating a sum in *tmp. + */ +static int +scan_fn( REGION *reg, void *seq, void *a, void *b ) +{ + double *tmp = (double *) seq; + 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", "%s", _( "bad input type" ) ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_avg", "%s", _( "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/libvips/arithmetic/im_bandmean.c b/libvips/arithmetic/im_bandmean.c new file mode 100644 index 00000000..dd602afb --- /dev/null +++ b/libvips/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", "%s", _( "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/libvips/arithmetic/im_ceil.c b/libvips/arithmetic/im_ceil.c new file mode 100644 index 00000000..eb3eb290 --- /dev/null +++ b/libvips/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", "%s", _( "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/libvips/arithmetic/im_cmulnorm.c b/libvips/arithmetic/im_cmulnorm.c new file mode 100644 index 00000000..22eefbb6 --- /dev/null +++ b/libvips/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/libvips/arithmetic/im_costra.c b/libvips/arithmetic/im_costra.c new file mode 100644 index 00000000..abe022c4 --- /dev/null +++ b/libvips/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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_costra", "%s", _( "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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_acostra", "%s", _( "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/libvips/arithmetic/im_cross_phase.c b/libvips/arithmetic/im_cross_phase.c new file mode 100644 index 00000000..5aba17af --- /dev/null +++ b/libvips/arithmetic/im_cross_phase.c @@ -0,0 +1,158 @@ +/* @(#) Find the phase of the cross power spectrum of two complex images, + * @(#) expressed as a complex image where the modulus of each pixel is + * @(#) one. + * @(#) + * @(#) I.E. find (a.b*)/|a.b*| where + * @(#) . represents complex multiplication + * @(#) * represents the complex conjugate + * @(#) || represents the complex modulus + * @(#) + * @(#) int im_cross_phase( IMAGE *a, IMAGE *b, IMAGE *out ); + * @(#) + * @(#) All functions return 0 on success and -1 on error + * @(#) + * + * Copyright: 2008, Nottingham Trent University + * + * Author: Tom Vajzovic + * Written on: 2008-01-09 + * + * 2008-02-04 tcv: + * - exp( i.th ) == cos(th)+i.sin(th) NOT sin(th)+i.cos(th) + * - add quadratic version (ifdef'd out ATM - still using trigonometric one) + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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*/ + + +/* There doesn't seem to be much difference in speed between these two methods (on an Athlon64), + * so I use the modulus argument version, since atan2() is in c89 but hypot() is c99. + * + * If you think that it might be faster on your platform, uncomment the following: + */ +#define USE_MODARG_DIV + +#ifdef USE_MODARG_DIV + +#define COMPLEX_PHASE_FN( TYPE, ABS ) \ +static void \ +complex_phase_ ## TYPE ( void *in1, void *in2, void *out, int n, void *im, void *unrequired ){ \ + \ + TYPE *X= (TYPE*) in1; \ + TYPE *Y= (TYPE*) in2; \ + TYPE *Z= (TYPE*) out; \ + TYPE *Z_stop= Z + 2 * n * ((IMAGE*)im)-> Bands; \ + \ + for( ; Z < Z_stop; X+= 2, Y+= 2 ){ \ + double arg= atan2( X[1], X[0] ) - atan2( Y[1], Y[0] ); \ + *Z++= cos( arg ); \ + *Z++= sin( arg ); \ + } \ +} + +#else /* USE_MODARG_DIV */ + +#define COMPLEX_PHASE_FN( TYPE, ABS ) \ +static void \ +complex_phase_ ## TYPE ( void *in1, void *in2, void *out, int n, void *im, void *unrequired ){ \ + \ + TYPE *X= (TYPE*) in1; \ + TYPE *Y= (TYPE*) in2; \ + TYPE *Z= (TYPE*) out; \ + TYPE *Z_stop= Z + 2 * n * ((IMAGE*)im)-> Bands; \ + \ + for( ; Z < Z_stop; X+= 2, Y+= 2 ) \ + \ + if( ABS( Y[0] ) > ABS( Y[1] )){ \ + double a= Y[1] / Y[0]; \ + double b= Y[0] + Y[1] * a; \ + double re= ( X[0] + X[1] * a ) / b; \ + double im= ( X[1] - X[0] * a ) / b; \ + double mod= im__hypot( re, im ); \ + *Z++= re / mod; \ + *Z++= im / mod; \ + } \ + else { \ + double a= Y[0] / Y[1]; \ + double b= Y[1] + Y[0] * a; \ + double re= ( X[0] * a + X[1] ) / b; \ + double im= ( X[1] * a - X[0] ) / b; \ + double mod= im__hypot( re, im ); \ + *Z++= re / mod; \ + *Z++= im / mod; \ + } \ +} + +#endif /* USE_MODARG_DIV */ + +COMPLEX_PHASE_FN( float, fabsf ) +COMPLEX_PHASE_FN( double, fabs ) + +int im_cross_phase( IMAGE *a, IMAGE *b, IMAGE *out ){ +#define FUNCTION_NAME "im_phase" + + if( im_pincheck( a ) || im_pincheck( b ) || im_poutcheck( out )) + return -1; + + if( a-> Xsize != b-> Xsize || a-> Ysize != b-> Ysize ){ + im_error( FUNCTION_NAME, "not same size" ); + return -1; + } + if( a-> Bands != b-> Bands ){ + im_error( FUNCTION_NAME, "numbers of bands differ" ); + return -1; + } + if( a-> Coding || b-> Coding ){ + im_error( FUNCTION_NAME, "not uncoded" ); + return -1; + } + if( a-> BandFmt != b-> BandFmt ){ + im_error( FUNCTION_NAME, "formats differ" ); + return -1; + } + if( IM_BANDFMT_COMPLEX != a-> BandFmt && IM_BANDFMT_DPCOMPLEX != a-> BandFmt ){ + im_error( FUNCTION_NAME, "not complex format" ); + return -1; + } + return im_cp_descv( out, a, b, NULL ) || im_wraptwo( a, b, out, + IM_BANDFMT_COMPLEX == a-> BandFmt ? complex_phase_float : complex_phase_double, a, NULL ); +} diff --git a/libvips/arithmetic/im_deviate.c b/libvips/arithmetic/im_deviate.c new file mode 100644 index 00000000..addb7097 --- /dev/null +++ b/libvips/arithmetic/im_deviate.c @@ -0,0 +1,226 @@ +/* @(#) 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, void *a, void *b ) +{ + 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( void *seq, void *a, void *b ) +{ + double *tmp = (double *) seq; + double *sum = (double *) a; + + 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, void *seq, void *a, void *b ) +{ + double *tmp = (double *) seq; + 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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_deviate", "%s", _( "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/libvips/arithmetic/im_divide.c b/libvips/arithmetic/im_divide.c new file mode 100644 index 00000000..c17755db --- /dev/null +++ b/libvips/arithmetic/im_divide.c @@ -0,0 +1,234 @@ +/* @(#) 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. + */ +#ifdef USE_MODARG_DIV +/* This is going to be much slower */ + +#define cloop(TYPE) \ +{ \ + TYPE *X= (TYPE*) in[0]; \ + TYPE *Y= (TYPE*) in[1]; \ + TYPE *Z= (TYPE*) out; \ + TYPE *Z_stop= Z + sz * 2; \ + \ + for( ; Z < Z_stop; X+= 2, Y+=2 ){ \ + double arg= atan2( X[1], X[0] ) - atan2( Y[1], Y[0] ); \ + double mod= hypot( X[1], X[0] ) / hypot( Y[1], Y[0] ); \ + *Z++= mod * cos( arg ); \ + *Z++= mod * sin( arg ); \ + } \ +} + +#else /* USE_MODARG_DIV */ + +#define cloop(TYPE) \ +{ \ + TYPE *X= (TYPE*) in[0]; \ + TYPE *Y= (TYPE*) in[1]; \ + TYPE *Z= (TYPE*) out; \ + TYPE *Z_stop= Z + sz * 2; \ + \ + for( ; Z < Z_stop; X+= 2, Y+=2 ) \ + if( fabs( Y[0] ) > fabs( Y[1] )){ \ + double a= Y[1] / Y[0]; \ + double b= Y[0] + Y[1] * a; \ + *Z++= ( X[0] + X[1] * a ) / b; \ + *Z++= ( X[1] - X[0] * a ) / b; \ + } \ + else { \ + double a= Y[0] / Y[1]; \ + double b= Y[1] + Y[0] * a; \ + *Z++= ( X[0] * a + X[1] ) / b; \ + *Z++= ( X[1] * a - X[0] ) / b; \ + } \ +} + +#endif /* USE_MODARG_DIV */ + +/* 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", "%s", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_divide", "%s", _( "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/libvips/arithmetic/im_expntra.c b/libvips/arithmetic/im_expntra.c new file mode 100644 index 00000000..7a927c88 --- /dev/null +++ b/libvips/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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_expntra_vec", "%s", _( "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/libvips/arithmetic/im_fav4.c b/libvips/arithmetic/im_fav4.c new file mode 100644 index 00000000..c3aa7246 --- /dev/null +++ b/libvips/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/libvips/arithmetic/im_floor.c b/libvips/arithmetic/im_floor.c new file mode 100644 index 00000000..9ad229c6 --- /dev/null +++ b/libvips/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", "%s", _( "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/libvips/arithmetic/im_gadd.c b/libvips/arithmetic/im_gadd.c new file mode 100644 index 00000000..5b35b666 --- /dev/null +++ b/libvips/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","%s", _("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","%s", _("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/libvips/arithmetic/im_gaddim.c b/libvips/arithmetic/im_gaddim.c new file mode 100644 index 00000000..a15dd911 --- /dev/null +++ b/libvips/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/libvips/arithmetic/im_gfadd.c b/libvips/arithmetic/im_gfadd.c new file mode 100644 index 00000000..24fd7bcb --- /dev/null +++ b/libvips/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/libvips/arithmetic/im_invert.c b/libvips/arithmetic/im_invert.c new file mode 100644 index 00000000..d660b173 --- /dev/null +++ b/libvips/arithmetic/im_invert.c @@ -0,0 +1,151 @@ +/* @(#) 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + + /* 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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_invert", "%s", _( "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/libvips/arithmetic/im_linreg.c b/libvips/arithmetic/im_linreg.c new file mode 100644 index 00000000..bf6aba26 --- /dev/null +++ b/libvips/arithmetic/im_linreg.c @@ -0,0 +1,433 @@ +/* @(#) 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 + * + * 12/5/09 + * - make x_anal() static, fix some signed/unsigned warnings + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 **/ + +static x_set *x_anal( IMAGE *im, double *xs, unsigned int n ); + +#define LINREG_START_DECL( TYPE ) static void * linreg_start_ ## TYPE( IMAGE *, void *, void * ); +#define LINREG_GEN_DECL( TYPE ) static int linreg_gen_ ## TYPE( REGION *, void *, void *, void * ); +#define LINREG_STOP_DECL( TYPE ) static int linreg_stop_ ## TYPE( void *, void *, void * ); +#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, 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 **/ + +static x_set *x_anal( IMAGE *im, double *xs, unsigned int n ){ + unsigned 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 void *linreg_start_ ## TYPE( IMAGE *out, void *a, void *b ){ \ + IMAGE **ins= (IMAGE **) a; \ + x_set *x_vals= (x_set *) b; \ + linreg_seq_ ## TYPE *seq= IM_NEW( out, linreg_seq_ ## TYPE ); \ + \ + if( ! seq ) \ + return NULL; \ + \ + seq-> regs= im_start_many( NULL, ins, NULL ); \ + seq-> ptrs= IM_ARRAY( out, x_vals-> n, TYPE* ); \ + seq-> skips= IM_ARRAY( out, x_vals-> n, size_t ); \ + \ + if( ! seq-> ptrs || ! seq-> regs || ! seq-> skips ){ \ + linreg_stop_ ## TYPE( seq, NULL, NULL ); \ + return NULL; \ + } \ + return (void *) 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, void *vseq, void *unrequired, void *b ){ \ + linreg_seq_ ## TYPE *seq= (linreg_seq_ ## TYPE *) vseq; \ + x_set *x_vals= (x_set *) b; \ + 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 ); \ + unsigned 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( void *vseq, void *a, void *b ){ \ + linreg_seq_ ## TYPE *seq = (linreg_seq_ ## TYPE *) vseq; \ + if( seq-> regs ) \ + im_stop_many( seq-> regs, NULL, NULL ); \ + 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/libvips/arithmetic/im_lintra.c b/libvips/arithmetic/im_lintra.c new file mode 100644 index 00000000..af8af422 --- /dev/null +++ b/libvips/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", "%s", _( "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/libvips/arithmetic/im_litecor.c b/libvips/arithmetic/im_litecor.c new file mode 100644 index 00000000..9b4aa577 --- /dev/null +++ b/libvips/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/libvips/arithmetic/im_log10tra.c b/libvips/arithmetic/im_log10tra.c new file mode 100644 index 00000000..45399112 --- /dev/null +++ b/libvips/arithmetic/im_log10tra.c @@ -0,0 +1,167 @@ +/* @(#) 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + 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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_log10tra", "%s", _( "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/libvips/arithmetic/im_logtra.c b/libvips/arithmetic/im_logtra.c new file mode 100644 index 00000000..d29861af --- /dev/null +++ b/libvips/arithmetic/im_logtra.c @@ -0,0 +1,167 @@ +/* @(#) 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + 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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_logtra", "%s", _( "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/libvips/arithmetic/im_max.c b/libvips/arithmetic/im_max.c new file mode 100644 index 00000000..172a533b --- /dev/null +++ b/libvips/arithmetic/im_max.c @@ -0,0 +1,249 @@ +/* @(#) 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) + * 15/10/07 + * - oh, heh, seq->inf was not being set correctly, not that it mattered + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 * +max_start( IMAGE *in, void *a, void *b ) +{ + MaxInfo *inf = (MaxInfo *) a; + 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 +max_stop( void *vseq, void *a, void *b ) +{ + Seq *seq = (Seq *) vseq; + MaxInfo *inf = (MaxInfo *) a; + + 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 +max_scan( REGION *reg, void *vseq, void *a, void *b ) +{ + Seq *seq = (Seq *) vseq; + 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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + + if( im_iterate( in, max_start, max_scan, max_stop, &inf, NULL ) ) + return( -1 ); + + *out = inf.value; + + return( 0 ); +} diff --git a/libvips/arithmetic/im_maxpos.c b/libvips/arithmetic/im_maxpos.c new file mode 100644 index 00000000..3a25214f --- /dev/null +++ b/libvips/arithmetic/im_maxpos.c @@ -0,0 +1,151 @@ +/* @(#) 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", "%s", _( "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/libvips/arithmetic/im_maxpos_avg.c b/libvips/arithmetic/im_maxpos_avg.c new file mode 100644 index 00000000..5427aaa1 --- /dev/null +++ b/libvips/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 + * 15/10/07 JC + * - changed spelling of occurrences + * - check for !occurrences before using val + * - renamed avg as sum, a bit clearer + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_sum; + double y_sum; + double val; + unsigned int occurrences; + +} pos_avg_t; + + +/** LOCAL FUNCTIONS DECLARATIONS **/ + +static void *maxpos_avg_start( IMAGE *im , void *, void * ); +static int maxpos_avg_scan( REGION *reg, void *seq, void *, void * ); +static int maxpos_avg_stop( void *seq, void *, void * ); + + +/** 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, "%s", _("uncoded images only") ); + return -1; + } + if( !( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, "%s", _("scalar images only") ); + return -1; + } + if( 1 != im-> Bands ){ + im_error( FUNCTION_NAME, "%s", _("single band images only") ); + return -1; + } + if( ! xpos || ! ypos || ! out ){ + im_error( FUNCTION_NAME, "%s", _("invalid argument") ); + return -1; + } + if( im_iterate( im, maxpos_avg_start, maxpos_avg_scan, maxpos_avg_stop, &master, NULL ) ) + return -1; + + *xpos= master. x_sum / master. occurrences; + *ypos= master. y_sum / master. occurrences; + + return im_point_bilinear( im, *xpos, *ypos, 0, out ); + +#undef FUNCTION_NAME +} + +static void *maxpos_avg_start( IMAGE *im, void *a, void *b ){ + pos_avg_t *seq; + + seq= IM_NEW( NULL, pos_avg_t ); + if( ! seq ) + return NULL; + + seq-> x_sum= 0.0; + seq-> y_sum= 0.0; + seq-> val= 0.0; + seq-> occurrences= 0; + + return (void *) seq; +} + +/* should be void (always returns 0) */ +static int maxpos_avg_scan( REGION *reg, void *vseq, void *a, void *b ) { + + pos_avg_t *seq= (pos_avg_t *) vseq; + 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( !seq-> occurrences || \ + read[x] > seq-> val ){ \ + seq-> val= read[x]; \ + seq-> x_sum= x; \ + seq-> y_sum= y; \ + seq-> occurrences= 1; \ + } \ + else if( read[x] == seq-> val ){ \ + seq-> x_sum+= x; \ + seq-> y_sum+= y; \ + ++ (seq-> occurrences); \ + } \ +} + + 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( void *vseq, void *a, void *b ) { + + pos_avg_t *seq = (pos_avg_t *) vseq; + pos_avg_t *master = (pos_avg_t *) a; + + if( !master->occurrences || + seq-> val > master-> val ){ + master-> val= seq-> val; + master-> x_sum= seq-> x_sum; + master-> y_sum= seq-> y_sum; + master-> occurrences= seq-> occurrences; + } + else if( seq-> val == master-> val ){ + master-> x_sum+= seq-> x_sum; + master-> y_sum+= seq-> y_sum; + master-> occurrences+= seq-> occurrences; + } + im_free( seq ); + return 0; +} + diff --git a/libvips/arithmetic/im_maxpos_vec.c b/libvips/arithmetic/im_maxpos_vec.c new file mode 100644 index 00000000..5542a091 --- /dev/null +++ b/libvips/arithmetic/im_maxpos_vec.c @@ -0,0 +1,470 @@ +/* @(#) Find the coordinates and values of the n 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 n 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 void *maxpos_vec_start( IMAGE *unrequired, void *, void * ); +static int maxpos_vec_scan( REGION *reg, void *seq, void *, void * ); +static void add_to_maxpos_list( maxpos_list *list, int x, int y, double val ); +static int maxpos_vec_stop( void *seq, void *, void * ); + +static void minpos_list_init( maxpos_list *list, int n ); +static void *minpos_vec_start( IMAGE *unrequired, void *, void * ); +static int minpos_vec_scan( REGION *reg, void *seq, void *, void * ); +static void add_to_minpos_list( maxpos_list *list, int x, int y, double val ); +static int minpos_vec_stop( void *seq, void *, void * ); + + +/** 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 */ + /* efficiency 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, "%s", _( "scalar images only" ) ); + return -1; + } + + if( 1 != im-> Bands ){ + im_error( FUNCTION_NAME, "%s", _( "single band images only" ) ); + return -1; + } + + if( IM_CODING_NONE != im-> Coding ){ + im_error( FUNCTION_NAME, "%s", _( "uncoded images only" ) ); + return -1; + } + + if( ! xpos || ! ypos || ! maxima || n < 1 ){ + im_error( FUNCTION_NAME, "%s", _( "invalid argument" ) ); + return -1; + } + + maxpos_list_init( &master_list, n ); + + result= im_iterate( im, 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, "%s", _( "scalar images only" ) ); + return -1; + } + + if( 1 != im-> Bands ){ + im_error( FUNCTION_NAME, "%s", _( "single band images only" ) ); + return -1; + } + + if( IM_CODING_NONE != im-> Coding ){ + im_error( FUNCTION_NAME, "%s", _( "uncoded images only" ) ); + return -1; + } + + if( ! xpos || ! ypos || ! minima || n < 1 ){ + im_error( FUNCTION_NAME, "%s", _( "invalid argument" ) ); + return -1; + } + + minpos_list_init( &master_list, n ); + + result= im_iterate( im, 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 void *maxpos_vec_start( IMAGE *unrequired, void *a, void *b ){ + + int *n = (int *) a; + 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, void *seq, void *a, void *b ){ + + maxpos_list *list = (maxpos_list *) seq; + +#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( void *seq, void *a, void *b ){ + + /* reverse list */ + + maxpos_list *list = (maxpos_list *) seq; + maxpos_list *master_list = (maxpos_list *) b; + + 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 void *minpos_vec_start( IMAGE *unrequired, void *a, void *b ){ + + int *n = (int *) a; + 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, void *seq, void *a, void *b ){ + + maxpos_list *list = (maxpos_list *) seq; + +#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( void *seq, void *a, void *b ){ + + /* reverse list */ + + maxpos_list *list = (maxpos_list *) seq; + maxpos_list *master_list = (maxpos_list *) b; + 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/libvips/arithmetic/im_measure.c b/libvips/arithmetic/im_measure.c new file mode 100644 index 00000000..55460f86 --- /dev/null +++ b/libvips/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", "%s", _( "not uncoded" ) ); + return( NULL ); + } + if( im_iscomplex( im ) ) { + im_error( "im_measure", "%s", _( "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/libvips/arithmetic/im_min.c b/libvips/arithmetic/im_min.c new file mode 100644 index 00000000..55a76c70 --- /dev/null +++ b/libvips/arithmetic/im_min.c @@ -0,0 +1,246 @@ +/* @(#) 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( IMAGE *im, void *a, void *b ) +{ + MinInfo *inf = (MinInfo *) a; + Seq *seq = IM_NEW( NULL, Seq ); + + seq->inf = inf; + seq->valid = 0; + + return( seq ); +} + +/* Merge the sequence value back into the per-call state. + */ +static int +stop_fn( void *vseq, void *a, void *b ) +{ + Seq *seq = (Seq *) vseq; + MinInfo *inf = (MinInfo *) a; + + 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, void *vseq, void *a, void *b ) +{ + Seq *seq = (Seq *) vseq; + 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", "%s", _( "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/libvips/arithmetic/im_minpos.c b/libvips/arithmetic/im_minpos.c new file mode 100644 index 00000000..6861d69a --- /dev/null +++ b/libvips/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", "%s", _("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/libvips/arithmetic/im_multiply.c b/libvips/arithmetic/im_multiply.c new file mode 100644 index 00000000..7e2488cf --- /dev/null +++ b/libvips/arithmetic/im_multiply.c @@ -0,0 +1,262 @@ +/* @(#) 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", "%s", _( "not same size" ) ); + return( -1 ); + } + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_multiply", + "%s", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_multiply", "%s", _( "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/libvips/arithmetic/im_point_bilinear.c b/libvips/arithmetic/im_point_bilinear.c new file mode 100644 index 00000000..97169af6 --- /dev/null +++ b/libvips/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, "%s", _("uncoded images only") ); + return -1; + } + if( !( im_isint( im ) || im_isfloat( im ) ) ){ + im_error( FUNCTION_NAME, "%s", _("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, "%s", _("coords outside image") ); + return -1; + } + if( ! val ){ + im_error( FUNCTION_NAME, "%s", _("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, "%s", _("coords outside image") ); + im_region_free( reg ); + return -1; + } + + if( x_frac ) + if( y_frac ) + *val= x_frac * y_frac * (double) IM_REGION_VALUE( reg, ((int)x + 1), ((int)y + 1), band ) + + x_frac * ( 1.0 - y_frac ) * (double) IM_REGION_VALUE( reg, ((int)x + 1), (int)y , band ) + + ( 1.0 - x_frac ) * y_frac * (double) IM_REGION_VALUE( reg, (int)x, ((int)y + 1), band ) + + ( 1.0 - x_frac ) * ( 1.0 - y_frac ) * (double) 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/libvips/arithmetic/im_powtra.c b/libvips/arithmetic/im_powtra.c new file mode 100644 index 00000000..49e1176d --- /dev/null +++ b/libvips/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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_powtra_vec", "%s", _( "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/libvips/arithmetic/im_remainder.c b/libvips/arithmetic/im_remainder.c new file mode 100644 index 00000000..8f6a0479 --- /dev/null +++ b/libvips/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 :-( + * 14/5/08 + * - better /0 test + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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", "%s", _( "not same size" ) ); + return( -1 ); + } + if( in1->Bands != in2->Bands && + (in1->Bands != 1 && in2->Bands != 1) ) { + im_error( "im_remainder", + "%s", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_remainder", "%s", _( "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", "%s", _( "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( rc->c[i] == 0 ) { + im_error( "im_remainderconst_vec", + "%s", _( "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/libvips/arithmetic/im_rint.c b/libvips/arithmetic/im_rint.c new file mode 100644 index 00000000..13ffed1f --- /dev/null +++ b/libvips/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", "%s", _( "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/libvips/arithmetic/im_sign.c b/libvips/arithmetic/im_sign.c new file mode 100644 index 00000000..783016d9 --- /dev/null +++ b/libvips/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", "%s", _( "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/libvips/arithmetic/im_sintra.c b/libvips/arithmetic/im_sintra.c new file mode 100644 index 00000000..feefb092 --- /dev/null +++ b/libvips/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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_sintra", "%s", _( "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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_asintra", "%s", _( "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/libvips/arithmetic/im_stats.c b/libvips/arithmetic/im_stats.c new file mode 100644 index 00000000..fd3a4067 --- /dev/null +++ b/libvips/arithmetic/im_stats.c @@ -0,0 +1,280 @@ +/* @(#) 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, void *a, void *b ) +{ + 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( 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( void *seq, void *a, void *b ) +{ + DOUBLEMASK *tmp = (DOUBLEMASK *) seq; + DOUBLEMASK *out = (DOUBLEMASK *) a; + 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, void *seq, void *a, void *b ) +{ + DOUBLEMASK *tmp = (DOUBLEMASK *) seq; + 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", "%s", _( "bad input type" ) ); + return( NULL ); + } + if( in->Coding != IM_CODING_NONE ) { + im_error( "im_stats", "%s", _( "not uncoded" ) ); + return( NULL ); + } + + /* Make output. + */ + pels = (gint64) in->Xsize * in->Ysize; + vals = pels * in->Bands; + if( !(out = make_mask( in, NULL, NULL )) ) + 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/libvips/arithmetic/im_subtract.c b/libvips/arithmetic/im_subtract.c new file mode 100644 index 00000000..6491b6ed --- /dev/null +++ b/libvips/arithmetic/im_subtract.c @@ -0,0 +1,233 @@ +/* @(#) 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", + "%s", _( "not same number of bands" ) ); + return( -1 ); + } + if( in1->Coding != IM_CODING_NONE || in2->Coding != IM_CODING_NONE ) { + im_error( "im_subtract", "%s", _( "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/libvips/arithmetic/im_tantra.c b/libvips/arithmetic/im_tantra.c new file mode 100644 index 00000000..78cf6f31 --- /dev/null +++ b/libvips/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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_tantra", "%s", _( "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", "%s", _( "not uncoded" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_atantra", "%s", _( "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/libvips/boolean/Makefile.am b/libvips/boolean/Makefile.am new file mode 100644 index 00000000..f257b0d1 --- /dev/null +++ b/libvips/boolean/Makefile.am @@ -0,0 +1,7 @@ +noinst_LTLIBRARIES = libboolean.la + +libboolean_la_SOURCES = \ + bool_dispatch.c \ + boolean.c + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/boolean/bool_dispatch.c b/libvips/boolean/bool_dispatch.c new file mode 100644 index 00000000..ae2cbb50 --- /dev/null +++ b/libvips/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/libvips/boolean/boolean.c b/libvips/boolean/boolean.c new file mode 100644 index 00000000..9befe7dc --- /dev/null +++ b/libvips/boolean/boolean.c @@ -0,0 +1,664 @@ +/* @(#) 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( IMAGE **in, IMAGE *out ) +{ + int i, n; + int fmt; + + /* Count args. + */ + for( n = 0; in[n]; n++ ) { + if( in[n]->Coding != IM_CODING_NONE ) { + im_error( "boolean", + "%s", _( "uncoded images only" ) ); + 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_error( "boolean", + "%s", _( "images differ in size" ) ); + return( -1 ); + } + + /* Calculate type conversion ... just 1ary and 2ary. + */ + switch( n ) { + case 1: + fmt = iformat[0][in[0]->BandFmt]; + break; + + case 2: + fmt = iformat[in[1]->BandFmt][in[0]->BandFmt]; + break; + + default: + assert( FALSE ); + } + + for( i = 0; i < n; i++ ) { + IMAGE *t = im_open_local( out, "check-1", "p" ); + + if( !t || im_clip2fmt( in[i], t, fmt ) ) + return( -1 ); + in[i] = t; + } + + /* Prepare the output image. + */ + if( im_cp_desc_array( out, in ) ) + return( -1 ); + + 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( 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( 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( 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, int fmt, double *p ) +{ + PEL *q; + int i; + + if( !(q = IM_ARRAY( out, IM_IMAGE_SIZEOF_PEL( out ), PEL )) ) + return( NULL ); + + switch( fmt ) { + 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( invec, out ) ) + return( -1 ); + in = invec[0]; + if( n != in->Bands ) { + im_error( "im_and_vec", + "%s", _( "vec size does not match bands" ) ); + return( -1 ); + } + if( !(cb = make_pixel( out, in->BandFmt, 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( invec, out ) ) + return( -1 ); + in = invec[0]; + if( n != in->Bands ) { + im_error( "im_or_vec", + "%s", _( "vec size does not match bands" ) ); + return( -1 ); + } + if( !(cb = make_pixel( out, in->BandFmt, 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( invec, out ) ) + return( -1 ); + in = invec[0]; + if( n != in->Bands ) { + im_error( "im_eor_vec", + "%s", _( "vec size does not match bands" ) ); + return( -1 ); + } + if( !(cb = make_pixel( out, in->BandFmt, 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;\ +} + +/* 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*/ + } +} + +/* 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( 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 ); +} + +#define SHIFTR( TYPE ) { \ + TYPE *pt = (TYPE *) p; \ + TYPE *qt = (TYPE *) q; \ + \ + for( x = 0; x < ne; x++ ) \ + qt[x] = pt[x] >> n; \ +} + +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*/ + } +} + +int +im_shiftright( IMAGE *in, IMAGE *out, int n ) +{ + IMAGE *invec[2]; + + invec[0] = in; invec[1] = NULL; + if( check( 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/libvips/cimg/CImg.h b/libvips/cimg/CImg.h new file mode 100644 index 00000000..e36f2f8d --- /dev/null +++ b/libvips/cimg/CImg.h @@ -0,0 +1,22226 @@ +/* + # + # File : CImg.h + # + # Description : The C++ Template Image Processing Library + # ( http://cimg.sourceforge.net ) + # + # Copyright : David Tschumperle + # ( http://www.greyc.ensicaen.fr/~dtschump/ ) + # + # License : CeCILL-C + # + # This software is governed by the CeCILL-C license under French law and + # abiding by the rules of distribution of free software. You can use, + # modify and or redistribute the software under the terms of the CeCILL-C + # license as circulated by CEA, CNRS and INRIA at the following URL + # "http://www.cecill.info". + # + # As a counterpart to the access to the source code and rights to copy, + # modify and redistribute granted by the license, users are provided only + # with a limited warranty and the software's author, the holder of the + # economic rights, and the successive licensors have only limited + # liability. + # + # In this respect, the user's attention is drawn to the risks associated + # with loading, using, modifying and/or developing or reproducing the + # software by the user in light of its specific status of free software, + # that may mean that it is complicated to manipulate, and that also + # therefore means that it is reserved for developers and experienced + # professionals having in-depth computer knowledge. Users are therefore + # encouraged to load and test the software's suitability as regards their + # requirements in conditions enabling the security of their systems and/or + # data to be ensured and, more generally, to use and operate it in the + # same conditions as regards security. + # + # The fact that you are presently reading this means that you have had + # knowledge of the CeCILL-C license and that you accept its terms. + # + */ +#ifndef cimg_version +#define cimg_version 1.20 + +// Detect Microsoft VC++ 6.0 compiler to get some workarounds afterwards. +#if defined(_MSC_VER) && _MSC_VER<1300 +#define cimg_use_visualcpp6 +#endif + +// Avoid strange 'deprecated' warning messages with Visual C++ .NET. +#if defined(_MSC_VER) && _MSC_VER>=1300 +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#endif + +// Standard C++ includes. +#include +#include +#include +#include +#include +#include + +// Overcome VisualC++ 6.0 compilers namespace 'std::' bug. +#ifdef cimg_use_visualcpp6 +#define std +#endif + +/* + # + # Set CImg configuration flags. + # + # If compilation flags are not adapted to your system, + # you may override their values, before including + # the header file "CImg.h" (use the #define directive). + # + */ + +// Try to detect the current system and set value of 'cimg_OS'. +#ifndef cimg_OS +#if defined(sun) || defined(__sun) || defined(linux) || defined(__linux) \ + || defined(__linux__) || defined(__CYGWIN__) || defined(BSD) || defined(__FreeBSD__) \ + || defined(__OPENBSD__) || defined(__MACOSX__) || defined(__APPLE__) || defined(sgi) \ + || defined(__sgi) +// Unix-like (Linux, Solaris, BSD, MacOSX, Irix,...). +#define cimg_OS 1 +#ifndef cimg_display_type +#define cimg_display_type 1 +#endif +#ifndef cimg_color_terminal +#define cimg_color_terminal +#endif +#elif defined(_WIN32) || defined(__WIN32__) +// Windows. +#define cimg_OS 2 +#ifndef cimg_display_type +#define cimg_display_type 2 +#endif +#else +// Unknown configuration : ask for minimal dependencies (no display). +#define cimg_OS 0 +#ifndef cimg_display_type +#define cimg_display_type 0 +#endif +#endif +#endif + +// Debug configuration. +// +// Set 'cimg_debug' to : 0 to remove debug messages (exceptions are still thrown anyway). +// 1 to display debug messages on standard error output (console). +// 2 to display debug messages with modal windows (default behavior). +// 3 to do as 2 + add extra memory access warnings (may slow down the code) +#ifndef cimg_debug +#define cimg_debug 2 +#endif + +// Allow compatibility with older CImg versions. +// +// Define 'cimg_strict' to avoid keeping the compatibility with older code +#ifndef cimg_strict +#define CImgl CImgList +#define cimgl_map cimglist_for +#define cimglist_map cimglist_for +#define cimg_map cimg_for +#define cimg_mapoff cimg_foroff +#define cimg_mapX cimg_forX +#define cimg_mapY cimg_forY +#define cimg_mapZ cimg_forZ +#define cimg_mapV cimg_forV +#define cimg_mapXY cimg_forXY +#define cimg_mapXZ cimg_forXZ +#define cimg_mapXV cimg_forXV +#define cimg_mapYZ cimg_forYZ +#define cimg_mapYV cimg_forYV +#define cimg_mapZV cimg_forZV +#define cimg_mapXYZ cimg_forXYZ +#define cimg_mapXYV cimg_forXYV +#define cimg_mapXZV cimg_forXZV +#define cimg_mapYZV cimg_forYZV +#define cimg_mapXYZV cimg_forXYZV +#define cimg_imapX cimg_for_insideX +#define cimg_imapY cimg_for_insideY +#define cimg_imapZ cimg_for_insideZ +#define cimg_imapV cimg_for_insideV +#define cimg_imapXY cimg_for_insideXY +#define cimg_imapXYZ cimg_for_insideXYZ +#define cimg_bmapX cimg_for_borderX +#define cimg_bmapY cimg_for_borderY +#define cimg_bmapZ cimg_for_borderZ +#define cimg_bmapV cimg_for_borderV +#define cimg_bmapXY cimg_for_borderXY +#define cimg_bmapXYZ cimg_for_borderXYZ +#define cimg_2mapX cimg_for2X +#define cimg_2mapY cimg_for2Y +#define cimg_2mapZ cimg_for2Z +#define cimg_2mapXY cimg_for2XY +#define cimg_2mapXZ cimg_for2XZ +#define cimg_2mapYZ cimg_for2YZ +#define cimg_2mapXYZ cimg_for2XYZ +#define cimg_3mapX cimg_for3X +#define cimg_3mapY cimg_for3Y +#define cimg_3mapZ cimg_for3Z +#define cimg_3mapXY cimg_for3XY +#define cimg_3mapXZ cimg_for3XZ +#define cimg_3mapYZ cimg_for3YZ +#define cimg_3mapXYZ cimg_for3XYZ +#define cimg_4mapX cimg_for4X +#define cimg_4mapY cimg_for4Y +#define cimg_4mapZ cimg_for4Z +#define cimg_4mapXY cimg_for4XY +#define cimg_4mapXZ cimg_for4XZ +#define cimg_4mapYZ cimg_for4YZ +#define cimg_4mapXYZ cimg_for4XYZ +#define cimg_5mapX cimg_for5X +#define cimg_5mapY cimg_for5Y +#define cimg_5mapZ cimg_for5Z +#define cimg_5mapXY cimg_for5XY +#define cimg_5mapXZ cimg_for5XZ +#define cimg_5mapYZ cimg_for5YZ +#define cimg_5mapXYZ cimg_for5XYZ +#define cimg_map2x2x1 cimg_for2x2 +#define cimg_map3x3x1 cimg_for3x3 +#define cimg_map4x4x1 cimg_for4x4 +#define cimg_map5x5x1 cimg_for5x5 +#define cimg_map2x2 cimg_for2x2 +#define cimg_map3x3 cimg_for3x3 +#define cimg_map4x4 cimg_for4x4 +#define cimg_map5x5 cimg_for5x5 +#define cimg_map3x3x3 cimg_for3x3x3 +#define cimg_map2x2x2 cimg_for2x2x2 +#define CImg_2x2x1 CImg_2x2 +#define CImg_3x3x1 CImg_3x3 +#define CImg_4x4x1 CImg_4x4 +#define CImg_5x5x1 CImg_5x5 +#define scroll translate +#define cimg_convert_path cimg_imagemagick_path +#define load_convert load_imagemagick +#define save_convert save_imagemagick +#endif + +// Architecture-dependent includes. +#if cimg_OS==1 +#include +#include +#elif cimg_OS==2 +#include +// Discard unuseful macros in windows.h +// to allow compilation with VC++ 6.0. +#ifdef min +#undef min +#undef max +#undef abs +#endif +#endif +// Display-dependent includes. +#if cimg_display_type==1 +#include +#include +#include +#include +#ifdef cimg_use_xshm +#include +#include +#include +#endif +#ifdef cimg_use_xrandr +#include +#endif +#endif + +// Configuration for native PNG and JPEG support +// +// Define 'cimg_use_png', 'cimg_use_jpeg' or 'cimg_use_tiff' to enable native PNG, JPEG or TIFF files support. +// This requires you link your code with the zlib/png, jpeg or tiff libraries. +// Without these libraries, PNG,JPEG and TIFF support will be done by the Image Magick's 'convert' tool, +// or byt the GraphicsMagick 'gm' tool if installed +// (this is the case on most unix plateforms). +#ifdef cimg_use_png +extern "C" { +#include "png.h" +} +#endif +#ifdef cimg_use_jpeg +extern "C" { +#include "jpeglib.h" +} +#endif +#ifdef cimg_use_tiff +extern "C" { +#include "tiffio.h" +} +#endif +#ifdef cimg_use_magick +#include "Magick++.h" +#endif +#ifdef cimg_use_fftw3 +extern "C" { +#include "fftw3.h" +} +#endif + +/* + # + # + # Define some useful macros. Macros of the CImg Library are prefixed by 'cimg_' + # Documented macros below may be safely used in your own code + # (particularly useful for option parsing, image loops and neighborhoods). + # + # + */ + +// Macros used to describe the program usage, and retrieve command line arguments +// (See corresponding module 'Retrieving command line arguments' in the generated documentation). +#define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage) +#define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0) +#define cimg_option(name,defaut,usage) cimg_library::cimg::option(name,argc,argv,defaut,usage) + +// Macros used for neighborhood definitions and manipulations. +// (see module 'Using Image Loops' in the generated documentation). +#define CImg_2(I,T) T I##cc,I##nc=0 +#define CImg_2x2(I,T) T I##cc,I##nc=0,I##cn,I##nn=0 +#define CImg_3(I,T) T I##pp,I##cp,I##np=0 +#define CImg_3x3(I,T) T I##pp,I##cp,I##np=0,I##pc,I##cc,I##nc=0,I##pn,I##cn,I##nn=0 +#define CImg_4(I,T) T I##pp,I##cp,I##np=0,I##ap=0 +#define CImg_4x4(I,T) T I##pp,I##cp,I##np=0,I##ap=0, \ + I##pc,I##cc,I##nc=0,I##ac=0, \ + I##pn,I##cn,I##nn=0,I##an=0, \ + I##pa,I##ca,I##na=0,I##aa=0 +#define CImg_5(I,T) T I##bb,I##pb,I##cb,I##nb=0,I##ab=0 +#define CImg_5x5(I,T) T I##bb,I##pb,I##cb,I##nb=0,I##ab=0, \ + I##bp,I##pp,I##cp,I##np=0,I##ap=0, \ + I##bc,I##pc,I##cc,I##nc=0,I##ac=0, \ + I##bn,I##pn,I##cn,I##nn=0,I##an=0, \ + I##ba,I##pa,I##ca,I##na=0,I##aa=0 +#define CImg_2x2x2(I,T) T I##ccc,I##ncc=0,I##cnc,I##nnc=0, \ + I##ccn,I##ncn=0,I##cnn,I##nnn=0 +#define CImg_3x3x3(I,T) T I##ppp,I##cpp,I##npp=0,I##pcp,I##ccp,I##ncp=0,I##pnp,I##cnp,I##nnp=0, \ + I##ppc,I##cpc,I##npc=0,I##pcc,I##ccc,I##ncc=0,I##pnc,I##cnc,I##nnc=0, \ + I##ppn,I##cpn,I##npn=0,I##pcn,I##ccn,I##ncn=0,I##pnn,I##cnn,I##nnn=0 + +#define CImg_2x2_ref(I,T,tab) T &I##cc=(tab)[0],&I##nc=(tab)[1],&I##cn=(tab)[2],&I##nn=(tab)[3] +#define CImg_3x3_ref(I,T,tab) T &I##pp=(tab)[0],&I##cp=(tab)[1],&I##np=(tab)[2], \ + &I##pc=(tab)[3],&I##cc=(tab)[4],&I##nc=(tab)[5], \ + &I##pn=(tab)[6],&I##cn=(tab)[7],&I##nn=(tab)[8] +#define CImg_4x4_ref(I,T,tab) T &I##pp=(tab)[0],&I##cp=(tab)[1],&I##np=(tab)[2],&I##ap=(tab)[3], \ + &I##pc=(tab)[4],&I##cc=(tab)[5],&I##nc=(tab)[6],&I##ap=(tab)[7], \ + &I##pn=(tab)[8],&I##cn=(tab)[9],&I##nn=(tab)[10],&I##aa=(tab)[11], \ + &I##pa=(tab)[12],&I##ca=(tab)[13],&I##na=(tab)[14],&I##aa=(tab)[15] +#define CImg_5x5_ref(I,T,tab) T &I##bb=(tab)[0],&I##pb=(tab)[1],&I##cb=(tab)[2],&I##nb=(tab)[3],&I##ab=(tab)[4], \ + &I##bp=(tab)[5],&I##pp=(tab)[6],&I##cp=(tab)[7],&I##np=(tab)[8],&I##ap=(tab)[9], \ + &I##bc=(tab)[10],&I##pc=(tab)[11],&I##cc=(tab)[12],&I##nc=(tab)[13],&I##ac=(tab)[14], \ + &I##bn=(tab)[15],&I##pn=(tab)[16],&I##cn=(tab)[17],&I##nn=(tab)[18],&I##an=(tab)[19], \ + &I##ba=(tab)[20],&I##pa=(tab)[21],&I##ca=(tab)[22],&I##na=(tab)[23],&I##aa=(tab)[24] +#define CImg_2x2x2_ref(I,T,tab) T &I##ccc=(tab)[0],&I##ncc=(tab)[1],&I##cnc=(tab)[2],&I##nnc=(tab)[3], \ + &I##ccn=(tab)[4],&I##ncn=(tab)[5],&I##cnn=(tab)[6],&I##nnn=(tab)[7] +#define CImg_3x3x3_ref(I,T,tab) T &I##ppp=(tab)[0],&I##cpp=(tab)[1],&I##npp=(tab)[2], \ + &I##pcp=(tab)[3],&I##ccp=(tab)[4],&I##ncp=(tab)[5], \ + &I##pnp=(tab)[6],&I##cnp=(tab)[7],&I##nnp=(tab)[8], \ + &I##ppc=(tab)[9],&I##cpc=(tab)[10],&I##npc=(tab)[11], \ + &I##pcc=(tab)[12],&I##ccc=(tab)[13],&I##ncc=(tab)[14], \ + &I##pnc=(tab)[15],&I##cnc=(tab)[16],&I##nnc=(tab)[17], \ + &I##ppn=(tab)[18],&I##cpn=(tab)[19],&I##npn=(tab)[20], \ + &I##pcn=(tab)[21],&I##ccn=(tab)[22],&I##ncn=(tab)[23], \ + &I##pnn=(tab)[24],&I##cnn=(tab)[25],&I##nnn=(tab)[26] + +#define cimg_copy2x2(J,I) I##cc=J##cc, I##nc=J##nc, I##cn=J##cn, I##nn=J##nn +#define cimg_copy3x3(J,I) I##pp=J##pp, I##cp=J##cp, I##np=J##np, \ + I##pc=J##pc, I##cc=J##cc, I##nc=J##nc, \ + I##pn=J##pn, I##cn=J##cn, I##nn=J##nn +#define cimg_copy5x5(J,I) I##bb=J##bb, I##pb=J##pb, I##cb=J##cb, I##nb=J##nb, I##ab=J##ab, \ + I##bp=J##bp, I##pp=J##pp, I##cp=J##cp, I##np=J##np, I##ap=J##ap, \ + I##bc=J##bc, I##pc=J##pc, I##cc=J##cc, I##nc=J##nc, I##ac=J##ac, \ + I##bn=J##bn, I##pn=J##pn, I##cn=J##cn, I##nn=J##nn, I##an=J##an, \ + I##ba=J##ba, I##pa=J##pa, I##ca=J##ca, I##na=J##na, I##aa=J##aa + +#define cimg_squaresum2x2(I) ( I##cc*I##cc + I##nc*I##nc + I##cn*I##cn + I##nn*I##nn ) +#define cimg_squaresum3x3(I) ( I##pp*I##pp + I##cp*I##cp + I##np*I##np + \ + I##pc*I##pc + I##cc*I##cc + I##nc*I##nc + \ + I##pn*I##pn + I##cn*I##cn + I##nn*I##nn ) +#define cimg_squaresum4x4(I) ( I##pp*I##pp + I##cp*I##cp + I##np*I##np + I##ap*I##ap + \ + I##pc*I##pc + I##cc*I##cc + I##nc*I##nc + I##ac*I##ac + \ + I##pn*I##pn + I##cn*I##cn + I##nn*I##nn + I##an*I##an + \ + I##pa*I##pa + I##ca*I##ca + I##na*I##na + I##aa*I##aa ) +#define cimg_squaresum5x5(I) ( I##bb*I##bb + I##pb*I##pb + I##cb*I##cb + I##nb*I##nb + I##ab*I##ab + \ + I##bp*I##bp + I##pp*I##pp + I##cp*I##cp + I##np*I##np + I##ap*I##ap + \ + I##bc*I##bc + I##pc*I##pc + I##cc*I##cc + I##nc*I##nc + I##ac*I##ac + \ + I##bn*I##bn + I##pn*I##pn + I##cn*I##cn + I##nn*I##nn + I##an*I##an + \ + I##ba*I##ba + I##pa*I##pa + I##ca*I##ca + I##na*I##na + I##aa*I##aa ) +#define cimg_squaresum2x2x2(I) ( I##ccc*I##ccc + I##ncc*I##ncc + I##cnc*I##cnc + I##nnc*I##nnc + \ + I##ccn*I##ccn + I##ncn*I##ncn + I##cnn*I##cnn + I##nnn*I##nnn ) +#define cimg_squaresum3x3x3(I) ( I##ppp*I##ppp + I##cpp*I##cpp + I##npp*I##npp + \ + I##pcp*I##pcp + I##ccp*I##ccp + I##ncp*I##ncp + \ + I##pnp*I##pnp + I##cnp*I##cnp + I##nnp*I##nnp + \ + I##ppc*I##ppc + I##cpc*I##cpc + I##npc*I##npc + \ + I##pcc*I##pcc + I##ccc*I##ccc + I##ncc*I##ncc + \ + I##pnc*I##pnc + I##cnc*I##cnc + I##nnc*I##nnc + \ + I##ppn*I##ppn + I##cpn*I##cpn + I##npn*I##npn + \ + I##pcn*I##pcn + I##ccn*I##ccn + I##ncn*I##ncn + \ + I##pnn*I##pnn + I##cnn*I##cnn + I##nnn*I##nnn ) + +#define cimg_corr2x2(I,m) ( I##cc*(m)(0,0)+I##nc*(m)(1,0)+I##cn*(m)(0,1)+I##nn*(m)(1,1) ) +#define cimg_corr3x3(I,m) ( I##pp*(m)(0,0)+I##cp*(m)(1,0)+I##np*(m)(2,0) + \ + I##pc*(m)(0,1)+I##cc*(m)(1,1)+I##nc*(m)(2,1) + \ + I##pn*(m)(0,2)+I##cn*(m)(1,2)+I##nn*(m)(2,2) ) +#define cimg_corr4x4(I,m) ( I##pp*(m)(0,0)+I##cp*(m)(1,0)+I##np*(m)(2,0)+I##ap*(m)(3,0) + \ + I##pc*(m)(0,1)+I##cc*(m)(1,1)+I##nc*(m)(2,1)+I##ac*(m)(3,1) + \ + I##pn*(m)(0,2)+I##cn*(m)(1,2)+I##nn*(m)(2,2)+I##an*(m)(3,2) + \ + I##pa*(m)(0,3)+I##ca*(m)(1,3)+I##na*(m)(2,3)+I##aa*(m)(3,3) ) +#define cimg_corr5x5(I,m) ( I##bb*(m)(0,0)+I##pb*(m)(1,0)+I##cb*(m)(2,0)+I##nb*(m)(3,0)+I##ab*(m)(4,0) + \ + I##bp*(m)(0,1)+I##pp*(m)(1,1)+I##cp*(m)(2,1)+I##np*(m)(3,1)+I##ap*(m)(4,1) + \ + I##bc*(m)(0,2)+I##pc*(m)(1,2)+I##cc*(m)(2,2)+I##nc*(m)(3,2)+I##ac*(m)(4,2) + \ + I##bn*(m)(0,3)+I##pn*(m)(1,3)+I##cn*(m)(2,3)+I##nn*(m)(3,3)+I##an*(m)(4,3) + \ + I##ba*(m)(0,4)+I##pa*(m)(1,4)+I##ca*(m)(2,4)+I##na*(m)(3,4)+I##aa*(m)(4,4) ) +#define cimg_corr2x2x2(I,m) ( I##ccc*(m)(0,0,0)+I##ncc*(m)(1,0,0)+I##cnc*(m)(0,1,0)+I##nnc*(m)(1,1,0) + \ + I##ccn*(m)(0,0,1)+I##ncn*(m)(1,0,1)+I##cnn*(m)(0,1,1)+I##nnn*(m)(1,1,1) ) +#define cimg_corr3x3x3(I,m) ( I##ppp*(m)(0,0,0)+I##cpp*(m)(1,0,0)+I##npp*(m)(2,0,0) + \ + I##pcp*(m)(0,1,0)+I##ccp*(m)(1,1,0)+I##ncp*(m)(2,1,0) + \ + I##pnp*(m)(0,2,0)+I##cnp*(m)(1,2,0)+I##nnp*(m)(2,2,0) + \ + I##ppc*(m)(0,0,1)+I##cpc*(m)(1,0,1)+I##npc*(m)(2,0,1) + \ + I##pcc*(m)(0,1,1)+I##ccc*(m)(1,1,1)+I##ncc*(m)(2,1,1) + \ + I##pnc*(m)(0,2,1)+I##cnc*(m)(1,2,1)+I##nnc*(m)(2,2,1) + \ + I##ppn*(m)(0,0,2)+I##cpn*(m)(1,0,2)+I##npn*(m)(2,0,2) + \ + I##pcn*(m)(0,1,2)+I##ccn*(m)(1,1,2)+I##ncn*(m)(2,1,2) + \ + I##pnn*(m)(0,2,2)+I##cnn*(m)(1,2,2)+I##nnn*(m)(2,2,2) ) + +#define cimg_conv2x2(I,m) ( I##cc*(m)(1,1)+I##nc*(m)(0,1)+I##cn*(m)(1,0)+I##nn*(m)(0,0) ) +#define cimg_conv3x3(I,m) ( I##pp*(m)(2,2)+I##cp*(m)(1,2)+I##np*(m)(0,2) + \ + I##pc*(m)(2,1)+I##cc*(m)(1,1)+I##nc*(m)(0,1) + \ + I##pn*(m)(2,0)+I##cn*(m)(1,0)+I##nn*(m)(0,0) ) +#define cimg_conv4x4(I,m) ( I##pp*(m)(3,3)+I##cp*(m)(2,3)+I##np*(m)(1,3)+I##ap*(m)(0,3) + \ + I##pc*(m)(3,2)+I##cc*(m)(2,2)+I##nc*(m)(1,2)+I##ac*(m)(0,2) + \ + I##pn*(m)(3,1)+I##cn*(m)(2,1)+I##nn*(m)(1,1)+I##an*(m)(0,1) + \ + I##pa*(m)(3,0)+I##ca*(m)(2,0)+I##na*(m)(1,0)+I##aa*(m)(0,0) ) +#define cimg_conv5x5(I,m) ( I##bb*(m)(4,4)+I##pb*(m)(3,4)+I##cb*(m)(2,4)+I##nb*(m)(1,4)+I##ab*(m)(0,4) + \ + I##bp*(m)(4,3)+I##pp*(m)(3,3)+I##cp*(m)(2,3)+I##np*(m)(1,3)+I##ap*(m)(0,3) + \ + I##bc*(m)(4,2)+I##pc*(m)(3,2)+I##cc*(m)(2,2)+I##nc*(m)(1,2)+I##ac*(m)(0,2) + \ + I##bn*(m)(4,1)+I##pn*(m)(3,1)+I##cn*(m)(2,1)+I##nn*(m)(1,1)+I##an*(m)(0,1) + \ + I##ba*(m)(4,0)+I##pa*(m)(3,0)+I##ca*(m)(2,0)+I##na*(m)(1,0)+I##aa*(m)(0,0) ) +#define cimg_conv2x2x2(I,m) ( I##ccc*(m)(1,1,1)+I##ncc*(m)(0,1,1)+I##cnc*(m)(1,0,1)+I##nnc*(m)(0,0,1) + \ + I##ccn*(m)(1,1,0)+I##ncn*(m)(0,1,0)+I##cnn*(m)(1,0,0)+I##nnn*(m)(0,0,0) ) +#define cimg_conv3x3x3(I,m) ( I##ppp*(m)(2,2,2)+I##cpp*(m)(1,2,2)+I##npp*(m)(0,2,2) + \ + I##pcp*(m)(2,1,2)+I##ccp*(m)(1,1,2)+I##ncp*(m)(0,1,2) + \ + I##pnp*(m)(2,0,2)+I##cnp*(m)(1,0,2)+I##nnp*(m)(0,0,2) + \ + I##ppc*(m)(2,2,1)+I##cpc*(m)(1,2,1)+I##npc*(m)(0,2,1) + \ + I##pcc*(m)(2,1,1)+I##ccc*(m)(1,1,1)+I##ncc*(m)(0,1,1) + \ + I##pnc*(m)(2,0,1)+I##cnc*(m)(1,0,1)+I##nnc*(m)(0,0,1) + \ + I##ppn*(m)(2,2,0)+I##cpn*(m)(1,2,0)+I##npn*(m)(0,2,0) + \ + I##pcn*(m)(2,1,0)+I##ccn*(m)(1,1,0)+I##ncn*(m)(0,1,0) + \ + I##pnn*(m)(2,0,0)+I##cnn*(m)(1,0,0)+I##nnn*(m)(0,0,0) ) + +#define cimg_get2x2(img,x,y,z,v,I) \ + I##cc=(img)(x, y,z,v), I##nc=(img)(_n##x, y,z,v), \ + I##cn=(img)(x,_n##y,z,v), I##nn=(img)(_n##x,_n##y,z,v) +#define cimg_get3x3(img,x,y,z,v,I) \ + I##pp=(img)(_p##x,_p##y,z,v), I##cp=(img)(x,_p##y,z,v), I##np=(img)(_n##x,_p##y,z,v), \ + I##pc=(img)(_p##x, y,z,v), I##cc=(img)(x, y,z,v), I##nc=(img)(_n##x, y,z,v), \ + I##pn=(img)(_p##x,_n##y,z,v), I##cn=(img)(x,_n##y,z,v), I##nn=(img)(_n##x,_n##y,z,v) +#define cimg_get4x4(img,x,y,z,v,I) \ + I##pp=(img)(_p##x,_p##y,z,v), I##cp=(img)(x,_p##y,z,v), I##np=(img)(_n##x,_p##y,z,v), I##ap=(img)(_a##x,_p##y,z,v), \ + I##pc=(img)(_p##x, y,z,v), I##cc=(img)(x, y,z,v), I##nc=(img)(_n##x, y,z,v), I##ac=(img)(_a##x, y,z,v), \ + I##pn=(img)(_p##x,_n##y,z,v), I##cn=(img)(x,_n##y,z,v), I##nn=(img)(_n##x,_n##y,z,v), I##an=(img)(_a##x,_n##y,z,v), \ + I##pa=(img)(_p##x,_a##y,z,v), I##ca=(img)(x,_a##y,z,v), I##na=(img)(_n##x,_a##y,z,v), I##aa=(img)(_a##x,_a##y,z,v) +#define cimg_get5x5(img,x,y,z,v,I) \ + I##bb=(img)(_b##x,_b##y,z,v), I##pb=(img)(_p##x,_b##y,z,v), I##cb=(img)(x,_b##y,z,v), I##nb=(img)(_n##x,_b##y,z,v), I##ab=(img)(_a##x,_b##y,z,v), \ + I##bp=(img)(_b##x,_p##y,z,v), I##pp=(img)(_p##x,_p##y,z,v), I##cp=(img)(x,_p##y,z,v), I##np=(img)(_n##x,_p##y,z,v), I##ap=(img)(_a##x,_p##y,z,v), \ + I##bc=(img)(_b##x, y,z,v), I##pc=(img)(_p##x, y,z,v), I##cc=(img)(x, y,z,v), I##nc=(img)(_n##x, y,z,v), I##ac=(img)(_a##x, y,z,v), \ + I##bn=(img)(_b##x,_n##y,z,v), I##pn=(img)(_p##x,_n##y,z,v), I##cn=(img)(x,_n##y,z,v), I##nn=(img)(_n##x,_n##y,z,v), I##an=(img)(_a##x,_n##y,z,v), \ + I##ba=(img)(_b##x,_a##y,z,v), I##pa=(img)(_p##x,_a##y,z,v), I##ca=(img)(x,_a##y,z,v), I##na=(img)(_n##x,_a##y,z,v), I##aa=(img)(_a##x,_a##y,z,v) +#define cimg_get2x2x2(img,x,y,z,v,I) \ + I##ccc=(img)(x,y, z,v), I##ncc=(img)(_n##x,y, z,v), I##cnc=(img)(x,_n##y, z,v), I##nnc=(img)(_n##x,_n##y, z,v), \ + I##ccc=(img)(x,y,_n##z,v), I##ncc=(img)(_n##x,y,_n##z,v), I##cnc=(img)(x,_n##y,_n##z,v), I##nnc=(img)(_n##x,_n##y,_n##z,v) +#define cimg_get3x3x3(img,x,y,z,v,I) \ + I##ppp=(img)(_p##x,_p##y,_p##z,v), I##cpp=(img)(x,_p##y,_p##z,v), I##npp=(img)(_n##x,_p##y,_p##z,v), \ + I##pcp=(img)(_p##x, y,_p##z,v), I##ccp=(img)(x, y,_p##z,v), I##ncp=(img)(_n##x, y,_p##z,v), \ + I##pnp=(img)(_p##x,_n##y,_p##z,v), I##cnp=(img)(x,_n##y,_p##z,v), I##nnp=(img)(_n##x,_n##y,_p##z,v), \ + I##ppc=(img)(_p##x,_p##y, z,v), I##cpc=(img)(x,_p##y, z,v), I##npc=(img)(_n##x,_p##y, z,v), \ + I##pcc=(img)(_p##x, y, z,v), I##ccc=(img)(x, y, z,v), I##ncc=(img)(_n##x, y, z,v), \ + I##pnc=(img)(_p##x,_n##y, z,v), I##cnc=(img)(x,_n##y, z,v), I##nnc=(img)(_n##x,_n##y, z,v), \ + I##ppn=(img)(_p##x,_p##y,_n##z,v), I##cpn=(img)(x,_p##y,_n##z,v), I##npn=(img)(_n##x,_p##y,_n##z,v), \ + I##pcn=(img)(_p##x, y,_n##z,v), I##ccn=(img)(x, y,_n##z,v), I##ncn=(img)(_n##x, y,_n##z,v), \ + I##pnn=(img)(_p##x,_n##y,_n##z,v), I##cnn=(img)(x,_n##y,_n##z,v), I##nnn=(img)(_n##x,_n##y,_n##z,v) + +// Macros used to define special image loops. +// (see module 'Using Image Loops' in the generated documentation). +#define cimg_for(img,ptr,T_ptr) for (T_ptr *ptr=(img).data+(img).size(); (ptr--)>(img).data; ) +#define cimglist_for(list,l) for (unsigned int l=0; l<(list).size; l++) +#define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn +#define cimg_foroff(img,off) for (unsigned int off=0; off<(img).size(); off++) +#define cimg_forX(img,x) for (int x=0; x<(int)((img).width); x++) +#define cimg_forY(img,y) for (int y=0; y<(int)((img).height);y++) +#define cimg_forZ(img,z) for (int z=0; z<(int)((img).depth); z++) +#define cimg_forV(img,v) for (int v=0; v<(int)((img).dim); v++) +#define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x) +#define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x) +#define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y) +#define cimg_forXV(img,x,v) cimg_forV(img,v) cimg_forX(img,x) +#define cimg_forYV(img,y,v) cimg_forV(img,v) cimg_forY(img,y) +#define cimg_forZV(img,z,v) cimg_forV(img,v) cimg_forZ(img,z) +#define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y) +#define cimg_forXYV(img,x,y,v) cimg_forV(img,v) cimg_forXY(img,x,y) +#define cimg_forXZV(img,x,z,v) cimg_forV(img,v) cimg_forXZ(img,x,z) +#define cimg_forYZV(img,y,z,v) cimg_forV(img,v) cimg_forYZ(img,y,z) +#define cimg_forXYZV(img,x,y,z,v) cimg_forV(img,v) cimg_forXYZ(img,x,y,z) +#define cimg_for_insideX(img,x,n) for (int x=(n); x<(int)((img).width-(n)); x++) +#define cimg_for_insideY(img,y,n) for (int y=(n); y<(int)((img).height-(n)); y++) +#define cimg_for_insideZ(img,z,n) for (int z=(n); z<(int)((img).depth-(n)); z++) +#define cimg_for_insideV(img,v,n) for (int v=(n); v<(int)((img).dim-(n)); v++) +#define cimg_for_insideXY(img,x,y,n) cimg_for_insideY(img,y,n) cimg_for_insideX(img,x,n) +#define cimg_for_insideXYZ(img,x,y,z,n) cimg_for_insideZ(img,z,n) cimg_for_insideXY(img,x,y,n) +#define cimg_for_borderX(img,x,n) for (int x=0; x<(int)((img).width); x==(n)-1?(x=(img).width-(n)): x++) +#define cimg_for_borderY(img,y,n) for (int y=0; y<(int)((img).height); y==(n)-1?(x=(img).height-(n)):y++) +#define cimg_for_borderZ(img,z,n) for (int z=0; z<(int)((img).depth); z==(n)-1?(x=(img).depth-(n)): z++) +#define cimg_for_borderV(img,v,n) for (int v=0; v<(int)((img).dim); v==(n)-1?(x=(img).dim-(n)): v++) +#define cimg_for_borderXY(img,x,y,n) cimg_forY(img,y) for (int x=0; x<(int)((img).width); (y<(n) || y>=(int)((img).height)-(n))?x++: \ + ((x<(n)-1 || x>=(int)((img).width)-(n))?x++:(x=(img).width-(n)))) +#define cimg_for_borderXYZ(img,x,y,z,n) cimg_forYZ(img,y,z) for (int x=0; x<(int)((img).width); (y<(n) || y>=(int)((img).height)-(n) || z<(n) || z>=(int)((img).depth)-(n))?x++: \ + ((x<(n)-1 || x>=(int)((img).width)-(n))?x++:(x=(img).width-(n)))) +#define cimg_for2X(img,x) for (int x=0,_n##x=1; _n##x<(int)((img).width) || x==--_n##x; x++, _n##x++) +#define cimg_for2Y(img,y) for (int y=0,_n##y=1; _n##y<(int)((img).height) || y==--_n##y; y++, _n##y++) +#define cimg_for2Z(img,z) for (int z=0,_n##z=1; _n##z<(int)((img).depth) || z==--_n##z; z++, _n##z++) +#define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x) +#define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x) +#define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y) +#define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y) +#define cimg_for3X(img,x) for (int x=0,_p##x=0,_n##x=1; _n##x<(int)((img).width) || x==--_n##x; _p##x=x++,_n##x++) +#define cimg_for3Y(img,y) for (int y=0,_p##y=0,_n##y=1; _n##y<(int)((img).height) || y==--_n##y; _p##y=y++,_n##y++) +#define cimg_for3Z(img,z) for (int z=0,_p##z=0,_n##z=1; _n##z<(int)((img).depth) || z==--_n##z; _p##z=z++,_n##z++) +#define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x) +#define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x) +#define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y) +#define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y) +#define cimg_for4X(img,x) for (int _p##x=0,x=0,_n##x=1,_a##x=2; \ + _a##x<(int)((img).width) || _n##x==--_a##x || x==(_a##x=--_n##x); \ + _p##x=x++,_n##x++,_a##x++) +#define cimg_for4Y(img,y) for (int _p##y=0,y=0,_n##y=1,_a##y=2; \ + _a##y<(int)((img).height) || _n##y==--_a##y || y==(_a##y=--_n##y); \ + _p##y=y++,_n##y++,_a##y++) +#define cimg_for4Z(img,z) for (int _p##z=0,z=0,_n##z=1,_a##z=2; \ + _a##z<(int)((img).depth) || _n##z==--_a##z || z==(_a##z=--_n##z); \ + _p##z=z++,_n##z++,_a##z++) +#define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x) +#define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x) +#define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y) +#define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y) +#define cimg_for5X(img,x) for (int _b##x=0,_p##x=0,x=0,_n##x=1,_a##x=2; \ + _a##x<(int)((img).width) || _n##x==--_a##x || x==(_a##x=--_n##x); \ + _b##x=_p##x,_p##x=x++,_n##x++,_a##x++) +#define cimg_for5Y(img,y) for (int _b##y=0,_p##y=0,y=0,_n##y=1,_a##y=2; \ + _a##y<(int)((img).height) || _n##y==--_a##y || y==(_a##y=--_n##y); \ + _b##y=_p##y,_p##y=y++,_n##y++,_a##y++) +#define cimg_for5Z(img,z) for (int _b##z=0,_p##z=0,z=0,_n##z=1,_a##z=2; \ + _a##z<(int)((img).depth) || _n##z==--_a##z || z==(_a##z=--_n##z); \ + _b##z=_p##z,_p##z=z++,_n##z++,_a##z++) +#define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x) +#define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x) +#define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y) +#define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y) + +#define cimg_for2x2(img,x,y,z,v,I) cimg_for2Y(img,y) \ + for (int _n##x=1, x=(int)((I##cc=(img)(0, y,z,v)), \ + (I##cn=(img)(0,_n##y,z,v)), \ + 0); \ + (_n##x<(int)((img).width) && ((I##nc=(img)(_n##x, y,z,v)), \ + (I##nn=(img)(_n##x,_n##y,z,v)), \ + 1)) || x==--_n##x; \ + I##cc=I##nc, I##cn=I##nn, \ + x++,_n##x++ ) + +#define cimg_for3x3(img,x,y,z,v,I) cimg_for3Y(img,y) \ + for (int _n##x=1, _p##x=(int)((I##cp=I##pp=(img)(0,_p##y,z,v)), \ + (I##cc=I##pc=(img)(0, y,z,v)), \ + (I##cn=I##pn=(img)(0,_n##y,z,v))), \ + x=_p##x=0; \ + (_n##x<(int)((img).width) && ((I##np=(img)(_n##x,_p##y,z,v)), \ + (I##nc=(img)(_n##x, y,z,v)), \ + (I##nn=(img)(_n##x,_n##y,z,v)), \ + 1)) || x==--_n##x; \ + I##pp=I##cp, I##pc=I##cc, I##pn=I##cn, \ + I##cp=I##np, I##cc=I##nc, I##cn=I##nn, \ + _p##x=x++,_n##x++ ) + + +#define cimg_for4x4(img,x,y,z,v,I) cimg_for4Y(img,y) \ + for (int _a##x=2, _n##x=1, x=(int)((I##cp=I##pp=(img)(0,_p##y,z,v)), \ + (I##cc=I##pc=(img)(0, y,z,v)), \ + (I##cn=I##pn=(img)(0,_n##y,z,v)), \ + (I##ca=I##pa=(img)(0,_a##y,z,v)), \ + (I##np=(img)(_n##x,_p##y,z,v)), \ + (I##nc=(img)(_n##x, y,z,v)), \ + (I##nn=(img)(_n##x,_n##y,z,v)), \ + (I##na=(img)(_n##x,_a##y,z,v)), \ + 0), _p##x=0; \ + (_a##x<(int)((img).width) && ((I##ap=(img)(_a##x,_p##y,z,v)), \ + (I##ac=(img)(_a##x, y,z,v)), \ + (I##an=(img)(_a##x,_n##y,z,v)), \ + (I##aa=(img)(_a##x,_a##y,z,v)), \ + 1)) || _n##x==--_a##x || x==(_a##x=--_n##x); \ + I##pp=I##cp, I##pc=I##cc, I##pn=I##cn, I##pa=I##ca, \ + I##cp=I##np, I##cc=I##nc, I##cn=I##nn, I##ca=I##na, \ + I##np=I##ap, I##nc=I##ac, I##nn=I##an, I##na=I##aa, \ + _p##x=x++, _n##x++, _a##x++ ) + +#define cimg_for5x5(img,x,y,z,v,I) cimg_for5Y(img,y) \ + for (int _a##x=2, _n##x=1, _b##x=(int)((I##cb=I##pb=I##bb=(img)(0,_b##y,z,v)), \ + (I##cp=I##pp=I##bp=(img)(0,_p##y,z,v)), \ + (I##cc=I##pc=I##bc=(img)(0, y,z,v)), \ + (I##cn=I##pn=I##bn=(img)(0,_n##y,z,v)), \ + (I##ca=I##pa=I##ba=(img)(0,_a##y,z,v)), \ + (I##nb=(img)(_n##x,_b##y,z,v)), \ + (I##np=(img)(_n##x,_p##y,z,v)), \ + (I##nc=(img)(_n##x, y,z,v)), \ + (I##nn=(img)(_n##x,_n##y,z,v)), \ + (I##na=(img)(_n##x,_a##y,z,v))), \ + x=0, _p##x=_b##x=0; \ + (_a##x<(int)((img).width) && ((I##ab=(img)(_a##x,_b##y,z,v)), \ + (I##ap=(img)(_a##x,_p##y,z,v)), \ + (I##ac=(img)(_a##x, y,z,v)), \ + (I##an=(img)(_a##x,_n##y,z,v)), \ + (I##aa=(img)(_a##x,_a##y,z,v)), \ + 1)) || _n##x==--_a##x || x==(_a##x=--_n##x); \ + I##bb=I##pb, I##bp=I##pp, I##bc=I##pc, I##bn=I##pn, I##ba=I##pa, \ + I##pb=I##cb, I##pp=I##cp, I##pc=I##cc, I##pn=I##cn, I##pa=I##ca, \ + I##cb=I##nb, I##cp=I##np, I##cc=I##nc, I##cn=I##nn, I##ca=I##na, \ + I##nb=I##ab, I##np=I##ap, I##nc=I##ac, I##nn=I##an, I##na=I##aa, \ + _b##x=_p##x, _p##x=x++, _n##x++, _a##x++ ) + +#define cimg_for2x2x2(img,x,y,z,v,I) cimg_for2YZ(img,y,z) \ + for (int _n##x=1, x=(int)((I##ccc=(img)(0, y, z,v)), \ + (I##cnc=(img)(0,_n##y, z,v)), \ + (I##ccn=(img)(0, y,_n##z,v)), \ + (I##cnn=(img)(0,_n##y,_n##z,v)), \ + 0); \ + (_n##x<(int)((img).width) && ((I##ncc=(img)(_n##x, y, z,v)), \ + (I##nnc=(img)(_n##x,_n##y, z,v)), \ + (I##ncn=(img)(_n##x, y,_n##z,v)), \ + (I##nnn=(img)(_n##x,_n##y,_n##z,v)), \ + 1)) || x==--_n##x; \ + I##ccc=I##ncc, I##cnc=I##nnc, \ + I##ccn=I##ncn, I##cnn=I##nnn, \ + x++, _n##x++ ) + +#define cimg_for3x3x3(img,x,y,z,v,I) cimg_for3YZ(img,y,z) \ + for (int _n##x=1, _p##x=(int)((I##cpp=I##ppp=(img)(0,_p##y,_p##z,v)), \ + (I##ccp=I##pcp=(img)(0, y,_p##z,v)), \ + (I##cnp=I##pnp=(img)(0,_n##y,_p##z,v)), \ + (I##cpc=I##ppc=(img)(0,_p##y, z,v)), \ + (I##ccc=I##pcc=(img)(0, y, z,v)), \ + (I##cnc=I##pnc=(img)(0,_n##y, z,v)), \ + (I##cpn=I##ppn=(img)(0,_p##y,_n##z,v)), \ + (I##ccn=I##pcn=(img)(0, y,_n##z,v)), \ + (I##cnn=I##pnn=(img)(0,_n##y,_n##z,v))),\ + x=_p##x=0; \ + (_n##x<(int)((img).width) && ((I##npp=(img)(_n##x,_p##y,_p##z,v)), \ + (I##ncp=(img)(_n##x, y,_p##z,v)), \ + (I##nnp=(img)(_n##x,_n##y,_p##z,v)), \ + (I##npc=(img)(_n##x,_p##y, z,v)), \ + (I##ncc=(img)(_n##x, y, z,v)), \ + (I##nnc=(img)(_n##x,_n##y, z,v)), \ + (I##npn=(img)(_n##x,_p##y,_n##z,v)), \ + (I##ncn=(img)(_n##x, y,_n##z,v)), \ + (I##nnn=(img)(_n##x,_n##y,_n##z,v)), \ + 1)) || x==--_n##x; \ + I##ppp=I##cpp, I##pcp=I##ccp, I##pnp=I##cnp, \ + I##cpp=I##npp, I##ccp=I##ncp, I##cnp=I##nnp, \ + I##ppc=I##cpc, I##pcc=I##ccc, I##pnc=I##cnc, \ + I##cpc=I##npc, I##ccc=I##ncc, I##cnc=I##nnc, \ + I##ppn=I##cpn, I##pcn=I##ccn, I##pnn=I##cnn, \ + I##cpn=I##npn, I##ccn=I##ncn, I##cnn=I##nnn, \ + _p##x=x++, _n##x++ ) + +/* + #------------------------------------------------ + # + # + # Definition of the cimg_library:: namespace + # + # + #------------------------------------------------ + */ + +//! Namespace that encompasses all classes and functions of the %CImg library. +/** + This namespace is defined to avoid class names collisions that could happen + with the include of other C++ header files. Anyway, it should not happen + very often and you may start most of your programs with + \code + #include "CImg.h" + using namespace cimg_library; + \endcode + to simplify the declaration of %CImg Library objects variables afterwards. +**/ + +namespace cimg_library { + + // Define the CImg classes. + template struct CImg; + template struct CImgList; + struct CImgStats; + struct CImgDisplay; + struct CImgException; + + namespace cimg { + + // The bodies of the functions below are defined afterwards + inline void info(); + + inline unsigned int& exception_mode(); + + inline int dialog(const char *title,const char *msg,const char *button1_txt="OK", + const char *button2_txt=0,const char *button3_txt=0, + const char *button4_txt=0,const char *button5_txt=0, + const char *button6_txt=0,const bool centering = false); + + template + inline void marching_cubes(const tfunc& func, const float isovalue, + const float x0,const float y0,const float z0, + const float x1,const float y1,const float z1, + const float resx,const float resy,const float resz, + CImgList& points, CImgList& primitives, + const bool invert_faces = false); + + template + inline void marching_squares(const tfunc& func, const float isovalue, + const float x0,const float y0, + const float x1,const float y1, + const float resx,const float resy, + CImgList& points, CImgList& primitives); + } + + /* + #---------------------------------------------- + # + # + # Definition of the CImgException structures + # + # + #---------------------------------------------- + */ + + // Never use the following macro in your own code ! +#define cimg_exception_err(etype,disp_flag) \ + if (cimg::exception_mode()>=1) { \ + std::va_list ap; \ + va_start(ap,format); \ + std::vsprintf(message,format,ap); \ + va_end(ap); \ + if (cimg::exception_mode()>=2 && disp_flag) { \ + try { cimg::dialog(etype,message,"Abort"); } \ + catch (CImgException&) { std::fprintf(stderr,"\n# %s :\n%s\n\n",etype,message); } \ + } else std::fprintf(stderr,"\n# %s :\n%s\n\n",etype,message); \ + } \ + if (cimg::exception_mode()>=3) cimg_library::cimg::info(); \ + + //! Class which is thrown when an error occured during a %CImg library function call. + /** + + \section ex1 Overview + + CImgException is the base class of %CImg exceptions. + Exceptions are thrown by the %CImg Library when an error occured in a %CImg library function call. + CImgException is seldom thrown itself. Children classes that specify the kind of error encountered + are generally used instead. These sub-classes are : + + - \b CImgInstanceException : Thrown when the instance associated to the called %CImg function is not + correctly defined. Generally, this exception is thrown when one tries to process \a empty images. The example + below will throw a \a CImgInstanceException. + \code + CImg img; // Construct an empty image. + img.blur(10); // Try to blur the image. + \endcode + + - \b CImgArgumentException : Thrown when one of the arguments given to the called %CImg function is not correct. + Generally, this exception is thrown when arguments passed to the function are outside an admissible range of values. + The example below will throw a \a CImgArgumentException. + \code + CImg img(100,100,1,3); // Define a 100x100 color image with float pixels. + img = 0; // Try to fill pixels from the 0 pointer (invalid argument to operator=() ). + \endcode + + - \b CImgIOException : Thrown when an error occured when trying to load or save image files. + The example below will throw a \a CImgIOException. + \code + CImg img("file_doesnt_exist.jpg"); // Try to load a file that doesn't exist. + \endcode + + - \b CImgDisplayException : Thrown when an error occured when trying to display an image in a window. + This exception is thrown when image display request cannot be satisfied. + + The parent class CImgException may be thrown itself when errors that cannot be classified in one of + the above type occur. It is recommended not to throw CImgExceptions yourself, since there are normally + reserved to %CImg Library functions. + \b CImgInstanceException, \b CImgArgumentException, \b CImgIOException and \b CImgDisplayException are simple + subclasses of CImgException and are thus not detailled more in this reference documentation. + + \section ex2 Exception handling + + When an error occurs, the %CImg Library first displays the error in a modal window. + Then, it throws an instance of the corresponding exception class, generally leading the program to stop + (this is the default behavior). + You can bypass this default behavior by handling the exceptions yourself, + using a code block try { ... } catch() { ... }. + In this case, you can avoid the apparition of the modal window, by + defining the environment variable cimg_debug to 0 before including the %CImg header file. + The example below shows how to cleanly handle %CImg Library exceptions : + \code + #define cimg_debug 0 // Disable modal window in CImg exceptions. + #define "CImg.h" + int main() { + try { + ...; // Here, do what you want. + } + catch (CImgInstanceException &e) { + std::fprintf(stderr,"CImg Library Error : %s",e.message); // Display your own error message + ... // Do what you want now. + } + } + \endcode + **/ + struct CImgException { + char message[1024]; //!< Message associated with the error that thrown the exception. + CImgException() { message[0]='\0'; } + CImgException(const char *format,...) { cimg_exception_err("CImgException",true); } + }; + + // The \ref CImgInstanceException class is used to throw an exception related + // to a non suitable instance encountered in a library function call. + struct CImgInstanceException : CImgException { + CImgInstanceException(const char *format,...) { cimg_exception_err("CImgInstanceException",true); } + }; + + // The \ref CImgArgumentException class is used to throw an exception related + // to invalid arguments encountered in a library function call. + struct CImgArgumentException : CImgException { + CImgArgumentException(const char *format,...) { cimg_exception_err("CImgArgumentException",true); } + }; + + // The \ref CImgIOException class is used to throw an exception related + // to Input/Output file problems encountered in a library function call. + struct CImgIOException : CImgException { + CImgIOException(const char *format,...) { cimg_exception_err("CImgIOException",true); } + }; + + // The CImgDisplayException class is used to throw an exception related to display problems + // encountered in a library function call. + struct CImgDisplayException : CImgException { + CImgDisplayException(const char *format,...) { cimg_exception_err("CImgDisplayException",false); } + }; + + /* + #------------------------------------- + # + # + # Definition of the namespace 'cimg' + # + # + #------------------------------------- + */ + + //! Namespace that encompasses \a low-level functions and variables of the %CImg Library. + /** + Most of the functions and variables within this namespace are used by the library for low-level processing. + Nevertheless, documented variables and functions of this namespace may be used safely in your own source code. + + \warning Never write using namespace cimg_library::cimg; in your source code, since a lot of functions of the + cimg:: namespace have prototypes similar to standard C functions defined in the global namespace ::. + **/ + namespace cimg { + + // Define the trait that will be used to determine the best data type to work with. + // Considered types are : bool, uchar, char, short, ushort, int, uint, long, ulong, float and double. + // Two rules applies there : + // - largest of two integer types is an integer type. + // - largest of integer/float type is a float type. + template struct largest { typedef t type; }; + template<> struct largest { typedef unsigned char type; }; + template<> struct largest { typedef short type; }; + template<> struct largest { typedef char type; }; + template<> struct largest { typedef short type; }; + template<> struct largest { typedef int type; }; + template<> struct largest { typedef unsigned int type; }; + template<> struct largest { typedef unsigned long type; }; + template<> struct largest { typedef unsigned short type; }; + template<> struct largest { typedef unsigned short type; }; + template<> struct largest { typedef int type; }; + template<> struct largest { typedef int type; }; + template<> struct largest { typedef short type; }; + template<> struct largest { typedef short type; }; + template<> struct largest { typedef short type; }; + template<> struct largest { typedef int type; }; + template<> struct largest { typedef unsigned int type; }; + template<> struct largest { typedef unsigned long type; }; + template<> struct largest { typedef unsigned int type; }; + template<> struct largest { typedef unsigned int type; }; + template<> struct largest { typedef unsigned int type; }; + template<> struct largest { typedef unsigned int type; }; + template<> struct largest { typedef unsigned int type; }; + template<> struct largest { typedef unsigned int type; }; + template<> struct largest { typedef int type; }; + template<> struct largest { typedef int type; }; + template<> struct largest { typedef int type; }; + template<> struct largest { typedef int type; }; + template<> struct largest { typedef int type; }; + template<> struct largest { typedef unsigned int type; }; + template<> struct largest { typedef unsigned long type; }; + template<> struct largest { typedef float type; }; + template<> struct largest { typedef float type; }; + template<> struct largest { typedef float type; }; + template<> struct largest { typedef float type; }; + template<> struct largest { typedef float type; }; + template<> struct largest { typedef float type; }; + template<> struct largest { typedef float type; }; + template<> struct largest { typedef float type; }; + template<> struct largest { typedef float type; }; + template<> struct largest { typedef double type; }; + template<> struct largest { typedef double type; }; + template<> struct largest { typedef double type; }; + template<> struct largest { typedef double type; }; + template<> struct largest { typedef double type; }; + template<> struct largest { typedef double type; }; + template<> struct largest { typedef double type; }; + template<> struct largest { typedef double type; }; + template<> struct largest { typedef double type; }; + template<> struct largest { typedef double type; }; + + template struct type { + static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); } + static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "unknown"; return s; } + }; + template<> struct type { + static bool min() { return false; } + static bool max() { return true; } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "bool"; return s; } + }; + template<> struct type { + static unsigned char min() { return 0; } + static unsigned char max() { return (unsigned char)~0U; } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "unsigned char"; return s; } + }; + template<> struct type { + static char min() { return (char)-1<<(8*sizeof(char)-1); } + static char max() { return ~((char)-1<<(8*sizeof(char)-1)); } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "char"; return s; } + }; + template<> struct type { + static unsigned short min() { return 0; } + static unsigned short max() { return (unsigned short)~0U; } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "unsigned short"; return s; } + }; + template<> struct type { + static short min() { return (short)-1<<(8*sizeof(short)-1); } + static short max() { return ~((short)-1<<(8*sizeof(short)-1)); } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "short"; return s; } + }; + template<> struct type { + static unsigned int min() { return 0; } + static unsigned int max() { return (unsigned int)~0U; } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "unsigned int"; return s; } + }; + template<> struct type { + static int min() { return (int)-1<<(8*sizeof(int)-1); } + static int max() { return ~((int)-1<<(8*sizeof(int)-1)); } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "int"; return s; } + }; + template<> struct type { + static unsigned long min() { return 0; } + static unsigned long max() { return (unsigned long)~0UL; } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "unsigned long"; return s; } + }; + template<> struct type { + static long min() { return (long)-1<<(8*sizeof(long)-1); } + static long max() { return ~((long)-1<<(8*sizeof(long)-1)); } + static bool is_float() { return false; } + static const char* id() { static const char *const s = "long"; return s; } + }; + template<> struct type { + static float min() { return -3.4E38f; } + static float max() { return 3.4E38f; } + static bool is_float() { return true; } + static const char* id() { static const char *const s = "float"; return s; } + }; + template<> struct type { + static double min() { return -1.7E308; } + static double max() { return 1.7E308; } + static bool is_float() { return true; } + static const char* id() { static const char *const s = "double"; return s; } + }; + + // Define internal library variables. +#if cimg_display_type==1 + struct X11info { + volatile unsigned int nb_wins; + pthread_mutex_t* mutex; + pthread_t* event_thread; + CImgDisplay* wins[1024]; + Display* display; + unsigned int nb_bits; + GC* gc; + bool blue_first; + bool byte_order; + bool shm_enabled; +#ifdef cimg_use_xrandr + XRRScreenSize *resolutions; + Rotation curr_rotation; + unsigned int curr_resolution; + unsigned int nb_resolutions; +#endif + X11info():nb_wins(0),mutex(0),event_thread(0),display(0), + nb_bits(0),gc(0),blue_first(false),byte_order(false),shm_enabled(false) { +#ifdef cimg_use_xrandr + resolutions = 0; + curr_rotation = 0; + curr_resolution = nb_resolutions = 0; +#endif + } + }; +#if defined(cimg_module) + X11info& X11attr(); +#elif defined(cimg_main) + X11info& X11attr() { static X11info val; return val; } +#else + inline X11info& X11attr() { static X11info val; return val; } +#endif + +#elif cimg_display_type==2 + struct Win32info { + HANDLE wait_event; + Win32info() { wait_event = CreateEvent(0,FALSE,FALSE,0); } + }; +#if defined(cimg_module) + Win32info& Win32attr(); +#elif defined(cimg_main) + Win32info& Win32attr() { static Win32info val; return val; } +#else + inline Win32info& Win32attr() { static Win32info val; return val; } +#endif +#endif + + inline unsigned int& exception_mode() { static unsigned int mode=cimg_debug; return mode; } + +#ifdef cimg_color_terminal + const char t_normal[9] = {0x1b,'[','0',';','0',';','0','m','\0'}; + const char t_red[11] = {0x1b,'[','4',';','3','1',';','5','9','m','\0'}; + const char t_bold[5] = {0x1b,'[','1','m','\0'}; + const char t_purple[11] = {0x1b,'[','0',';','3','5',';','5','9','m','\0'}; +#else + const char t_normal[1] = {'\0'}; + const char *const t_red = cimg::t_normal, *const t_bold = cimg::t_normal, *const t_purple = cimg::t_normal; +#endif + +#if cimg_display_type==1 + // Keycodes for X11-based graphical systems + const unsigned int keyESC = XK_Escape; + const unsigned int keyF1 = XK_F1; + const unsigned int keyF2 = XK_F2; + const unsigned int keyF3 = XK_F3; + const unsigned int keyF4 = XK_F4; + const unsigned int keyF5 = XK_F5; + const unsigned int keyF6 = XK_F6; + const unsigned int keyF7 = XK_F7; + const unsigned int keyF8 = XK_F8; + const unsigned int keyF9 = XK_F9; + const unsigned int keyF10 = XK_F10; + const unsigned int keyF11 = XK_F11; + const unsigned int keyF12 = XK_F12; + const unsigned int keyPAUSE = XK_Pause; + const unsigned int key1 = XK_1; + const unsigned int key2 = XK_2; + const unsigned int key3 = XK_3; + const unsigned int key4 = XK_4; + const unsigned int key5 = XK_5; + const unsigned int key6 = XK_6; + const unsigned int key7 = XK_7; + const unsigned int key8 = XK_8; + const unsigned int key9 = XK_9; + const unsigned int key0 = XK_0; + const unsigned int keyBACKSPACE = XK_BackSpace; + const unsigned int keyINSERT = XK_Insert; + const unsigned int keyHOME = XK_Home; + const unsigned int keyPAGEUP = XK_Page_Up; + const unsigned int keyTAB = XK_Tab; + const unsigned int keyQ = XK_q; + const unsigned int keyW = XK_w; + const unsigned int keyE = XK_e; + const unsigned int keyR = XK_r; + const unsigned int keyT = XK_t; + const unsigned int keyY = XK_y; + const unsigned int keyU = XK_u; + const unsigned int keyI = XK_i; + const unsigned int keyO = XK_o; + const unsigned int keyP = XK_p; + const unsigned int keyDELETE = XK_Delete; + const unsigned int keyEND = XK_End; + const unsigned int keyPAGEDOWN = XK_Page_Down; + const unsigned int keyCAPSLOCK = XK_Caps_Lock; + const unsigned int keyA = XK_a; + const unsigned int keyS = XK_s; + const unsigned int keyD = XK_d; + const unsigned int keyF = XK_f; + const unsigned int keyG = XK_g; + const unsigned int keyH = XK_h; + const unsigned int keyJ = XK_j; + const unsigned int keyK = XK_k; + const unsigned int keyL = XK_l; + const unsigned int keyENTER = XK_Return; + const unsigned int keySHIFTLEFT = XK_Shift_L; + const unsigned int keyZ = XK_z; + const unsigned int keyX = XK_x; + const unsigned int keyC = XK_c; + const unsigned int keyV = XK_v; + const unsigned int keyB = XK_b; + const unsigned int keyN = XK_n; + const unsigned int keyM = XK_m; + const unsigned int keySHIFTRIGHT = XK_Shift_R; + const unsigned int keyARROWUP = XK_Up; + const unsigned int keyCTRLLEFT = XK_Control_L; + const unsigned int keyAPPLEFT = XK_Super_L; + const unsigned int keySPACE = XK_space; + const unsigned int keyALTGR = XK_Alt_R; + const unsigned int keyAPPRIGHT = XK_Super_R; + const unsigned int keyMENU = XK_Menu; + const unsigned int keyCTRLRIGHT = XK_Control_R; + const unsigned int keyARROWLEFT = XK_Left; + const unsigned int keyARROWDOWN = XK_Down; + const unsigned int keyARROWRIGHT = XK_Right; + const unsigned int keyPAD0 = XK_KP_0; + const unsigned int keyPAD1 = XK_KP_1; + const unsigned int keyPAD2 = XK_KP_2; + const unsigned int keyPAD3 = XK_KP_3; + const unsigned int keyPAD4 = XK_KP_4; + const unsigned int keyPAD5 = XK_KP_5; + const unsigned int keyPAD6 = XK_KP_6; + const unsigned int keyPAD7 = XK_KP_7; + const unsigned int keyPAD8 = XK_KP_8; + const unsigned int keyPAD9 = XK_KP_9; + const unsigned int keyPADADD = XK_KP_Add; + const unsigned int keyPADSUB = XK_KP_Subtract; + const unsigned int keyPADMUL = XK_KP_Multiply; + const unsigned int keyPADDIV = XK_KP_Divide; + +#elif (cimg_display_type==2 && cimg_OS==2) + // Keycodes for Windows-OS + const unsigned int keyESC = VK_ESCAPE; + const unsigned int keyF1 = VK_F1; + const unsigned int keyF2 = VK_F2; + const unsigned int keyF3 = VK_F3; + const unsigned int keyF4 = VK_F4; + const unsigned int keyF5 = VK_F5; + const unsigned int keyF6 = VK_F6; + const unsigned int keyF7 = VK_F7; + const unsigned int keyF8 = VK_F8; + const unsigned int keyF9 = VK_F9; + const unsigned int keyF10 = VK_F10; + const unsigned int keyF11 = VK_F11; + const unsigned int keyF12 = VK_F12; + const unsigned int keyPAUSE = VK_PAUSE; + const unsigned int key1 = '1'; + const unsigned int key2 = '2'; + const unsigned int key3 = '3'; + const unsigned int key4 = '4'; + const unsigned int key5 = '5'; + const unsigned int key6 = '6'; + const unsigned int key7 = '7'; + const unsigned int key8 = '8'; + const unsigned int key9 = '9'; + const unsigned int key0 = '0'; + const unsigned int keyBACKSPACE = VK_BACK; + const unsigned int keyINSERT = VK_INSERT; + const unsigned int keyHOME = VK_HOME; + const unsigned int keyPAGEUP = VK_PRIOR; + const unsigned int keyTAB = VK_TAB; + const unsigned int keyQ = 'Q'; + const unsigned int keyW = 'W'; + const unsigned int keyE = 'E'; + const unsigned int keyR = 'R'; + const unsigned int keyT = 'T'; + const unsigned int keyY = 'Y'; + const unsigned int keyU = 'U'; + const unsigned int keyI = 'I'; + const unsigned int keyO = 'O'; + const unsigned int keyP = 'P'; + const unsigned int keyDELETE = VK_DELETE; + const unsigned int keyEND = VK_END; + const unsigned int keyPAGEDOWN = VK_NEXT; + const unsigned int keyCAPSLOCK = VK_CAPITAL; + const unsigned int keyA = 'A'; + const unsigned int keyS = 'S'; + const unsigned int keyD = 'D'; + const unsigned int keyF = 'F'; + const unsigned int keyG = 'G'; + const unsigned int keyH = 'H'; + const unsigned int keyJ = 'J'; + const unsigned int keyK = 'K'; + const unsigned int keyL = 'L'; + const unsigned int keyENTER = VK_RETURN; + const unsigned int keySHIFTLEFT = VK_SHIFT; + const unsigned int keyZ = 'Z'; + const unsigned int keyX = 'X'; + const unsigned int keyC = 'C'; + const unsigned int keyV = 'V'; + const unsigned int keyB = 'B'; + const unsigned int keyN = 'N'; + const unsigned int keyM = 'M'; + const unsigned int keySHIFTRIGHT = VK_SHIFT; + const unsigned int keyARROWUP = VK_UP; + const unsigned int keyCTRLLEFT = VK_CONTROL; + const unsigned int keyAPPLEFT = VK_LWIN; + const unsigned int keySPACE = VK_SPACE; + const unsigned int keyALTGR = VK_CONTROL; + const unsigned int keyAPPRIGHT = VK_RWIN; + const unsigned int keyMENU = VK_APPS; + const unsigned int keyCTRLRIGHT = VK_CONTROL; + const unsigned int keyARROWLEFT = VK_LEFT; + const unsigned int keyARROWDOWN = VK_DOWN; + const unsigned int keyARROWRIGHT = VK_RIGHT; + const unsigned int keyPAD0 = 0x60; + const unsigned int keyPAD1 = 0x61; + const unsigned int keyPAD2 = 0x62; + const unsigned int keyPAD3 = 0x63; + const unsigned int keyPAD4 = 0x64; + const unsigned int keyPAD5 = 0x65; + const unsigned int keyPAD6 = 0x66; + const unsigned int keyPAD7 = 0x67; + const unsigned int keyPAD8 = 0x68; + const unsigned int keyPAD9 = 0x69; + const unsigned int keyPADADD = VK_ADD; + const unsigned int keyPADSUB = VK_SUBTRACT; + const unsigned int keyPADMUL = VK_MULTIPLY; + const unsigned int keyPADDIV = VK_DIVIDE; +#else + // Define unknow keycodes when no display + const unsigned int keyESC = 1U; + const unsigned int keyF1 = 2U; + const unsigned int keyF2 = 3U; + const unsigned int keyF3 = 4U; + const unsigned int keyF4 = 5U; + const unsigned int keyF5 = 6U; + const unsigned int keyF6 = 7U; + const unsigned int keyF7 = 8U; + const unsigned int keyF8 = 9U; + const unsigned int keyF9 = 10U; + const unsigned int keyF10 = 11U; + const unsigned int keyF11 = 12U; + const unsigned int keyF12 = 13U; + const unsigned int keyPAUSE = 14U; + const unsigned int key1 = 15U; + const unsigned int key2 = 16U; + const unsigned int key3 = 17U; + const unsigned int key4 = 18U; + const unsigned int key5 = 19U; + const unsigned int key6 = 20U; + const unsigned int key7 = 21U; + const unsigned int key8 = 22U; + const unsigned int key9 = 23U; + const unsigned int key0 = 24U; + const unsigned int keyBACKSPACE = 25U; + const unsigned int keyINSERT = 26U; + const unsigned int keyHOME = 27U; + const unsigned int keyPAGEUP = 28U; + const unsigned int keyTAB = 29U; + const unsigned int keyQ = 30U; + const unsigned int keyW = 31U; + const unsigned int keyE = 32U; + const unsigned int keyR = 33U; + const unsigned int keyT = 34U; + const unsigned int keyY = 35U; + const unsigned int keyU = 36U; + const unsigned int keyI = 37U; + const unsigned int keyO = 38U; + const unsigned int keyP = 39U; + const unsigned int keyDELETE = 40U; + const unsigned int keyEND = 41U; + const unsigned int keyPAGEDOWN = 42U; + const unsigned int keyCAPSLOCK = 43U; + const unsigned int keyA = 44U; + const unsigned int keyS = 45U; + const unsigned int keyD = 46U; + const unsigned int keyF = 47U; + const unsigned int keyG = 48U; + const unsigned int keyH = 49U; + const unsigned int keyJ = 50U; + const unsigned int keyK = 51U; + const unsigned int keyL = 52U; + const unsigned int keyENTER = 53U; + const unsigned int keySHIFTLEFT = 54U; + const unsigned int keyZ = 55U; + const unsigned int keyX = 56U; + const unsigned int keyC = 57U; + const unsigned int keyV = 58U; + const unsigned int keyB = 59U; + const unsigned int keyN = 60U; + const unsigned int keyM = 61U; + const unsigned int keySHIFTRIGHT = 62U; + const unsigned int keyARROWUP = 63U; + const unsigned int keyCTRLLEFT = 64U; + const unsigned int keyAPPLEFT = 65U; + const unsigned int keySPACE = 66U; + const unsigned int keyALTGR = 67U; + const unsigned int keyAPPRIGHT = 68U; + const unsigned int keyMENU = 69U; + const unsigned int keyCTRLRIGHT = 70U; + const unsigned int keyARROWLEFT = 71U; + const unsigned int keyARROWDOWN = 72U; + const unsigned int keyARROWRIGHT = 73U; + const unsigned int keyPAD0 = 74U; + const unsigned int keyPAD1 = 75U; + const unsigned int keyPAD2 = 76U; + const unsigned int keyPAD3 = 77U; + const unsigned int keyPAD4 = 78U; + const unsigned int keyPAD5 = 79U; + const unsigned int keyPAD6 = 80U; + const unsigned int keyPAD7 = 81U; + const unsigned int keyPAD8 = 82U; + const unsigned int keyPAD9 = 83U; + const unsigned int keyPADADD = 84U; + const unsigned int keyPADSUB = 85U; + const unsigned int keyPADMUL = 86U; + const unsigned int keyPADDIV = 87U; +#endif + +#ifdef PI +#undef PI +#endif + const double PI = 3.14159265358979323846; //!< Definition of the mathematical constant PI + + // Definition of a 7x11 font, used to return a default font for drawing text. + const unsigned int font7x11[7*11*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x0,0x7f0000,0x40000,0x0,0x0,0x4010c0a4,0x82000040,0x11848402,0x18480050,0x80430292,0x8023,0x9008000, + 0x40218140,0x4000040,0x21800402,0x18000051,0x1060500,0x8083,0x10000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24002,0x4031,0x80000000,0x10000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c0400,0x40020000,0x80070080,0x40440e00,0x0,0x0,0x1,0x88180000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x200000,0x0,0x0,0x80000,0x0,0x0,0x20212140,0x5000020,0x22400204,0x240000a0,0x40848500,0x4044,0x80010038,0x20424285,0xa000020, + 0x42428204,0x2428e0a0,0x82090a14,0x4104,0x85022014,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10240a7,0x88484040,0x40800000,0x270c3,0x87811e0e, + 0x7c70e000,0x78,0x3c23c1ef,0x1f3e1e89,0xf1c44819,0xa23cf0f3,0xc3cff120,0xc18307f4,0x4040400,0x20000,0x80080080,0x40200,0x0, + 0x40000,0x2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8188,0x50603800,0xf3c00000,0x1c004003,0xc700003e,0x18180,0xc993880,0x10204081, + 0x2071ef9,0xf3e7cf9f,0x3e7c7911,0xe3c78f1e,0x7d1224,0x48906048,0x0,0x4000000,0x0,0x9000,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x10240aa,0x14944080,0x23610000,0x68940,0x40831010,0x8891306,0x802044,0x44522208,0x90202088,0x40448819,0xb242890a,0x24011111, + 0x49448814,0x4040a00,0xe2c3c7,0x8e3f3cb9,0xc1c44216,0xee38b0f2,0xe78f9120,0xc18507e2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x101c207,0x88a04001,0x9c00000,0x2200a041,0x8200113a,0x8240,0x50a3110,0x2850a142,0x850c2081,0x2040204,0x8104592,0x142850a1, + 0x42cd1224,0x4888bc48,0x70e1c387,0xe3b3c70,0xe1c38e1c,0x38707171,0xc3870e1c,0x10791224,0x48906c41,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x10003ee,0x15140080,0x21810000,0x48840,0x40851020,0x8911306,0x31fd804,0x9c522408,0x90204088,0x4045081a,0xba42890a,0x24011111, + 0x49285024,0x2041b00,0x132408,0x910844c8,0x4044821b,0x7244c913,0x24041111,0x49488822,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x28204,0x85006001,0x6a414000,0x3a004043,0xc700113a,0x8245,0x50a3a00,0x2850a142,0x850c4081,0x2040204,0x81045d2,0x142850a1, + 0x24951224,0x48852250,0x8102040,0x81054089,0x12244204,0x8108992,0x24489122,0x991224,0x4888b222,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1000143,0xa988080,0x2147c01f,0x88840,0x83091c2c,0x1070f000,0xc000608,0xa48bc408,0x9e3c46f8,0x40460816,0xaa42f10b,0xc3811111, + 0x35102044,0x1041100,0xf22408,0x9f084488,0x40470212,0x62448912,0x6041111,0x55308846,0x8061c80,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1028704,0x8f805801,0x4be28fdf,0x220001f0,0x111a,0x60000182,0x82c5c710,0x44891224,0x489640f1,0xe3c78204,0x810e552,0x142850a1, + 0x18a51224,0x48822250,0x78f1e3c7,0x8f1f40f9,0xf3e7c204,0x8108912,0x24489122,0x7ea91224,0x4888a222,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x10007e2,0x85648080,0x20010000,0x88841,0x8f8232,0x20881000,0xc1fc610,0xbefa2408,0x90204288,0x40450816,0xa642810a,0x4041110a, + 0x36282084,0x1042080,0x1122408,0x90084488,0x40450212,0x62448912,0x184110a,0x55305082,0x8042700,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1028207,0x82004801,0x68050040,0x1c000040,0x110a,0x60000001,0x45484d10,0x7cf9f3e7,0xcf944081,0x2040204,0x8104532,0x142850a1, + 0x18a51224,0x48822248,0x89122448,0x91244081,0x2040204,0x8108912,0x24489122,0xc91224,0x48852214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x282, + 0x89630080,0x20010c00,0x30108842,0x810222,0x20882306,0x3001800,0x408a2208,0x90202288,0x40448814,0xa642810a,0x2041110a,0x26442104, + 0x840000,0x1122408,0x90084488,0x40448212,0x62448912,0x84130a,0x36485102,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x101c208,0x4f802801, + 0x8028040,0x40,0x130a,0x2,0x85e897a0,0x44891224,0x489c2081,0x2040204,0x8104532,0x142850a1,0x24cd1224,0x48823c44,0x89122448, + 0x91244081,0x2040204,0x8108912,0x24489122,0xc93264,0xc9852214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100028f,0x109f0080,0x20010c00, + 0x303071f3,0xc7011c1c,0x4071c306,0x802010,0x3907c1ef,0x1f201e89,0xf3844f90,0xa23c80f2,0x17810e04,0x228223f4,0x840000,0xfbc3c7, + 0x8f083c88,0x40444212,0x6238f0f2,0x7039d04,0x228423e2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1008780,0x2201800,0xf0014000,0x1f0, + 0x1d0a,0x5,0x851e140,0x83060c18,0x30671ef9,0xf3e7cf9f,0x3e7c7911,0xe3c78f1e,0x42f8e1c3,0x8702205c,0x7cf9f3e7,0xcf9b3c78,0xf1e3c204, + 0x8107111,0xc3870e1c,0x10f1d3a7,0x4e823c08,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x40,0x40000400,0x200000,0x0,0x2,0x0,0x0,0x0,0x0,0x18, + 0x0,0x4,0x44007f,0x0,0x400,0x400000,0x8010,0x0,0x6002,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000000,0x200800,0x0,0x0,0x100a, + 0x400000,0x44,0x0,0x400,0x0,0x0,0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x0,0x62018,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x80000800, + 0x400000,0x0,0x4,0x0,0x0,0x0,0x0,0xc,0x0,0x7,0x3c0000,0x0,0x3800,0x3800000,0x8010,0x0,0x1c001,0x881c0000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x207000,0x0,0x0,0x100a,0xc00000,0x3c,0x0,0xc00,0x0,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x0,0x1c2070 + }; + + // Definition of a 10x13 font (used in dialog boxes). + const unsigned int font10x13[256*10*13/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80100c0, + 0x68000300,0x801,0xc00010,0x100c000,0x68100,0x100c0680,0x2,0x403000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x4020120, + 0x58120480,0x402,0x1205008,0x2012050,0x58080,0x20120581,0x40000001,0x804812,0x2000000,0x0,0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x140,0x80000,0x200402,0x800000,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x7010,0x7000000,0x8000200,0x20000,0xc0002000,0x8008,0x0,0x0,0x0,0x0,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x80000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x80100c0,0x68000480,0x1001, + 0xc00010,0x1018000,0x68100,0x100c0680,0x4,0x403000,0x1020000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,0x28081883,0x200801, + 0x2a00000,0x10,0x1c0201c0,0x70040f80,0xc0f81c07,0x0,0x70,0x3e0303c0,0x3c3c0f83,0xe03c2107,0xe08810,0x18c31070,0x3c0703c0, + 0x783e0842,0x22222208,0x83e04010,0x1008000,0x4000200,0x20001,0x2002,0x408008,0x0,0x0,0x100000,0x0,0x1008,0x2000000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20080,0x38000880,0x8078140f,0x81c00000,0x3e000,0xc020180,0x60080001,0xe0000002,0xc00042,0x108e2010, + 0xc0300c0,0x300c0303,0xf83c3e0f,0x83e0f81c,0x701c070,0x3c0c41c0,0x701c0701,0xc0001d08,0x42108421,0x8820088,0x4020120,0x58140480, + 0x802,0x1205008,0x3014050,0xc058080,0x20120581,0x40000002,0x804814,0x2020050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140, + 0x281e2484,0x80200801,0x1c02000,0x10,0x22060220,0x880c0801,0x82208,0x80000001,0x20008,0x41030220,0x40220802,0x402102,0x209010, + 0x18c31088,0x22088220,0x80080842,0x22222208,0x80204010,0x1014000,0x200,0x20001,0x2000,0x8008,0x0,0x0,0x100000,0x0,0x1008, + 0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40000500,0x80800010,0x40200000,0x41000,0x12020040,0x10000003,0xa0000006, + 0x12000c4,0x31014000,0xc0300c0,0x300c0302,0x80402008,0x2008008,0x2008020,0x220c4220,0x88220882,0x20002208,0x42108421,0x8820088, + 0x0,0x300,0x0,0x0,0x0,0x14000000,0x0,0x200200,0x0,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xfc282504,0x80001000, + 0x82a02000,0x20,0x22020020,0x8140802,0x102208,0x80801006,0x18008,0x9c848220,0x80210802,0x802102,0x20a010,0x15429104,0x22104220, + 0x80080842,0x22221405,0x404008,0x1022000,0x703c0,0x381e0701,0xc0783c02,0xc09008,0x1d83c070,0x3c078140,0x381c0882,0x21242208, + 0x81e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,0x40220500,0x80800027,0x20e02800,0x9c800,0x12020040, + 0x20000883,0xa0200002,0x120a044,0x11064010,0x12048120,0x48120484,0x80802008,0x2008008,0x2008020,0x210a4411,0x4411044,0x10884508, + 0x42108421,0x503c0b0,0x1c0701c0,0x701c0707,0x70381c07,0x1c07008,0x2008020,0x20f01c0,0x701c0701,0xc0201c08,0x82208822,0x883c088, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x50281903,0x20001000,0x80802000,0x20,0x22020040,0x30240f03,0xc0101c08,0x80801018, + 0x1fc06010,0xa48483c0,0x80210f03,0xe0803f02,0x20c010,0x15429104,0x22104220,0x70080841,0x41540805,0x804008,0x1041000,0x8220, + 0x40220881,0x882202,0x40a008,0x12422088,0x22088180,0x40100882,0x21241408,0x80201008,0x2031000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x20280,0x401c0200,0x700028,0x21205000,0x92800,0xc1fc080,0x10000883,0xa0200002,0x1205049,0x12c19010,0x12048120,0x48120484, + 0xf0803c0f,0x3c0f008,0x2008020,0x790a4411,0x4411044,0x10504908,0x42108421,0x5022088,0x2008020,0x8020080,0x88402208,0x82208808, + 0x2008020,0x1e088220,0x88220882,0x20002608,0x82208822,0x8822088,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x501c0264, + 0xa0001000,0x8001fc00,0x7000020,0x22020080,0x83e0082,0x20202207,0x80000020,0x1020,0xa4848220,0x80210802,0x9c2102,0x20c010, + 0x12425104,0x3c1043c0,0x8080841,0x41540802,0x804008,0x1000000,0x78220,0x40220f81,0x882202,0x40c008,0x12422088,0x22088100, + 0x60100881,0x41540805,0x406008,0x1849000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0xf0140200,0x880028,0x20e0a03f,0x709c800, + 0x201c0,0x60000881,0xa0000007,0xc0284b,0x122eb020,0x12048120,0x48120487,0x80802008,0x2008008,0x2008020,0x21094411,0x4411044, + 0x10204908,0x42108421,0x2022088,0x1e0781e0,0x781e0787,0xf8403e0f,0x83e0f808,0x2008020,0x22088220,0x88220882,0x21fc2a08,0x82208822, + 0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xf80a0294,0x40001000,0x80002000,0x20,0x22020100,0x8040082,0x20202200, + 0x80000018,0x1fc06020,0xa48fc220,0x80210802,0x842102,0x20a010,0x12425104,0x20104240,0x8080841,0x41541402,0x1004008,0x1000000, + 0x88220,0x40220801,0x882202,0x40a008,0x12422088,0x22088100,0x18100881,0x41540805,0x801008,0x2046000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x20280,0x401c0f80,0x80880028,0x20005001,0x94800,0x20000,0x880,0xa0000000,0x5015,0x4215040,0x3f0fc3f0,0xfc3f0fc8, + 0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,0x10505108,0x42108421,0x203c088,0x22088220,0x88220888,0x80402008,0x2008008, + 0x2008020,0x22088220,0x88220882,0x20002a08,0x82208822,0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa00a0494,0x60001000, + 0x80002004,0x8020,0x22020200,0x88040882,0x20402201,0x801006,0x18000,0x9f084220,0x40220802,0x442102,0x209010,0x10423088,0x20088220, + 0x8080840,0x80882202,0x2004008,0x1000000,0x88220,0x40220881,0x882202,0x409008,0x12422088,0x22088100,0x8100880,0x80881402, + 0x1001008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0x40220200,0x80700027,0x20002801,0x92800,0x1fc000,0x980, + 0xa0000000,0xa017,0x84417840,0x21084210,0x84210848,0x80402008,0x2008008,0x2008020,0x2208c220,0x88220882,0x20882208,0x42108421, + 0x2020088,0x22088220,0x88220888,0xc8402208,0x82208808,0x2008020,0x22088220,0x88220882,0x20203208,0x82208822,0x2022020,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xa03c0463,0x90000801,0x2004,0x8040,0x1c0703e0,0x70040701,0xc0401c06,0x801001,0x20020, + 0x400843c0,0x3c3c0f82,0x3c2107,0x1c0881e,0x10423070,0x20070210,0xf0080780,0x80882202,0x3e04004,0x1000000,0x783c0,0x381e0701, + 0x782202,0x408808,0x12422070,0x3c078100,0x700c0780,0x80882202,0x1e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0, + 0xf8000200,0x80080010,0x40000001,0x41000,0x0,0xe80,0xa0000000,0x21,0x8e21038,0x21084210,0x84210848,0xf83c3e0f,0x83e0f81c, + 0x701c070,0x3c08c1c0,0x701c0701,0xc0005c07,0x81e0781e,0x20200b0,0x1e0781e0,0x781e0787,0x30381c07,0x1c07008,0x2008020,0x1c0881c0, + 0x701c0701,0xc0201c07,0x81e0781e,0x203c020,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x801,0x4,0x40,0x0,0x0,0x0,0x1000, + 0x0,0x3c000000,0x0,0x0,0x0,0x0,0x10000,0x0,0x0,0x4004,0x1000000,0x0,0x0,0x80000,0x400000,0x0,0x20008000,0x0,0x4,0x1008,0x2000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x8008000f,0x80000000,0x3e000,0x0,0x800,0xa0000400,0x0,0x0,0x0,0x0,0x80000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100000,0x0,0x0,0x0,0x0,0x2000,0x0,0x4020040,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000, + 0x402,0x8,0x40,0x0,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x7004,0x70000fc,0x0,0x0,0x700000,0x800000,0x0,0x20008000, + 0x0,0x4,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x80f00000,0x0,0x0,0x0,0x800,0xa0001800,0x0,0x0,0x0,0x0, + 0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x4020040 + }; + + // Definition of a 8x17 font + const unsigned int font8x17[8*17*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x2400,0x2400,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20081834,0x1c0000,0x20081800,0x20081800,0x342008, + 0x18340000,0x200818,0x80000,0x0,0x180000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4200000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x380000,0x4000,0x2000c00,0x40100840,0x70000000,0x0,0x0,0x1c,0x10700000,0x7,0x0, + 0x1800,0x1800,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1010242c,0x14140000,0x10102414,0x10102414,0x2c1010,0x242c1400, + 0x101024,0x14100038,0x0,0x240000,0x0,0x0,0x30000000,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x8100000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x80000,0x10004000,0x2001000,0x40000040,0x10000000,0x0,0x0,0x10,0x10100000,0x4, + 0x0,0x18000000,0x0,0x0,0x0,0x34002400,0x2400,0x0,0x0,0x0,0x3c,0x0,0x8000000,0x0,0x60607800,0x0,0x140000,0x0,0x0,0x0,0x0,0x0, + 0x44,0x10081834,0x240000,0x10081800,0x10081800,0x1c341008,0x18340000,0x100818,0x84000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102812, + 0x8601c10,0x8100800,0x2,0x1c383e3e,0x67e1e7f,0x3e3c0000,0x38,0x1e087e1e,0x7c7f7f1e,0x417c1c42,0x4063611c,0x7e1c7e3e,0xfe414181, + 0x63827f10,0x40081000,0x8004000,0x2001000,0x40000040,0x10000000,0x0,0x10000000,0x10,0x10100000,0x3c000008,0x0,0x24003e00, + 0x3f007f00,0x0,0x0,0x2ce91800,0x1882,0x10101c,0xc2103c,0x143c3c00,0x3c00,0x18003c3c,0x10001f00,0x181c00,0x20200810,0x8080808, + 0x8083e1e,0x7f7f7f7f,0x7c7c7c7c,0x7c611c1c,0x1c1c1c00,0x1e414141,0x41824044,0x810242c,0x14180000,0x8102414,0x8102414,0x382c0810, + 0x242c1400,0x81024,0x14104014,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102816,0x3e902010,0x10084910,0x4,0x22084343,0xa402102,0x41620000, + 0x44,0x33144121,0x42404021,0x41100444,0x40636122,0x43224361,0x10416381,0x22440310,0x20082800,0x4000,0x2001000,0x40000040, + 0x10000000,0x0,0x10000000,0x10,0x10100000,0x24000008,0x0,0x606100,0x68000300,0x8106c,0x34000000,0x4f0000,0x44,0x101020,0x441040, + 0x420200,0x4200,0x24000404,0x7d00,0x82200,0x20203010,0x14141414,0x14082821,0x40404040,0x10101010,0x42612222,0x22222200,0x23414141, + 0x41447e48,0x0,0x0,0x0,0x0,0x4000000,0x18,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10287f,0x49902010,0x10083e10,0x4,0x41080101, + 0x1a404002,0x41411818,0x1004004,0x21144140,0x41404040,0x41100448,0x40555141,0x41414140,0x10412281,0x14280610,0x20084400,0x1c7c1c, + 0x3e3c7c3a,0x5c703844,0x107f5c3c,0x7c3e3c3c,0x7e424281,0x66427e10,0x10100000,0x40100008,0x1010,0xa04000,0x48100610,0x100c3024, + 0x24000000,0x4f3c00,0x2c107e28,0x3820,0x42281060,0x9d1e12,0xbd00,0x24100818,0x427d00,0x82248,0x20200800,0x14141414,0x14142840, + 0x40404040,0x10101010,0x41514141,0x41414142,0x43414141,0x41284350,0x1c1c1c1c,0x1c1c6c1c,0x3c3c3c3c,0x70707070,0x3c5c3c3c, + 0x3c3c3c18,0x3e424242,0x42427c42,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102824,0x48623010,0x10081c10,0x8,0x41080103,0x127c5e04, + 0x41411818,0xe7f3808,0x4f144140,0x41404040,0x41100450,0x40555141,0x41414160,0x1041225a,0x1c280410,0x1008c600,0x226622,0x66661066, + 0x62100848,0x10496266,0x66663242,0x10426681,0x24220260,0x100c0000,0xf8280008,0x1010,0x606000,0x48280428,0x28042014,0x48000000, + 0x494200,0x52280228,0x105420,0x3cee1058,0xa12236,0xa500,0x18101004,0x427d00,0x8226c,0x76767e10,0x14141414,0x14142840,0x40404040, + 0x10101010,0x41514141,0x41414124,0x45414141,0x41284150,0x22222222,0x22221222,0x66666666,0x10101010,0x66626666,0x66666600, + 0x66424242,0x42226622,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100024,0x381c4900,0x10086bfe,0x8,0x4908021c,0x22036304,0x3e630000, + 0x70000710,0x51227e40,0x417f7f43,0x7f100470,0x40554941,0x43417e3e,0x1041225a,0x8100810,0x10080000,0x24240,0x42421042,0x42100850, + 0x10494242,0x42422040,0x1042245a,0x18240410,0x10103900,0x407c003e,0x1818,0x1c3e10,0x4f7c087c,0x7c002010,0x48000000,0x4008, + 0x527c0410,0x105078,0x2410104c,0xa13e6c,0x7f00b900,0xfe3c3c,0x421d18,0x1c1c36,0x38383810,0x22222222,0x22144e40,0x7f7f7f7f, + 0x10101010,0xf1494141,0x41414118,0x49414141,0x4110435c,0x2020202,0x2021240,0x42424242,0x10101010,0x42424242,0x424242ff,0x4e424242, + 0x42244224,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000fe,0xe664d00,0x10080810,0x380010,0x41080c03,0x42014108,0x633d0000,0x70000710, + 0x51224140,0x41404041,0x41100448,0x40494541,0x7e414203,0x1041145a,0x14101010,0x10080000,0x3e4240,0x427e1042,0x42100870,0x10494242, + 0x4242203c,0x1042245a,0x18241810,0x10104600,0xf8f60008,0x1010,0x600320,0x48f610f6,0xf6000000,0x187eff,0x3c04,0x5ef61810,0x105020, + 0x24fe0064,0x9d006c,0x138ad00,0x100000,0x420518,0x36,0xc0c0c020,0x22222222,0x22224840,0x40404040,0x10101010,0x41454141,0x41414118, + 0x51414141,0x41107e46,0x3e3e3e3e,0x3e3e7e40,0x7e7e7e7e,0x10101010,0x42424242,0x42424200,0x5a424242,0x42244224,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x28,0x9094500,0x10080010,0x10,0x41081801,0x7f014118,0x41010000,0xe7f3800,0x513e4140,0x41404041,0x41100444, + 0x40414541,0x40414101,0x10411466,0x36103010,0x8080000,0x424240,0x42401042,0x42100848,0x10494242,0x42422002,0x10423c5a,0x18142010, + 0x10100000,0x407c0010,0x1010,0x260140,0x487c307c,0x7c000000,0x180000,0x202,0x507c2010,0x105020,0x3c10003c,0x423e36,0x1004200, + 0x100000,0x420500,0x3e6c,0x41e0440,0x3e3e3e3e,0x3e3e7840,0x40404040,0x10101010,0x41454141,0x41414124,0x61414141,0x41104042, + 0x42424242,0x42425040,0x40404040,0x10101010,0x42424242,0x42424218,0x72424242,0x42144214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100048, + 0x49096200,0x8100010,0x18001810,0x22082043,0x2432310,0x61421818,0x1004010,0x4f634121,0x42404021,0x41104444,0x40414322,0x40234143, + 0x10411466,0x22106010,0x8080000,0x466622,0x66621066,0x42100844,0x10494266,0x66662042,0x10461824,0x24184010,0x10100000,0x24381010, + 0x34001018,0xda4320,0x68386038,0x38000000,0x0,0x4204,0x50384010,0x105420,0x4210100c,0x3c0012,0x3c00,0x0,0x460500,0x48,0xc020c44, + 0x63636363,0x63228821,0x40404040,0x10101010,0x42432222,0x22222242,0x62414141,0x41104042,0x46464646,0x46465022,0x62626262, + 0x10101010,0x66426666,0x66666618,0x66464646,0x46186618,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100048,0x3e063d00,0x8100000,0x18001820, + 0x1c3e7f3e,0x23c1e20,0x3e3c1818,0x10,0x20417e1e,0x7c7f401e,0x417c3842,0x7f41431c,0x401e40be,0x103e0866,0x41107f10,0x4080000, + 0x3a5c1c,0x3a3c103a,0x427c0842,0xe49423c,0x7c3e203c,0xe3a1824,0x66087e10,0x10100000,0x3c103010,0x245a1010,0x5a3e10,0x3f107f10, + 0x10000000,0x0,0x3c08,0x2e107e10,0x1038fc,0x101004,0x0,0x0,0xfe0000,0x7f0500,0x0,0x14041438,0x41414141,0x41418e1e,0x7f7f7f7f, + 0x7c7c7c7c,0x7c431c1c,0x1c1c1c00,0xbc3e3e3e,0x3e10405c,0x3a3a3a3a,0x3a3a6e1c,0x3c3c3c3c,0x7c7c7c7c,0x3c423c3c,0x3c3c3c00, + 0x7c3a3a3a,0x3a087c08,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x4200000,0x10000020,0x0,0x0,0x10,0x0,0x30000000,0x0, + 0x0,0x0,0x60000,0x0,0x1c,0x4380000,0x0,0x2,0x800,0x0,0x40020000,0x0,0x8000c,0x10600000,0x2010,0x48000000,0x240000,0x0,0x0, + 0x0,0x0,0x0,0x1000,0x1078,0x0,0x0,0x0,0x400500,0x0,0x1e081e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x84008,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x0,0x20000040,0x0,0x0,0x20,0x0,0x1e000000,0x0,0x0,0x0,0x20000,0x0, + 0x0,0x2000000,0x0,0x26,0x800,0x0,0x40020000,0x0,0x100000,0x10000000,0x2030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x1000,0x0, + 0x0,0x0,0x400000,0x8000000,0x41e0400,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x104010,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x1c,0x7000,0x0,0x40020000,0x0,0x300000, + 0x0,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x0,0x0,0x0,0x400000,0x38000000,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1c,0x0,0x0,0x0,0x0,0x0,0x304030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 10x19 font + const unsigned int font10x19[10*19*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3600000,0x36000,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x180181c0,0xe81b0300,0x1801,0x81c06c18,0x181c06c,0xe8180,0x181c0e81,0xb0000006,0x60701b,0x1800000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x1c000,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xc030360,0xb81b0480,0xc03,0x3606c0c,0x303606c,0xb80c0,0x30360b81,0xb0000003,0xc0d81b,0x3000000,0x0, + 0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x0,0x0,0x2200000, + 0x22000,0x0,0x0,0x0,0x0,0x0,0x0,0x30000,0x0,0xe0,0x38078000,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3000c080,0x480,0x3000, + 0xc0800030,0xc08000,0x300,0xc080000,0xc,0x302000,0xc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x41c01,0xe020060c, + 0x800000,0x4,0x1e0703e0,0xf8060fc1,0xe1fe1e07,0x80000000,0x78,0x307e0,0x3c7c1fe7,0xf83c408f,0x80f10440,0x18660878,0x7e0787e0, + 0x78ff9024,0xa0140a0,0x27f83840,0x700e000,0x18000400,0x8000,0x70004002,0x410078,0x0,0x0,0x0,0x0,0x1808,0xc000000,0xf000000, + 0xe000000,0x1400,0x1e0001f,0x8007f800,0x0,0x0,0x3a3b,0x61400000,0x14202,0x20000,0x38002020,0x3c1b00,0x3e00000,0xf8,0x1c0001c0, + 0x78060001,0xf800000e,0x1e00020,0x8004020,0xc0300c0,0x300c0301,0xf83c7f9f,0xe7f9fe3e,0xf83e0f8,0x7c1821e0,0x781e0781,0xe0001f10, + 0x24090240,0xa02400f8,0x18018140,0xe81b0480,0x1801,0x81406c18,0x181406c,0x190e8180,0x18140e81,0xb0000006,0x60501b,0x184006c, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x26042202,0x200c06,0x800000,0x8,0x210d0611,0x40e0803,0x10026188,0x40000000, + 0x8c,0xf030418,0xc6431004,0xc64082,0x110840,0x18660884,0x41084410,0x8c081024,0xa012110,0x40082020,0x101b000,0xc000400,0x8000, + 0x80004002,0x410008,0x0,0x0,0x100000,0x0,0x2008,0x2000000,0x18800000,0x10000000,0x2200,0x2300024,0x800,0x0,0x0,0x2e13,0x60800000, + 0x8104,0x20040,0x64001040,0x80401b07,0x80100000,0x1e000,0x22000020,0x40c0003,0xc8000002,0x3300020,0x8004020,0xc0300c0,0x300c0301, + 0x40c64010,0x4010008,0x2008020,0x43182210,0x84210842,0x10002190,0x24090240,0x9044018c,0xc030220,0xb81b0300,0xc03,0x2206c0c, + 0x302206c,0x1e0b80c0,0x30220b81,0xb0000003,0xc0881b,0x304006c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x241f2202, + 0x200802,0x4900000,0x8,0x21010408,0x20a0802,0x44090,0x20000000,0x4,0x11878408,0x80411004,0x804082,0x111040,0x1ce50986,0x40986409, + 0x81022,0x12012108,0x80102020,0x1031800,0x400,0x8000,0x80004000,0x10008,0x0,0x0,0x100000,0x0,0x2008,0x2000000,0x10000000, + 0x10000000,0x18,0x4000044,0x1000,0x30180,0xd81b0000,0x13,0xe0000000,0x88,0x40,0x400018c0,0x80400018,0x61f00000,0x61800,0x22020020, + 0x4000007,0xc8000002,0x2100020,0x8038000,0x1e0781e0,0x781e0301,0x40804010,0x4010008,0x2008020,0x41142619,0x86619866,0x18002190, + 0x24090240,0x8887e104,0x0,0x0,0x0,0x0,0x0,0x2000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x2434a202, + 0x200802,0x3e00000,0x10,0x40810008,0x21a0804,0x44090,0x20000000,0x80040004,0x20848409,0x409004,0x1004082,0x112040,0x14a50902, + 0x40902409,0x81022,0x11321208,0x80202010,0x1060c00,0x7c5e0,0x781e8783,0xf07a5f0e,0x1c10808,0xfc5f078,0x5e07a170,0x7c7e1024, + 0xa016190,0x27f82008,0x2000000,0x20000000,0x10000000,0x80200024,0x4000044,0x2000,0x18180,0xc8320000,0x12,0xa1f00037,0x7f888, + 0x1e0,0x40410880,0x80600017,0xa2100000,0x5e800,0x22020040,0x38001027,0xc8000002,0x2100020,0x8004020,0x12048120,0x48120482, + 0x41004010,0x4010008,0x2008020,0x40942409,0x2409024,0x9044390,0x24090240,0x88841918,0x1f07c1f0,0x7c1f07c3,0x70781e07,0x81e07838, + 0xe0380e0,0x1f17c1e0,0x781e0781,0xe0001f90,0x24090240,0x9025e102,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xff241c41, + 0x1001,0x1c02000,0x10,0x40810008,0x6120f85,0xe0086190,0x20c03007,0x8007800c,0x27848419,0x409004,0x1004082,0x114040,0x14a48902, + 0x40902409,0x81022,0x11321205,0x602010,0x1000000,0x86610,0x84218840,0x80866182,0x411008,0x9261884,0x61086189,0x82101022,0x12012108, + 0x40082008,0x2000000,0x20030000,0x20000000,0x80200024,0x4000044,0x3006030,0xc018100,0x4c260000,0x12,0x26080048,0x83000850, + 0x20250,0x403e0500,0x8078002c,0x12302200,0x92400,0x1c0200c0,0x4001027,0xc8000002,0x3308820,0x8004020,0x12048120,0x48120482, + 0x41004010,0x4010008,0x2008020,0x40922409,0x2409024,0x8884690,0x24090240,0x85040920,0x21886218,0x86218860,0x88842108,0x42108408, + 0x2008020,0x21186210,0x84210842,0x10302190,0x24090240,0x88461084,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x4c240182, + 0x80001001,0x6b02000,0x20,0x4c810010,0x78220846,0x10081e10,0x20c0301c,0x1fe0e018,0x4d8487e1,0x409fe7,0xf9007f82,0x11a040, + 0x13248902,0x41102418,0xe0081022,0x11320c05,0x402008,0x1000000,0x2409,0x409020,0x81024082,0x412008,0x9240902,0x40902101,0x101022, + 0x11321208,0x40102008,0x2000000,0x7e0c8000,0xfc000003,0xf0fc0018,0x43802047,0x8c8040c8,0x32008300,0x44240000,0x0,0x4000048, + 0x8c801050,0x20440,0x40221dc0,0x808c0028,0x11d0667f,0x8009c400,0x1fc180,0x4001023,0xc8300002,0x1e0ccfb,0x3ec7b020,0x12048120, + 0x48120482,0x79007f9f,0xe7f9fe08,0x2008020,0xf0922409,0x2409024,0x8504490,0x24090240,0x85040920,0x802008,0x2008020,0x89004090, + 0x24090208,0x2008020,0x40902409,0x2409024,0x8304390,0x24090240,0x88440884,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000, + 0x481c0606,0xc8001001,0x802000,0x20,0x4c810020,0x4220024,0x8102108,0x60000070,0x3820,0x48884419,0x409004,0x10e4082,0x112040, + 0x13244902,0x7e1027e0,0x3c081021,0x21320c02,0x802008,0x1000000,0x7e409,0x409020,0x81024082,0x414008,0x9240902,0x40902101, + 0x80101022,0x11320c08,0x40202008,0x2038800,0x200bc000,0x20000000,0x80200003,0x80f04044,0xbc080bc,0x2f000200,0x0,0x0,0x6001048, + 0x8bc02020,0x20441,0xf8220200,0x80820028,0x1000cc00,0x80094400,0x201e0,0x78001021,0xc830000f,0x8000663c,0xf03c0c0,0x21084210, + 0x84210846,0x41004010,0x4010008,0x2008020,0x40912409,0x2409024,0x8204890,0x24090240,0x82040930,0x1f87e1f8,0x7e1f87e0,0x89004090, + 0x24090208,0x2008020,0x40902409,0x2409024,0x8004690,0x24090240,0x88440884,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000, + 0x480719c4,0x48001001,0x81fc00,0x7800020,0x40810040,0x2420024,0x8104087,0xa0000070,0x3820,0x48884409,0x409004,0x1024082,0x111040, + 0x13244902,0x40102410,0x2081021,0x214a1202,0x1802008,0x1000000,0x182409,0x409fe0,0x81024082,0x41a008,0x9240902,0x40902100, + 0xf8101021,0x214a0c04,0x80c0c008,0x1847000,0x7c1ee000,0x20000000,0x8020000c,0x8c044,0x1ee181ee,0x7b800000,0x707,0xf3ff0000, + 0x3e0084f,0x9ee0c020,0x20440,0x40221fc0,0xc2002c,0x13f11000,0x87892400,0x20000,0x1020,0x48000000,0x3f011c6,0x31cc6180,0x21084210, + 0x84210844,0x41004010,0x4010008,0x2008020,0x40912409,0x2409024,0x8505090,0x24090240,0x8204191c,0x60982609,0x82609823,0xf9007f9f, + 0xe7f9fe08,0x2008020,0x40902409,0x2409024,0x9fe4c90,0x24090240,0x84840848,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xfe048224, + 0x28001001,0x2000,0x40,0x40810080,0x27f8024,0x8104080,0x2000001c,0x1fe0e020,0x488fc409,0x409004,0x1024082,0x110840,0x10242902, + 0x40102408,0x2081021,0x214a1202,0x1002004,0x1000000,0x102409,0x409000,0x81024082,0x411008,0x9240902,0x40902100,0x6101021, + 0x214a0c04,0x81002008,0x2000000,0x201dc000,0x20000000,0x80200000,0x98044,0x1dc101dc,0x77000000,0x700,0x0,0x180448,0x1dc10020, + 0x20440,0x403e0200,0x620017,0xa000cc00,0x80052800,0x20000,0x1020,0x48000000,0x6606,0x206100,0x3f0fc3f0,0xfc3f0fc7,0xc1004010, + 0x4010008,0x2008020,0x4090a409,0x2409024,0x8886090,0x24090240,0x8207e106,0x40902409,0x2409024,0x81004010,0x4010008,0x2008020, + 0x40902409,0x2409024,0x8005890,0x24090240,0x84840848,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98048224,0x30001001,0x2000, + 0x40,0x21010100,0x2020024,0x8204080,0x40000007,0x80078000,0x48884408,0x80411004,0x824082,0x110840,0x10242986,0x40086409,0x2081021, + 0xe14a2102,0x2002004,0x1000000,0x106409,0x409000,0x81024082,0x410808,0x9240902,0x40902100,0x2101021,0x214a1202,0x82002008, + 0x2000000,0x300f8000,0x20000000,0x80fc001d,0xe4088044,0xf8200f8,0x3e000000,0x300,0x0,0x80c48,0xf820020,0x20640,0x40410200, + 0x803c0018,0x60006600,0x61800,0x0,0x1020,0x48000000,0xcc0a,0x20a100,0x21084210,0x84210844,0x40804010,0x4010008,0x2008020, + 0x4110a619,0x86619866,0x19046110,0x24090240,0x82040102,0x41906419,0x6419064,0x81004010,0x4010008,0x2008020,0x40902409,0x2409024, + 0x8307090,0x24090240,0x82840828,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x90248222,0x30000802,0x200c,0xc080,0x21010301, + 0x4021042,0x10202108,0xc0c03000,0x80040020,0x4d902418,0xc6431004,0xc24082,0x6210440,0x10241884,0x40084409,0x86080840,0xc0842102, + 0x4002002,0x1000000,0x18e610,0x84218820,0x80864082,0x410408,0x9240884,0x61086101,0x6101860,0xc0842103,0x4002008,0x2000000, + 0x10850180,0x20330000,0x80200013,0x26184024,0x5040050,0x14000000,0x0,0x0,0x4180848,0x85040020,0x20350,0x40000200,0x800c0007, + 0x80002200,0x1e000,0x0,0x1860,0x48000000,0x880a,0x40a188,0x40902409,0x2409028,0x40c64010,0x4010008,0x2008020,0x43106210,0x84210842, + 0x10006108,0x42108421,0x2040102,0x6398e639,0x8e6398e4,0x88842088,0x22088208,0x2008020,0x21102210,0x84210842,0x10306118,0x66198661, + 0x83061030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0x901f01c1,0xe8000802,0xc,0xc080,0x1e07c7f8,0xf8020f81,0xe0401e07, + 0x80c03000,0x20,0x279027e0,0x3c7c1fe4,0x3c408f,0x83c1027f,0x90241878,0x4007c404,0xf8080780,0xc0844082,0x7f82002,0x1000000, + 0xfa5e0,0x781e87c0,0x807a409f,0xc0410207,0x9240878,0x5e07a100,0xf80e0fa0,0xc0846183,0x7f82008,0x2000000,0xf020100,0x40321360, + 0x80200014,0xa3e0201f,0x8207f820,0x8000000,0x0,0x0,0x3e01037,0x207f820,0x201e1,0xfc000200,0x80040000,0x0,0x0,0x1fc000,0x17b0, + 0x48000000,0x12,0xc120f0,0x40902409,0x2409028,0x783c7f9f,0xe7f9fe3e,0xf83e0f8,0x7c1061e0,0x781e0781,0xe000be07,0x81e0781e, + 0x204017c,0x3e8fa3e8,0xfa3e8fa3,0x70781f07,0xc1f07c7f,0x1fc7f1fc,0x1e1021e0,0x781e0781,0xe0007e0f,0xa3e8fa3e,0x8305e030,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000,0xc06,0xc,0x100,0x0,0x0,0x0,0x3000,0x0,0x20000000,0x0,0x0,0x0,0x0,0xc000, + 0x0,0x0,0x2001,0x1000000,0x0,0x0,0x20000,0x400000,0x0,0x40002000,0x0,0x1,0x2008,0x2000000,0x100,0x40240000,0x80200008,0x40000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x80040000,0x0,0x0,0x0,0x1000,0x48000000,0x1f,0x181f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1040010,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000,0x60c,0x18,0x0, + 0x0,0x0,0x0,0x6000,0x0,0x10000000,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x3800,0x7000000,0x0,0x0,0x840000,0x400000,0x0,0x40002000, + 0x0,0x2,0x2008,0x2000000,0x200,0x40440000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x80780000,0x0,0x0,0x0,0x1000,0x48000400, + 0x2,0x1e02000,0x0,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x2040020,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x4000,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x780000,0x3800000,0x0,0x40002000,0x0,0xe,0x1808,0xc000000,0x3,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000, + 0x0,0x0,0x0,0x1000,0x1c00,0x0,0x0,0x0,0x0,0x380000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x380000,0x0,0x0,0x0,0x0,0x0,0x0,0xe0400e0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 12x24 font + const unsigned int font12x24[12*24*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x80000000,0x198000,0x0,0x0,0x0,0x0, + 0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc001806,0xc81980,0x60000000,0xc001806,0x1980c00,0x18060198,0xc80c, + 0x180600,0xc8198000,0xc001,0x80601980,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x600300f,0x1301980,0x90000000,0x600300f,0x1980600,0x300f0198,0x13006,0x300f01,0x30198000,0x6003, + 0xf01980,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x60000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7007,0x3c0000,0x3006019, + 0x80000000,0x90000000,0x3006019,0x80000300,0x60198000,0x3,0x601980,0x0,0x3006,0x1980000,0x60000000,0x0,0x0,0xe0000000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000000, + 0x0,0x0,0x0,0x0,0x0,0xc800019,0x80000000,0x198000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x1001,0x420000,0x0,0x0,0x90000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000c06,0xc80001,0x10000000,0x18000c06,0x1800,0xc060000,0xc818,0xc0600,0xc8000000, + 0x18000,0xc0600000,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80660207,0x800f8060,0x300c004,0x0,0x6, + 0xe00703f,0x3f00383,0xf80f07fc,0x1f01f000,0x0,0xf8,0x607f,0x7c7e07,0xfe7fe0f8,0x6063fc1f,0x86066007,0xe7060f0,0x7f80f07f, + 0x81f8fff6,0x6606c03,0x70ee077f,0xe0786000,0xf0070000,0xc000060,0xc0,0x3e000,0x60006003,0x600fc00,0x0,0x0,0x0,0x0,0x0,0x3c0603, + 0xc0000000,0x7800000,0xf0000,0x0,0xf00001f,0x80001fe0,0x7fe000,0x0,0x0,0x0,0x168fe609,0x0,0x90e07,0x6000,0x3c000e,0x70000f8, + 0x1980001f,0x0,0x1f8,0xf00000f,0xf00180,0xfe000,0xe00e,0x1001,0x20060,0x6006006,0x600600,0x600fe07c,0x7fe7fe7f,0xe7fe3fc3, + 0xfc3fc3fc,0x7e07060f,0xf00f00,0xf00f0000,0xf360660,0x6606606e,0x76001e0,0xc00180f,0x1681981,0x10000000,0xc00180f,0x1980c00, + 0x180f0198,0x3801680c,0x180f01,0x68198000,0xc001,0x80f01980,0x18600198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019, + 0x8044020c,0xc01f8060,0x2004004,0x0,0xc,0x3f81f07f,0x87f80383,0xf81f87fc,0x3f83f800,0x0,0x1fc,0x780607f,0x81fe7f87,0xfe7fe1fc, + 0x6063fc1f,0x860c6007,0xe7061f8,0x7fc1f87f,0xc3fcfff6,0x6606c03,0x30c6067f,0xe0783000,0xf00d8000,0x6000060,0xc0,0x7e000,0x60006003, + 0x600fc00,0x0,0x0,0xc00,0x0,0x0,0x7c0603,0xe0000000,0xfc00000,0x1f0000,0x0,0x900003f,0xc0003fe0,0x7fe000,0x0,0x0,0x0,0x1302660f, + 0x0,0xf0606,0x6004,0x7e0006,0x60601f8,0x19800001,0x80000000,0x1f8,0x19800010,0x81080300,0x3f2000,0x2011,0x1001,0x1c0060,0x6006006, + 0x600600,0x601fe1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f87061f,0x81f81f81,0xf81f8000,0x3fa60660,0x66066066,0x66003f0,0x6003009, + 0x1301981,0x10000000,0x6003009,0x1980600,0x30090198,0x1f013006,0x300901,0x30198000,0x6003,0x901980,0x30600198,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc0f8c,0xc0180060,0x6006044,0x40000000,0xc,0x3181b041,0xc41c0783,0x388018, + 0x71c71800,0x0,0x106,0x18c0f061,0xc38261c6,0x600384,0x60606001,0x86186007,0xe78630c,0x60e30c60,0xe7040606,0x630cc03,0x39c30c00, + 0xc0603000,0x3018c000,0x3000060,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,0x60000000,0x18400000,0x180000, + 0x0,0x19800070,0x40003600,0xc000,0x0,0x0,0x0,0x25a06,0x0,0x6030c,0x4,0xe20007,0xe060180,0xf000,0x80000000,0xf0000,0x10800000, + 0x80080600,0x7f2000,0x2020,0x80001001,0x20000,0xf00f00f,0xf00f00,0x601b0382,0x60060060,0x6000600,0x60060060,0x61c78630,0xc30c30c3, + 0xc30c000,0x30e60660,0x66066063,0xc600738,0x3006019,0x80000000,0xe0000000,0x3006019,0x80000300,0x60198000,0x3e000003,0x601980, + 0x0,0x3006,0x1980000,0x60600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc1fcc,0xc0180060,0x6006035,0x80000000, + 0x18,0x71c03000,0xc00c0583,0x300018,0x60c60c00,0x0,0x6,0x3060f060,0xc30060c6,0x600300,0x60606001,0x86306007,0x9e78670e,0x60670e60, + 0x66000606,0x630c606,0x19830c01,0xc0601800,0x30306000,0x60,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600, + 0x60000000,0x18000000,0x300000,0x0,0x78060,0x6600,0x1c000,0x300c,0x39819c0,0x0,0x25a00,0x0,0x30c,0x4,0xc00003,0xc060180,0x30c1f, + 0x80000000,0x30c000,0x10800001,0x80700000,0x7f2000,0x2020,0x80001001,0x20060,0xf00f00f,0xf00f00,0xf01b0300,0x60060060,0x6000600, + 0x60060060,0x60c78670,0xe70e70e7,0xe70e000,0x70c60660,0x66066063,0xc7f8618,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0, + 0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x87ff3a4c,0xc0180060,0x400600e,0x600000,0x18,0x60c03000, + 0xc00c0d83,0x700018,0x60c60c00,0x20,0x400006,0x3060f060,0xc6006066,0x600600,0x60606001,0x86606006,0x966c6606,0x60660660,0x66000606, + 0x630c666,0xf019801,0x80601800,0x30603000,0x1f06f,0xf01ec0,0xf03fe1ec,0x6703e01f,0x61c0c06,0xdc6701f0,0x6f01ec0c,0xe1f87fc6, + 0xc60cc03,0x71c60c7f,0xc0600600,0x60000000,0x30000000,0x300000,0x40040,0x88060,0x6600,0x18000,0x300c,0x1981980,0x0,0x2421f, + 0x80003ce0,0x7fc198,0x601f,0xc02021,0x980600c0,0x40230,0x80000000,0x402000,0x19806003,0x80006,0xc7f2000,0x2020,0x80001001, + 0x420060,0xf00f00f,0xf00f00,0xf01b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x6606208,0x60e60660,0x66066061, + 0x987fc670,0x1f01f01f,0x1f01f01,0xf039c0f0,0xf00f00f,0xf03e03,0xe03e03e0,0x1f06701f,0x1f01f01,0xf01f0060,0x1e660c60,0xc60c60c6, + 0xc6f060c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x7ff3207,0x8c0c0000,0xc00300e,0x600000,0x30,0x60c03000, + 0xc01c0983,0xf0600030,0x31860c06,0x6001e0,0x78000e,0x23e1f861,0xc6006066,0x600600,0x60606001,0x86c06006,0x966c6606,0x60660660, + 0xe7000606,0x630c666,0xf01f803,0x600c00,0x30000000,0x3f87f,0x83f83fc3,0xf83fe3fc,0x7f83e01f,0x6380c07,0xfe7f83f8,0x7f83fc0d, + 0xf3fc7fc6,0xc71cc03,0x3183187f,0xc0600600,0x60000000,0xff806000,0x300000,0x40040,0x88070,0x6600,0x60030060,0x6001818,0x1883180, + 0x0,0x2423f,0xc0007ff0,0x607fc1f8,0x603f,0x80c01fc1,0xf80601e0,0x5f220,0x80420000,0x5f2000,0xf006006,0x80006,0xc7f2000,0x2020, + 0x82107c07,0xc03c0060,0x1f81f81f,0x81f81f80,0xf03b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x660671c,0x61660660, + 0x66066061,0xf860e6c0,0x3f83f83f,0x83f83f83,0xf87fe3f8,0x3f83f83f,0x83f83e03,0xe03e03e0,0x3f87f83f,0x83f83f83,0xf83f8060, + 0x3fc60c60,0xc60c60c3,0x187f8318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x883200,0x300c0000,0xc003035,0x80600000, + 0x30,0x66c03001,0xc0f81983,0xf86f0030,0x1f071c06,0x600787,0xfe1e001c,0x6261987f,0x86006067,0xfe7fc600,0x7fe06001,0x87c06006, + 0xf6646606,0x60e6067f,0xc3e00606,0x61986f6,0x600f007,0x600c00,0x30000000,0x21c71,0x830831c3,0x1c06031c,0x71c06003,0x6700c06, + 0x6671c318,0x71831c0f,0x16040c06,0xc318606,0x1b031803,0x80600600,0x60000000,0x30009000,0x300000,0x40040,0x7003e,0x67e0,0x90070090, + 0x9001818,0x8c3100,0x0,0x60,0x4000e730,0x900380f0,0x6034,0x80c018c7,0xfe060338,0xb0121,0x80c60000,0x909000,0x6008,0x1080006, + 0xc3f2000,0x2011,0x3180060,0x60060e0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0x60664660,0x66066066, + 0x66063b8,0x62660660,0x66066060,0xf06066c0,0x21c21c21,0xc21c21c2,0x1c466308,0x31c31c31,0xc31c0600,0x60060060,0x31871c31,0x83183183, + 0x18318000,0x71860c60,0xc60c60c3,0x18718318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1981a00,0xe03e0000,0xc003044, + 0x40600000,0x60,0x66c03001,0x80f03182,0x1c7f8030,0x3f83fc06,0x601e07,0xfe078038,0x6661987f,0x86006067,0xfe7fc61e,0x7fe06001, + 0x87e06006,0x66666606,0x7fc6067f,0x81f80606,0x61986f6,0x6006006,0x600600,0x30000000,0xc60,0xc60060c6,0xc06060c,0x60c06003, + 0x6e00c06,0x6660c60c,0x60c60c0e,0x6000c06,0xc318666,0x1f031803,0x600600,0x603c2000,0x30016800,0x1fe0000,0x1f81f8,0x1c1f,0x804067e1, + 0x68060168,0x16800810,0xc42300,0x0,0x60,0x20c331,0x68030060,0x6064,0x3fc1040,0xf006031c,0xa011e,0x818c7fe0,0x909000,0x7fe1f, + 0x80f00006,0xc0f2060,0xf80e,0x18c0780,0x780781c0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0xfc666660, + 0x66066066,0x66061f0,0x66660660,0x66066060,0x606066e0,0xc00c00,0xc00c00c0,0xc066600,0x60c60c60,0xc60c0600,0x60060060,0x60c60c60, + 0xc60c60c6,0xc60c000,0x61c60c60,0xc60c60c3,0x1860c318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1980f81,0x80373000, + 0xc003004,0x7fe0001,0xf0000060,0x60c03003,0x183180,0xc71c060,0x3181ec00,0x7000,0xe070,0x66619860,0xc6006066,0x60061e,0x60606001, + 0x87606006,0x66626606,0x7f860661,0xc01c0606,0x6198696,0xf00600e,0x600600,0x30000000,0x1fc60,0xc60060c7,0xfc06060c,0x60c06003, + 0x7c00c06,0x6660c60c,0x60c60c0c,0x7f00c06,0xc3b8666,0xe01b007,0x3c00600,0x3c7fe000,0xff03ec00,0x1fe0000,0x40040,0xe001,0xc0806603, + 0xec0e03ec,0x3ec00010,0x0,0x60000000,0x7f,0x10c3f3,0xec070060,0x6064,0x3fc1040,0x6000030c,0xa0100,0x3187fe1,0xf09f1000,0x7fe00, + 0x6,0xc012060,0x0,0xc63c03,0xc03c0380,0x19819819,0x81981981,0x98330600,0x60060060,0x6000600,0x60060060,0xfc662660,0x66066066, + 0x66060e0,0x6c660660,0x66066060,0x6060e630,0x1fc1fc1f,0xc1fc1fc1,0xfc3fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6, + 0xc60c7fe,0x62c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe02c6,0x3c633000,0xc003004, + 0x7fe0001,0xf00000c0,0x60c03006,0xc6180,0xc60c060,0x60c00c00,0x7000,0xe060,0x66639c60,0x66006066,0x600606,0x60606001,0x86306006, + 0x66636606,0x60060660,0xc0060606,0x61f8696,0xf00600c,0x600300,0x30000000,0x3fc60,0xc60060c7,0xfc06060c,0x60c06003,0x7c00c06, + 0x6660c60c,0x60c60c0c,0x1f80c06,0xc1b0666,0xe01b00e,0x3c00600,0x3c43c000,0x3007de00,0x600000,0x40040,0x30000,0x61006607,0xde0c07de, + 0x7de00000,0x0,0xf07fefff,0x1f,0x8008c3f7,0xde0e0060,0x6064,0xc01047,0xfe00018c,0xb013f,0x86300061,0xf0911000,0x6000,0x6, + 0xc012060,0x3f,0x8063c0cc,0x3cc0c700,0x39c39c39,0xc39c39c1,0x98630600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066, + 0x66061f0,0x78660660,0x66066060,0x607fc618,0x3fc3fc3f,0xc3fc3fc3,0xfc7fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6, + 0xc60c7fe,0x64c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe0260,0x6661b000,0xc003000, + 0x600000,0xc0,0x60c0300c,0xc7fe0,0xc60c060,0x60c01c00,0x1e07,0xfe078060,0x6663fc60,0x66006066,0x600606,0x60606001,0x86386006, + 0x6636606,0x60060660,0xe0060606,0x60f039c,0x1b806018,0x600300,0x30000000,0x70c60,0xc60060c6,0x6060c,0x60c06003,0x7600c06, + 0x6660c60c,0x60c60c0c,0x1c0c06,0xc1b03fc,0xe01f01c,0xe00600,0x70000000,0x3007fc00,0x600000,0x40040,0x0,0x62006607,0xfc1807fc, + 0x7fc00000,0x0,0xf0000000,0x1,0xc004c307,0xfc1c0060,0x6064,0xc018c0,0x600000d8,0x5f200,0x3180060,0x50a000,0x6000,0x6,0xc012000, + 0x0,0xc601c0,0x4201c600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,0x66063b8, + 0x70660660,0x66066060,0x607f860c,0x70c70c70,0xc70c70c7,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000, + 0x68c60c60,0xc60c60c1,0xf060c1f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3300260,0x6661e000,0xc003000,0x600000, + 0x180,0x71c03018,0xc7fe0,0xc60c0c0,0x60c01800,0x787,0xfe1e0060,0x6663fc60,0x630060c6,0x600306,0x60606001,0x86186006,0x661e70e, + 0x60070c60,0x60060606,0x60f039c,0x19806038,0x600180,0x30000000,0x60c60,0xc60060c6,0x6060c,0x60c06003,0x6700c06,0x6660c60c, + 0x60c60c0c,0xc0c06,0xc1b039c,0x1f00e018,0x600600,0x60000000,0x1803f800,0x600000,0x40040,0x39e00,0x63006603,0xf83803f8,0x3f800000, + 0x0,0x60000000,0x0,0xc00cc303,0xf8180060,0x6064,0xc01fc0,0x60060070,0x40200,0x18c0060,0x402000,0x6000,0x6,0xc012000,0x0,0x18c0140, + 0x2014600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0300,0x60060060,0x6000600,0x60060060,0x60c61e70,0xe70e70e7,0xe70e71c,0x60e60660,0x66066060, + 0x6060060c,0x60c60c60,0xc60c60c6,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,0x70c60c60,0xc60c60c0, + 0xe060c0e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33022e0,0x6670c000,0xc003000,0x600600,0x60180,0x31803030, + 0x41c0184,0x1831c0c0,0x71c23806,0x6001e0,0x780000,0x62630c60,0xe38261c6,0x600386,0x60606043,0x860c6006,0x661e30c,0x60030c60, + 0x740e0607,0xe0f039c,0x31c06030,0x600180,0x30000000,0x61c71,0x830831c3,0x406031c,0x60c06003,0x6300c06,0x6660c318,0x71831c0c, + 0x41c0c07,0x1c0e039c,0x1b00e030,0x600600,0x60000000,0x1c41b00e,0x601cc0,0x401f8,0x45240,0xe1803601,0xb03001b0,0x1b000000, + 0x0,0x0,0x41,0xc008e711,0xb0300060,0x6034,0x80c02020,0x60060030,0x30c00,0xc60000,0x30c000,0x0,0x7,0x1c012000,0x0,0x3180240, + 0x6024608,0x30c30c30,0xc30c30c3,0xc630382,0x60060060,0x6000600,0x60060060,0x61c61e30,0xc30c30c3,0xc30c208,0x70c70e70,0xe70e70e0, + 0x6060068c,0x61c61c61,0xc61c61c6,0x1cc62308,0x30430430,0x43040600,0x60060060,0x31860c31,0x83183183,0x18318060,0x31c71c71, + 0xc71c71c0,0xe07180e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2203fc0,0x663f6000,0x6006000,0x600600,0x60300, + 0x3f81fe7f,0xc7f80187,0xf83f80c0,0x3f83f006,0x600020,0x400060,0x33e6067f,0xc1fe7f87,0xfe6001fe,0x6063fc7f,0x60e7fe6,0x660e3f8, + 0x6001f860,0x37fc0603,0xfc06030c,0x30c0607f,0xe06000c0,0x30000000,0x7fc7f,0x83f83fc3,0xfc0603fc,0x60c7fe03,0x61807c6,0x6660c3f8, + 0x7f83fc0c,0x7f80fc3,0xfc0e039c,0x3180607f,0xc0600600,0x60000000,0xfc0e00c,0x601986,0x66040040,0x4527f,0xc0803fe0,0xe07fe0e0, + 0xe000000,0x0,0x0,0x7f,0x80107ff0,0xe07fc060,0x603f,0x83fe0000,0x60060018,0xf000,0x420000,0xf0000,0x7fe00,0x7,0xfe012000, + 0x0,0x2100640,0xc0643f8,0x60660660,0x66066067,0xec3e1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f860e3f,0x83f83f83,0xf83f8000, + 0x5fc3fc3f,0xc3fc3fc0,0x606006fc,0x7fc7fc7f,0xc7fc7fc7,0xfcffe3f8,0x3fc3fc3f,0xc3fc7fe7,0xfe7fe7fe,0x3f860c3f,0x83f83f83, + 0xf83f8060,0x7f83fc3f,0xc3fc3fc0,0x607f8060,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2201f80,0x3c1e7000,0x6006000, + 0x600,0x60300,0xe01fe7f,0xc3f00183,0xe01f0180,0x1f01e006,0x600000,0x60,0x3006067f,0x807c7e07,0xfe6000f8,0x6063fc3e,0x6067fe6, + 0x660e0f0,0x6000f060,0x3bf80601,0xf806030c,0x60e0607f,0xe06000c0,0x30000000,0x1ec6f,0xf01ec0,0xf80601ec,0x60c7fe03,0x61c03c6, + 0x6660c1f0,0x6f01ec0c,0x3f007c1,0xcc0e030c,0x71c0c07f,0xc0600600,0x60000000,0x7804018,0xe01186,0x66040040,0x39e3f,0x80401fe0, + 0x407fe040,0x4000000,0x0,0x0,0x3f,0x203ce0,0x407fc060,0x601f,0x3fe0000,0x60060018,0x0,0x0,0x0,0x7fe00,0x6,0xe6012000,0x0, + 0x7e0,0x1807e1f0,0x60660660,0x66066066,0x6c3e07c,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7e060e0f,0xf00f00,0xf00f0000,0x8f01f81f, + 0x81f81f80,0x60600670,0x1ec1ec1e,0xc1ec1ec1,0xec79c0f0,0xf80f80f,0x80f87fe7,0xfe7fe7fe,0x1f060c1f,0x1f01f01,0xf01f0000,0x4f01cc1c, + 0xc1cc1cc0,0xc06f00c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x6006000,0x600,0x600,0x0,0x0,0x0,0x0, + 0x600000,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x600060,0x30000000,0x0,0x0,0xc,0x3,0x0,0x0,0x60000c00,0x0, + 0x0,0xc000,0x600600,0x60000000,0x18,0xc03100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f8,0x0,0x0,0x0,0x0,0x6, + 0x12000,0x2000000,0x40,0x20004000,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0xc06000c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x2004000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000, + 0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0xc00,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x21c,0x3,0x0,0x0,0x60000c00,0x0,0x0,0xc000, + 0x7c0603,0xe0000000,0x10,0xc02300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f0,0x0,0x0,0x0,0x0,0x6,0x12000,0x1000000, + 0x40,0x7e004000,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc06000c0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x300c000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,0x0,0x7800000,0x0, + 0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x3f8,0x3e,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x3c0603,0xc0000000, + 0x10,0xfc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x60000,0x0,0x0,0x0,0x0,0x6,0x0,0x1000000,0x0,0x0,0x0,0x0, + 0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0, + 0x0,0x1f0,0x3c,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x600,0x0,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x6,0x0,0xe000000,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 16x32 font + const unsigned int font16x32[16*32*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70000e0,0x3c00730,0xe7001c0,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0x730,0x70000e0,0x3c00730, + 0xe700000,0x700,0xe003c0,0xe7000e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x18001c0,0x6600ff0,0xe7003e0,0x0,0x18001c0,0x6600e70,0x18001c0,0x6600e70,0xff0,0x18001c0,0x6600ff0,0xe700000,0x180, + 0x1c00660,0xe7001c0,0x0,0x0,0x0,0x380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000, + 0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00380, + 0xc300ce0,0xe700630,0x0,0x1c00380,0xc300e70,0x1c00380,0xc300e70,0xce0,0x1c00380,0xc300ce0,0xe700000,0x1c0,0x3800c30,0xe700380, + 0x0,0x0,0x0,0x7c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x700000,0x0,0x0,0x0,0x7c007c00,0x3e000000, + 0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000070,0x1800000,0xc60,0x0,0xe000070,0x1800000,0xe000070, + 0x1800000,0x0,0xe000070,0x1800000,0x0,0xe00,0x700180,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x800000,0x0,0x600600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x3f0,0xfc0,0x0,0x7000000,0x38000000,0x1c0000,0xfc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x7c, + 0x1801f00,0x0,0x0,0x1c,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7300000,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0xe700000, + 0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0x0,0xc000c00,0x43800000,0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0xf80,0x70000e0,0x3c00730,0xe700c60,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0xe000730,0x70000e0,0x3c00730,0xe700000,0x700, + 0xe003c0,0xe7000e0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300000,0x803c00,0x7c00180, + 0xc00300,0x1000000,0x0,0x1c,0x3c007c0,0xfc007e0,0xe01ff8,0x3f03ffc,0x7e007c0,0x0,0x0,0x7c0,0x1c0,0x7f8003f0,0x7f007ff8,0x7ff803f0, + 0x70381ffc,0xff0700e,0x7000783c,0x783807c0,0x7fc007c0,0x7fc00fc0,0x7fff7038,0x700ee007,0x780f780f,0x7ffc03f0,0x70000fc0,0x3c00000, + 0x3000000,0x38000000,0x1c0000,0x1fc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1801f80,0x0,0x1f80000, + 0x7e,0x0,0x0,0x2400000,0xfc00000,0x7ff0000,0x7ffc0000,0x0,0x0,0x0,0x0,0xf30fb0c,0x2400000,0x0,0x240780f,0x1c0,0xfc,0x780f, + 0x18003f0,0xe700000,0x7c00000,0x0,0xff0,0x3c00000,0x78007c0,0xc00000,0xff80000,0xf80,0x7c00000,0xc000c00,0x18001c0,0x1c001c0, + 0x1c001c0,0x1c003e0,0x7fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007838,0x7c007c0,0x7c007c0,0x7c00000,0x7c67038, + 0x70387038,0x7038780f,0x70001fe0,0x30000c0,0x2400f30,0xe700c60,0x0,0x30000c0,0x2400e70,0x30000c0,0x2400e70,0xf700f30,0x30000c0, + 0x2400f30,0xe700000,0x300,0xc00240,0xe7000c0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0, + 0x630018c,0x807e00,0xfe00180,0xc00300,0x1000000,0x0,0x38,0xff01fc0,0x3ff01ff0,0x1e01ff8,0x7f83ffc,0x1ff80ff0,0x0,0x0,0xff0, + 0x1f003e0,0x7fe00ff8,0x7fc07ff8,0x7ff80ff8,0x70381ffc,0xff0701c,0x7000783c,0x78381ff0,0x7fe01ff0,0x7fe01ff0,0x7fff7038,0x781ee007, + 0x3c1e380e,0x7ffc0380,0x380001c0,0x3c00000,0x1800000,0x38000000,0x1c0000,0x3c00000,0x380001c0,0xe01c00,0x3800000,0x0,0x0, + 0x0,0x7000000,0x0,0x0,0x1e0,0x18003c0,0x0,0x3fc0000,0x70,0x0,0x0,0x6600000,0x1ff00000,0x1fff0000,0x7ffc0000,0x0,0x0,0x0,0x0, + 0xcf0239c,0x3c00000,0x0,0x3c0380e,0x1c0,0x2001fe,0x380e,0x18007f8,0xe700000,0x8600000,0x0,0xff0,0x7e00000,0x8c00870,0x1800000, + 0x1ff80000,0x180,0xc600000,0xc000c00,0x38001c0,0x3e003e0,0x3e003e0,0x3e001c0,0x7fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc, + 0x7fc07838,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x1fec7038,0x70387038,0x7038380e,0x70003ce0,0x1800180,0x6600cf0,0xe7007c0,0x0, + 0x1800180,0x6600e70,0x1800180,0x6600e70,0x7c00cf0,0x1800180,0x6600cf0,0xe700000,0x180,0x1800660,0xe700180,0x38000e70,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630030c,0x3f0e700,0x1e200180,0x1800180,0x21100000,0x0, + 0x38,0x1e7819c0,0x38781038,0x1e01c00,0xf080038,0x1c381c38,0x0,0x0,0x1878,0x7fc03e0,0x70e01e18,0x70e07000,0x70001e18,0x703801c0, + 0x707038,0x70007c7c,0x7c381c70,0x70701c70,0x70703830,0x1c07038,0x381ce007,0x1c1c3c1e,0x3c0380,0x380001c0,0x7e00000,0xc00000, + 0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,0x70c0000,0xe0, + 0x0,0x0,0xc300000,0x38300000,0x3c700000,0x3c0000,0x0,0x0,0x0,0x0,0xce022f4,0x1800000,0x0,0x1803c1e,0x1c0,0x2003c2,0x3c1e, + 0x1800e08,0x7e0,0x300000,0x0,0x7e00000,0xe700000,0x600030,0x3000000,0x3f980000,0x180,0x18200000,0xc000c00,0x1e0001c0,0x3e003e0, + 0x3e003e0,0x3e003e0,0xfe01e18,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70e07c38,0x1c701c70,0x1c701c70,0x1c700000,0x3c787038, + 0x70387038,0x70383c1e,0x70003870,0xc00300,0xc300ce0,0x380,0x0,0xc00300,0xc300000,0xc00300,0xc300000,0xfc00ce0,0xc00300,0xc300ce0, + 0x0,0xc0,0x3000c30,0x300,0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630031c,0xff8c300, + 0x1c000180,0x1800180,0x39380000,0x0,0x70,0x1c3801c0,0x203c001c,0x3e01c00,0x1c000038,0x381c3838,0x0,0x0,0x1038,0xe0e03e0,0x70703c08, + 0x70707000,0x70003808,0x703801c0,0x707070,0x70007c7c,0x7c383838,0x70383838,0x70387010,0x1c07038,0x381c700e,0x1e3c1c1c,0x780380, + 0x1c0001c0,0xe700000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0, + 0x0,0xe000000,0xe0,0x0,0x1000100,0x3800,0x70100000,0x38700000,0x780000,0x1c0,0x7801ce0,0xe380000,0x0,0x2264,0x0,0x0,0x1c1c, + 0x0,0x200780,0x1c1c,0x1800c00,0x1818,0x7f00000,0x0,0x18180000,0xc300000,0x600070,0x0,0x7f980000,0x180,0x18300000,0xc000c00, + 0x3000000,0x3e003e0,0x3e003e0,0x3e003e0,0xee03c08,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838, + 0x38380000,0x38387038,0x70387038,0x70381c1c,0x7fc03870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc00000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0xe88c300,0x1c000180,0x38001c0, + 0xfe00180,0x0,0x70,0x1c3801c0,0x1c001c,0x6e01c00,0x1c000078,0x381c3818,0x0,0x40000,0x40000038,0x1c0607e0,0x70703800,0x70707000, + 0x70003800,0x703801c0,0x7070e0,0x70007c7c,0x7c383838,0x70383838,0x70387000,0x1c07038,0x381c700e,0xf780e38,0x700380,0x1c0001c0, + 0x1c380000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0, + 0xe000000,0xe0,0x0,0x1000100,0x4400,0x70000000,0x38700000,0x700000,0xe0,0x7001c70,0xe380000,0x0,0x2264,0x0,0x0,0xe38,0x0, + 0x200700,0xe38,0x1800c00,0x300c,0xc300000,0x0,0x300c0000,0xc300180,0x6003c0,0x0,0x7f980000,0x180,0x18300000,0xc000c00,0x1800000, + 0x7e007e0,0x7e007e0,0x7e003e0,0xee03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,0x38380000, + 0x38387038,0x70387038,0x70380e38,0x7ff039f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x40000,0x0,0x0,0x38000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0x1c80e700,0x1c000180,0x38001c0,0x3800180, + 0x0,0xe0,0x381c01c0,0x1c001c,0x6e01c00,0x38000070,0x381c381c,0x0,0x3c0000,0x78000078,0x38030770,0x70707800,0x70387000,0x70007000, + 0x703801c0,0x7071c0,0x7000745c,0x7638701c,0x7038701c,0x70387000,0x1c07038,0x1c38718e,0x7700f78,0xf00380,0xe0001c0,0x381c0000, + 0x7e0,0x39e003e0,0x79c03f0,0x3ffc079c,0x39e01fc0,0xfe01c1e,0x3807778,0x39e007e0,0x39e0079c,0x73c07e0,0x7ff83838,0x701ce007, + 0x783c701c,0x1ffc01c0,0x18001c0,0x0,0x1c000100,0xe0,0x0,0x1000100,0x4200,0x70000000,0x70700100,0xf00100,0x10000e0,0x7000c70, + 0xc700000,0x0,0x2204,0x7e00000,0x1e380100,0x1ffc0f78,0x0,0xf80700,0xf78,0x1800e00,0x63e6,0x18300000,0x0,0x6fe60000,0xe700180, + 0xc00060,0x3838,0x7f980000,0x180,0x18300000,0xc000c00,0x18001c0,0x7700770,0x7700770,0x77007f0,0xee07800,0x70007000,0x70007000, + 0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1008,0x707c7038,0x70387038,0x70380f78,0x707039c0,0x7e007e0,0x7e007e0, + 0x7e007e0,0x1f3c03e0,0x3f003f0,0x3f003f0,0x1fc01fc0,0x1fc01fc0,0x7f039e0,0x7e007e0,0x7e007e0,0x7e00380,0x7ce3838,0x38383838, + 0x3838701c,0x39e0701c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6307fff,0x1c807e0c,0xe000180, + 0x30000c0,0x3800180,0x0,0xe0,0x381c01c0,0x1c001c,0xce01fe0,0x38000070,0x381c381c,0x3800380,0xfc0000,0x7e0000f0,0x30030770, + 0x70707000,0x70387000,0x70007000,0x703801c0,0x707380,0x700076dc,0x7638701c,0x7038701c,0x70387800,0x1c07038,0x1c3873ce,0x7f00770, + 0xe00380,0xe0001c0,0x700e0000,0x1ff8,0x3ff00ff0,0xffc0ff8,0x3ffc0ffc,0x3bf01fc0,0xfe01c3c,0x3807f78,0x3bf00ff0,0x3ff00ffc, + 0x77e0ff0,0x7ff83838,0x3838e007,0x3c783838,0x1ffc01c0,0x18001c0,0x0,0x7ff00380,0x1e0,0x0,0x1000100,0x4200,0x78000000,0x70700380, + 0xe00380,0x3800060,0xe000e30,0x1c600000,0x0,0x2204,0xff00000,0x7f7c0380,0x1ffc0770,0x1c0,0x3fc0700,0x18040770,0x1800780,0x4e12, + 0x18300104,0x0,0x4c320000,0x7e00180,0x1c00030,0x3838,0x7f980000,0x180,0x18302080,0xc000c00,0x18001c0,0x7700770,0x7700770, + 0x7700770,0x1ee07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c381c,0x705c7038,0x70387038, + 0x70380770,0x70383b80,0x1ff81ff8,0x1ff81ff8,0x1ff81ff8,0x3fbe0ff0,0xff80ff8,0xff80ff8,0x1fc01fc0,0x1fc01fc0,0xff83bf0,0xff00ff0, + 0xff00ff0,0xff00380,0xffc3838,0x38383838,0x38383838,0x3ff03838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x1c0,0x7fff,0x1c803c38,0xf000000,0x70000e0,0xfe00180,0x0,0x1c0,0x381c01c0,0x3c0078,0xce01ff0,0x39e000f0,0x1c38381c,0x3800380, + 0x3e07ffc,0xf8001f0,0x307b0770,0x70e07000,0x70387000,0x70007000,0x703801c0,0x707700,0x700076dc,0x7638701c,0x7038701c,0x70387e00, + 0x1c07038,0x1c3873ce,0x3e007f0,0x1e00380,0x70001c0,0x0,0x1038,0x3c381e18,0x1c7c1e3c,0x3801e3c,0x3c7801c0,0xe01c78,0x380739c, + 0x3c781c38,0x3c381c3c,0x7c21e10,0x7003838,0x3838700e,0x1ef03838,0x3c01c0,0x18001c0,0x0,0x7fe007c0,0x1c0,0x0,0x1000100,0x6400, + 0x7e000000,0x707007c0,0x1e007c0,0x7c00070,0xe000638,0x18600000,0x0,0x0,0x1e100000,0x73ce07c0,0x3c07f0,0x1c0,0x7240700,0x1ddc3ffe, + 0x1800de0,0x8c01,0x1870030c,0x0,0x8c310000,0x3c00180,0x3800030,0x3838,0x7f980000,0x180,0x183030c0,0xc000c00,0x430001c0,0x7700770, + 0x7700770,0x7700770,0x1ce07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1c38,0x70dc7038, + 0x70387038,0x703807f0,0x70383b80,0x10381038,0x10381038,0x10381038,0x21e71e18,0x1e3c1e3c,0x1e3c1e3c,0x1c001c0,0x1c001c0,0x1e383c78, + 0x1c381c38,0x1c381c38,0x1c380380,0x1c383838,0x38383838,0x38383838,0x3c383838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0x1e8000e0,0x1f000000,0x70000e0,0x39380180,0x0,0x1c0,0x3b9c01c0,0x3c07f0,0x18e01078,0x3bf800e0, + 0x7e0383c,0x3800380,0x1f807ffc,0x3f001c0,0x61ff0e38,0x7fc07000,0x70387ff0,0x7ff07000,0x7ff801c0,0x707f00,0x7000729c,0x7338701c, + 0x7070701c,0x70703fc0,0x1c07038,0x1e7873ce,0x1c003e0,0x3c00380,0x70001c0,0x0,0x1c,0x3c381c00,0x1c3c1c1c,0x3801c3c,0x383801c0, + 0xe01cf0,0x380739c,0x38381c38,0x3c381c3c,0x7801c00,0x7003838,0x3838700e,0xfe03c78,0x7801c0,0x18001c0,0x0,0x1c000c20,0xff8, + 0x0,0x1ff01ff0,0x3818,0x3fc00100,0x707e0c20,0x3c00c20,0xc200030,0xc000618,0x18c00000,0x0,0x0,0x1c000080,0xe1ce0c20,0x7803e0, + 0x1c0,0xe200700,0xff83ffe,0x1801878,0x9801,0x1cf0071c,0x7ffc0000,0x8c310000,0x7ffe,0x7000030,0x3838,0x3f980380,0x180,0xc6038e0, + 0x7f9c7f9c,0x3e1c01c0,0xe380e38,0xe380e38,0xe380f78,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,0x1c001c0,0xfe387338,0x701c701c, + 0x701c701c,0x701c0e70,0x719c7038,0x70387038,0x703803e0,0x70383b80,0x1c001c,0x1c001c,0x1c001c,0xe71c00,0x1c1c1c1c,0x1c1c1c1c, + 0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380000,0x3c383838,0x38383838,0x38383c78,0x3c383c78,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0xf800380,0x3f830000,0x70000e0,0x31080180,0x0,0x380,0x3b9c01c0, + 0x7807e0,0x38e00038,0x3c3800e0,0xff01c3c,0x3800380,0x7c000000,0x7c03c0,0x61870e38,0x7fc07000,0x70387ff0,0x7ff070fc,0x7ff801c0, + 0x707f80,0x7000739c,0x7338701c,0x7ff0701c,0x7fe00ff0,0x1c07038,0xe7073ce,0x1c003e0,0x3800380,0x38001c0,0x0,0x1c,0x381c3800, + 0x381c380e,0x380381c,0x383801c0,0xe01de0,0x380739c,0x3838381c,0x381c381c,0x7001e00,0x7003838,0x1c70718e,0x7e01c70,0xf00380, + 0x18001e0,0x1e000000,0x1c001bb0,0xff8,0x0,0x1000100,0xe0,0xff00300,0x707e1bb0,0x3801bb0,0x1bb00010,0x8000308,0x30c00000,0x0, + 0x0,0x1e0000c0,0xe1ce1bb0,0xf003e0,0x1c0,0x1c203ff8,0x63003e0,0x180181c,0x9801,0xfb00e38,0x7ffc0000,0x8fc10000,0x7ffe,0xe000860, + 0x3838,0x1f980380,0x180,0x7c01c70,0x1f001f0,0x1f003c0,0xe380e38,0xe380e38,0xe380e38,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0, + 0x1c001c0,0xfe387338,0x701c701c,0x701c701c,0x701c07e0,0x731c7038,0x70387038,0x703803e0,0x70383980,0x1c001c,0x1c001c,0x1c001c, + 0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x387c3838,0x38383838,0x38381c70, + 0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc30,0x7f00e00,0x33c30000,0x70000e0,0x1007ffe, + 0x0,0x380,0x3b9c01c0,0xf00078,0x30e0001c,0x3c1c01c0,0x1c381fdc,0x0,0x70000000,0x1c0380,0x63030e38,0x70707000,0x70387000,0x700070fc, + 0x703801c0,0x707b80,0x7000739c,0x7338701c,0x7fc0701c,0x7fc001f0,0x1c07038,0xe703e5c,0x3e001c0,0x7800380,0x38001c0,0x0,0x7fc, + 0x381c3800,0x381c380e,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7001fc0,0x7003838,0x1c70718e,0x7c01c70, + 0xe01f00,0x180007c,0x7f8c0000,0x7fc03fb8,0x1c0,0x0,0x1000100,0x700,0x1f00600,0x70703fb8,0x7803fb8,0x3fb80000,0x8000000,0x180, + 0x0,0x0,0x1fc00060,0xe1ce3fb8,0xe001c0,0x1c0,0x1c203ff8,0xc1801c0,0x180c,0x9801,0x1c70,0xc0000,0x8cc10000,0x180,0xfe007c0, + 0x3838,0x7980380,0xff0,0xe38,0x3e003e00,0x3e000380,0xe380e38,0xe380e38,0xe380e38,0x38e07000,0x70007000,0x70007000,0x1c001c0, + 0x1c001c0,0x70387338,0x701c701c,0x701c701c,0x701c03c0,0x731c7038,0x70387038,0x703801c0,0x703838e0,0x7fc07fc,0x7fc07fc,0x7fc07fc, + 0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x38dc3838,0x38383838,0x38381c70, + 0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc60,0xf83878,0x71e30000,0x70000e0,0x1007ffe, + 0x7f0,0x380,0x381c01c0,0x1e0003c,0x60e0001c,0x381c01c0,0x381c079c,0x0,0x7c000000,0x7c0380,0x63031c1c,0x70307000,0x70387000, + 0x7000701c,0x703801c0,0x7071c0,0x7000739c,0x71b8701c,0x7000701c,0x71e00078,0x1c07038,0xe703e7c,0x7e001c0,0xf000380,0x38001c0, + 0x0,0x1ffc,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fc0,0x380739c,0x3838381c,0x381c381c,0x7000ff0,0x7003838,0x1ef03bdc, + 0x3800ee0,0x1e01f00,0x180007c,0x61fc0000,0x7fc07f3c,0x1c0,0x0,0x1000100,0x1800,0x780c00,0x70707f3c,0xf007f3c,0x7f3c0000,0x0, + 0x3c0,0x3ffcffff,0x0,0xff00030,0xe1fe7f3c,0x1e001c0,0x1c0,0x1c200700,0xc183ffe,0xe0c,0x9801,0x1ff038e0,0xc07f0,0x8c610000, + 0x180,0x0,0x3838,0x1980380,0x0,0x1ff0071c,0xe000e000,0xe0000f80,0x1c1c1c1c,0x1c1c1c1c,0x1c1c1e38,0x38e07000,0x70007000,0x70007000, + 0x1c001c0,0x1c001c0,0x703871b8,0x701c701c,0x701c701c,0x701c03c0,0x761c7038,0x70387038,0x703801c0,0x70703870,0x1ffc1ffc,0x1ffc1ffc, + 0x1ffc1ffc,0xfff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x389c3838,0x38383838, + 0x38380ee0,0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xfffc,0xbc60fc,0x70e30000,0x70000e0, + 0x180,0x7f0,0x700,0x381c01c0,0x3e0001c,0x7ffc001c,0x381c03c0,0x381c001c,0x0,0x1f807ffc,0x3f00380,0x63031ffc,0x70387000,0x70387000, + 0x7000701c,0x703801c0,0x7071e0,0x7000701c,0x71b8701c,0x7000701c,0x70f00038,0x1c07038,0x7e03e7c,0x77001c0,0xe000380,0x1c001c0, + 0x0,0x3c1c,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x70003f8,0x7003838,0xee03bdc, + 0x3c00ee0,0x3c00380,0x18000e0,0xf00000,0x1c007e7c,0x3c0,0x0,0x1000100,0x0,0x381800,0x70707e7c,0xe007e7c,0x7e7c0000,0x0,0x7c0, + 0x0,0x0,0x3f80018,0xe1fe7e7c,0x3c001c0,0x1c0,0x1c200700,0xc183ffe,0xf0c,0x8c01,0x38e0,0xc07f0,0x8c710000,0x180,0x0,0x3838, + 0x1980000,0x0,0x71c,0x7000f0,0x700f00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x3fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0, + 0x703871b8,0x701c701c,0x701c701c,0x701c07e0,0x7c1c7038,0x70387038,0x703801c0,0x7ff03838,0x3c1c3c1c,0x3c1c3c1c,0x3c1c3c1c, + 0x3fff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x391c3838,0x38383838,0x38380ee0, + 0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfffc,0x9c01ce,0x70f60000,0x70000e0,0x180, + 0x0,0x700,0x381c01c0,0x780001c,0x7ffc001c,0x381c0380,0x381c003c,0x0,0x3e07ffc,0xf800380,0x63031ffc,0x70387000,0x70387000, + 0x7000701c,0x703801c0,0x7070f0,0x7000701c,0x71b8701c,0x7000701c,0x70700038,0x1c07038,0x7e03e7c,0xf7801c0,0x1e000380,0x1c001c0, + 0x0,0x381c,0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7000078,0x7003838,0xee03a5c, + 0x7c00fe0,0x78001c0,0x18001c0,0x0,0x1c003ef8,0x380,0x0,0x1000100,0x810,0x383000,0x70703ef8,0x1e003ef8,0x3ef80000,0x0,0x7c0, + 0x0,0x0,0x78000c,0xe1c03ef8,0x78001c0,0x1c0,0x1c200700,0x63001c0,0x18003f8,0x4e12,0x1c70,0xc0000,0x4c320000,0x180,0x0,0x3838, + 0x1980000,0x0,0xe38,0x700118,0x701e00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x7fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0, + 0x703871b8,0x701c701c,0x701c701c,0x701c0e70,0x7c1c7038,0x70387038,0x703801c0,0x7fc0381c,0x381c381c,0x381c381c,0x381c381c, + 0x78e03800,0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x3b1c3838,0x38383838,0x38380fe0, + 0x381c0fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1860,0x9c0186,0x707e0000,0x30000c0,0x180, + 0x0,0xe00,0x183801c0,0xf00001c,0xe0001c,0x181c0380,0x381c0038,0x0,0xfc0000,0x7e000000,0x61873c1e,0x70383800,0x70707000,0x7000381c, + 0x703801c0,0x707070,0x7000701c,0x70f83838,0x70003838,0x70780038,0x1c07038,0x7e03c3c,0xe3801c0,0x1c000380,0xe001c0,0x0,0x381c, + 0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01ef0,0x380739c,0x3838381c,0x381c381c,0x7000038,0x7003838,0xfe03e7c,0xfe007c0, + 0x70001c0,0x18001c0,0x0,0xe001ff0,0x380,0x0,0x1000100,0x162c,0x381800,0x30701ff0,0x1c001ff0,0x1ff00000,0x0,0x3c0,0x0,0x0, + 0x380018,0xe1c01ff0,0x70001c0,0x1c0,0x1c200700,0xff801c0,0x18000f0,0x63e6,0xe38,0x0,0x6c3e0000,0x0,0x0,0x3838,0x1980000,0x0, + 0x1c70,0xf0000c,0xf01c00,0x3c1e3c1e,0x3c1e3c1e,0x3c1e3c1c,0x70e03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x707070f8, + 0x38383838,0x38383838,0x38381c38,0x38387038,0x70387038,0x703801c0,0x7000381c,0x381c381c,0x381c381c,0x381c381c,0x70e03800, + 0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0380,0x3e1c3838,0x38383838,0x383807c0,0x381c07c0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18c0,0x9c0186,0x783c0000,0x38001c0,0x180,0x3800000, + 0x3800e00,0x1c3801c0,0x1e00003c,0xe00038,0x1c1c0780,0x381c0038,0x3800380,0x3c0000,0x78000000,0x61ff380e,0x70383808,0x70707000, + 0x7000381c,0x703801c0,0x40707078,0x7000701c,0x70f83838,0x70003838,0x70384038,0x1c07038,0x7e03c3c,0x1e3c01c0,0x3c000380,0xe001c0, + 0x0,0x383c,0x3c381c00,0x1c3c1c00,0x3801c3c,0x383801c0,0xe01c78,0x380739c,0x38381c38,0x3c381c3c,0x7000038,0x7003878,0x7c01e78, + 0x1ef007c0,0xf0001c0,0x18001c0,0x0,0xe000ee0,0x7800380,0xe380000,0x1001ff0,0x2242,0x40380c00,0x38700ee0,0x3c000ee0,0xee00000, + 0x0,0x0,0x0,0x0,0x380030,0xe1c00ee0,0xf0001c0,0x1c0,0xe200700,0xdd801c0,0x1800038,0x300c,0x71c,0x0,0x300c0000,0x0,0x0,0x3838, + 0x1980000,0x0,0x38e0,0xb0000c,0xb01c08,0x380e380e,0x380e380e,0x380e380e,0x70e03808,0x70007000,0x70007000,0x1c001c0,0x1c001c0, + 0x707070f8,0x38383838,0x38383838,0x3838381c,0x38387038,0x70387038,0x703801c0,0x7000381c,0x383c383c,0x383c383c,0x383c383c, + 0x70e01c00,0x1c001c00,0x1c001c00,0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383878,0x38783878,0x387807c0, + 0x3c3807c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x18c0,0x10b801ce,0x3c3e0000,0x38001c0,0x180, + 0x3800000,0x3801c00,0x1e7801c0,0x3c002078,0xe02078,0x1c380700,0x1c3810f0,0x3800380,0x40000,0x40000380,0x307b380e,0x70701e18, + 0x70e07000,0x70001c1c,0x703801c0,0x60e0703c,0x7000701c,0x70f83c78,0x70003c70,0x703c70f0,0x1c03870,0x3c01c3c,0x3c1c01c0,0x78000380, + 0x7001c0,0x0,0x3c7c,0x3c381e18,0x1c7c1e0c,0x3801c3c,0x383801c0,0xe01c38,0x3c0739c,0x38381c38,0x3c381c3c,0x7001078,0x7803c78, + 0x7c01c38,0x1c780380,0x1e0001c0,0x18001c0,0x0,0x70c06c0,0x7000380,0xe300000,0x1000100,0x2142,0x70f00600,0x3c7006c0,0x780006c0, + 0x6c00000,0x0,0x0,0x0,0x0,0x10780060,0x73e206c0,0x1e0001c0,0x1c0,0x7240700,0x180c01c0,0x1800018,0x1818,0x30c,0x0,0x18180000, + 0x0,0x0,0x3c78,0x1980000,0x0,0x30c0,0x130000c,0x1301c18,0x380e380e,0x380e380e,0x380e380e,0x70e01e18,0x70007000,0x70007000, + 0x1c001c0,0x1c001c0,0x70e070f8,0x3c783c78,0x3c783c78,0x3c781008,0x7c783870,0x38703870,0x387001c0,0x70003a3c,0x3c7c3c7c,0x3c7c3c7c, + 0x3c7c3c7c,0x79f11e18,0x1e0c1e0c,0x1e0c1e0c,0x1c001c0,0x1c001c0,0x1c783838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383c78,0x3c783c78, + 0x3c780380,0x3c380380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x38c0,0x1ff800fc,0x1fee0000, + 0x1800180,0x180,0x3800000,0x3801c00,0xff01ffc,0x3ffc3ff0,0xe03ff0,0xff00700,0x1ff81fe0,0x3800380,0x0,0x380,0x3000780f,0x7ff00ff8, + 0x7fc07ff8,0x70000ffc,0x70381ffc,0x7fe0701c,0x7ff8701c,0x70781ff0,0x70001ff0,0x701c7ff0,0x1c01fe0,0x3c01c38,0x380e01c0,0x7ffc0380, + 0x7001c0,0x0,0x1fdc,0x3ff00ff0,0xffc0ffc,0x3800fdc,0x38383ffe,0xe01c3c,0x1fc739c,0x38380ff0,0x3ff00ffc,0x7001ff0,0x3f81fb8, + 0x7c01c38,0x3c3c0380,0x1ffc01c0,0x18001c0,0x0,0x3fc0380,0x7000380,0xc70718c,0x1000100,0x2244,0x7ff00200,0x1fff0380,0x7ffc0380, + 0x3800000,0x0,0x0,0x0,0x0,0x1ff000c0,0x7f7e0380,0x1ffc01c0,0x1c0,0x3fc3ffe,0x1c0,0x1800018,0x7e0,0x104,0x0,0x7e00000,0x7ffe, + 0x0,0x3fde,0x1980000,0x0,0x2080,0x3300018,0x3300ff0,0x780f780f,0x780f780f,0x780f780e,0xf0fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc, + 0x1ffc1ffc,0x7fc07078,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x7ff01fe0,0x1fe01fe0,0x1fe001c0,0x70003bf8,0x1fdc1fdc,0x1fdc1fdc, + 0x1fdc1fdc,0x3fbf0ff0,0xffc0ffc,0xffc0ffc,0x3ffe3ffe,0x3ffe3ffe,0xff03838,0xff00ff0,0xff00ff0,0xff00000,0x3ff01fb8,0x1fb81fb8, + 0x1fb80380,0x3ff00380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x31c0,0x7e00078,0x7cf0000,0x1800180, + 0x0,0x3800000,0x3803800,0x3c01ffc,0x3ffc0fe0,0xe01fc0,0x3e00e00,0x7e00f80,0x3800380,0x0,0x380,0x18007007,0x7fc003f0,0x7f007ff8, + 0x700003f0,0x70381ffc,0x3f80701e,0x7ff8701c,0x707807c0,0x700007c0,0x701e1fc0,0x1c00fc0,0x3c01818,0x780f01c0,0x7ffc0380,0x3801c0, + 0x0,0xf9c,0x39e003e0,0x79c03f0,0x380079c,0x38383ffe,0xe01c1e,0x7c739c,0x383807e0,0x39e0079c,0x7000fc0,0x1f80f38,0x3801c38, + 0x781e0380,0x1ffc01c0,0x18001c0,0x0,0x1f80100,0xe000700,0x1c60718c,0x1000100,0x1e3c,0x1fc00100,0x7ff0100,0x7ffc0100,0x1000000, + 0x0,0x0,0x0,0x0,0xfc00080,0x3e3c0100,0x1ffc01c0,0x1c0,0xf83ffe,0x1c0,0x1800838,0x0,0x0,0x0,0x0,0x7ffe,0x0,0x3b9e,0x1980000, + 0x0,0x0,0x2300038,0x23003e0,0x70077007,0x70077007,0x70077007,0xe0fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007078, + 0x7c007c0,0x7c007c0,0x7c00000,0xc7c00fc0,0xfc00fc0,0xfc001c0,0x700039f0,0xf9c0f9c,0xf9c0f9c,0xf9c0f9c,0x1f1e03e0,0x3f003f0, + 0x3f003f0,0x3ffe3ffe,0x3ffe3ffe,0x7e03838,0x7e007e0,0x7e007e0,0x7e00000,0x63e00f38,0xf380f38,0xf380380,0x39e00380,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x3000000,0x3800,0x0,0x0,0x0,0x0, + 0x0,0x300,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,0x380,0x3801c0,0x0,0x0,0x0,0x0,0x1c,0x0,0xe00000, + 0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1c0,0x18001c0,0x0,0x0,0xe000700,0x18600000,0x1000100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800ff0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0x1800000,0x0,0x6300070,0x6300000,0x0, + 0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000000, + 0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x7000000, + 0x7000,0x0,0x0,0x0,0x0,0x0,0x700,0x0,0x0,0xf040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x3f0,0x1c0fc0,0x0,0x0, + 0x0,0x0,0x1c,0x0,0xe00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1e0,0x18003c0,0x0,0x0,0xc000700,0x18c00000,0x1000000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x18007e0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000, + 0x0,0x7f800e0,0x7f80000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000, + 0x0,0x600600,0x0,0x6000000,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0, + 0x3f0,0xfc0,0x0,0x0,0x0,0x0,0x838,0x0,0x1e00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0xf00,0xfc,0x1801f80,0x0,0x0,0x8008e00,0x30c00000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000, + 0x0,0x3001c0,0x300000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0xf00,0x38000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0xff0,0x0,0x1fc00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3e00,0x7c,0x1801f00,0x0,0x0,0x800fe00,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7c00000,0x0,0x3001fc,0x300000, + 0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x3e00,0x38003e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x7e0,0x0,0x1f000000, + 0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3c00,0x0,0x1800000,0x0,0x0,0x7800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00,0x38003c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 19x38 font + const unsigned int font19x38[19*38*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c380000,0x0,0x1c380,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800007,0x3c003,0x86000000, + 0x1e00000,0x3,0x80000700,0x3c00000,0x380000,0x70003c00,0x0,0xe1800e,0x1c00,0xf000e18,0x0,0x0,0x700000e0,0x780000,0x7000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe700000,0x0,0xe700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0000e,0x7e003,0xe60071c0,0x7f80000,0x1,0xc0000e00,0x7e0038e,0x1c0000, + 0xe0007e00,0x38e00000,0xf98007,0x3800,0x1f800f98,0x1c70000,0x0,0x380001c0,0xfc0071,0xc000e000,0x0,0x0,0x0,0x0,0x3e00000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x7e00000,0x0,0x7e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0001c,0xe7006,0x7c0071c0,0xe180000,0x0,0xe0001c00,0xe70038e,0xe0001,0xc000e700,0x38e00000, + 0x19f0003,0x80007000,0x39c019f0,0x1c70000,0x0,0x1c000380,0x1ce0071,0xc001c000,0x0,0x0,0x0,0x0,0x7f00000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000, + 0x0,0x3c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x700038,0x1c3806,0x3c0071c0,0xc0c0000,0x0,0x70003800,0x1c38038e,0x70003,0x8001c380,0x38e00000,0x18f0001,0xc000e000, + 0x70e018f0,0x1c70000,0x0,0xe000700,0x3870071,0xc0038000,0x0,0x0,0x0,0x0,0xe380000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60000000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c38,0x0,0x1,0xc3800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xc0c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000003,0x80018000,0x0,0xc180000, + 0xe,0x380,0x1800000,0xe00000,0x38001800,0x0,0x38,0xe00,0x6000000,0x0,0x1,0xc0000070,0x300000,0x3800,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78c00,0xc30, + 0x0,0x0,0xc3000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800000,0x0,0x0,0x0,0xe0,0x1c000f,0xc0000000,0x0,0x0, + 0x0,0xc0c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7000007,0x3c003,0xc6000000,0xc180000,0x7,0x700, + 0x3c00000,0x700000,0x70003c00,0x0,0xf1801c,0x1c00,0xf000f18,0x0,0x0,0xe00000e0,0x780000,0x7000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x1c007000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe0000,0xfe000,0x0,0x3800000,0x700000,0x38, + 0x7,0xe000001c,0x1c00,0x1c00700,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf800e,0x3e0000,0x0,0x0,0x0,0x1e00000,0x0,0x1, + 0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7cc00,0x660,0x0,0x0,0x66000000,0x0,0x0,0x0,0x0,0x7,0x1c000000,0x0,0x0,0x0,0x3fe00000, + 0x0,0x0,0x7000000,0x0,0x0,0x0,0x3e0,0x7c001f,0xe0000000,0x0,0x0,0x0,0xe1c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x1f80,0x380000e,0x7e007,0xe60071c0,0xc180000,0x3,0x80000e00,0x7e0038e,0x380000,0xe0007e00,0x38e00f00,0x1f9800e, + 0x3800,0x1f801f98,0x1c70000,0x0,0x700001c0,0xfc0071,0xc000e007,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0x61c00600,0x1e00007e,0x70000,0x18003000,0x1800000,0x0,0x0,0x1c01f0,0x7e003f,0xc003f800, + 0x1e03ffc,0x7f01ff,0xfc03f000,0x7e000000,0x0,0x0,0xfc0,0x1e,0x7fe000,0x7e03fe00,0x3fff07ff,0xe007e038,0x383ffe0,0xff81c01, + 0xe1c000f8,0xf8f00e0,0xfc01ffc,0x3f00ff,0xc000fe07,0xfffc7007,0x1c007700,0x73c01ef,0x78ffff,0xfe0380,0xfe000,0x38000000,0x1800000, + 0x700000,0x38,0x1f,0xe000001c,0x1c00,0x1c00700,0x7fc0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x3f800e,0x3f8000,0x0,0xfc0000, + 0x0,0x7f00000,0x0,0x1,0x98000000,0x7f00000,0x3ffe00,0xffff0,0x0,0x0,0x0,0x0,0x0,0xcf81f,0xee3807e0,0x0,0x0,0x7e03c01e,0x1c, + 0x0,0x1f800000,0xf0078038,0xfc007,0x1c000000,0xfe00000,0x0,0x0,0x3fe000f0,0xf,0xc001f800,0x6000000,0xffc000,0x0,0x1c0007e0, + 0x360,0x6c0010,0x70000700,0xf0001e,0x3c000,0x78000f00,0x7f800ff,0xf007e01f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83fc0, + 0x7807007,0xe000fc00,0x1f8003f0,0x7e0000,0x1f867,0x70e00e,0x1c01c380,0x38f00787,0x3fe0,0x180000c,0x66006,0x7c0071c0,0xe380000, + 0x1,0x80000c00,0x660038e,0x180000,0xc0006600,0x38e0078e,0x19f0006,0x3000,0x198019f0,0x1c70000,0x0,0x30000180,0xcc0071,0xc000c007, + 0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0x61800600,0x7f8001ff,0x70000, + 0x38003800,0x1800000,0x0,0x0,0x3807fc,0x1fe00ff,0xf00ffe00,0x3e03ffc,0xff81ff,0xfc07fc01,0xff800000,0x0,0x0,0x3fe0,0xfe001e, + 0x7ff801,0xff83ff80,0x3fff07ff,0xe01ff838,0x383ffe0,0xff81c03,0xc1c000f8,0xf8f80e0,0x3ff01fff,0xffc0ff,0xf003ff87,0xfffc7007, + 0x1e00f700,0x71c03c7,0x70ffff,0xfe01c0,0xfe000,0x7c000000,0xc00000,0x700000,0x38,0x3f,0xe000001c,0x1c00,0x1c00700,0x7fc0000, + 0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x3f800e,0x3f8000,0x0,0x3fe0000,0x0,0xff00000,0x0,0x3,0xc000000,0x1ffc0000,0xfffe00, + 0xffff0,0x0,0x0,0x0,0x0,0x0,0xc781f,0xee3803c0,0x0,0x0,0x3c01c01c,0x1c,0xc000,0x7fc00000,0x70070038,0x3fe007,0x1c000000,0x1ff80000, + 0x0,0x0,0x3fe003fc,0x1f,0xe003fc00,0xc000000,0x3ffc000,0x0,0x7c000ff0,0x60,0xc0000,0x30000700,0xf0001e,0x3c000,0x78000f00, + 0x3f000ff,0xf01ff81f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ff8,0x7c0701f,0xf803ff00,0x7fe00ffc,0x1ff8000,0x7fe67, + 0x70e00e,0x1c01c380,0x38700707,0x7ff0,0xc00018,0xc3006,0x3c0071c0,0x7f00000,0x0,0xc0001800,0xc30038e,0xc0001,0x8000c300,0x38e003fc, + 0x18f0003,0x6000,0x30c018f0,0x1c70000,0x0,0x18000300,0x1860071,0xc0018007,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xe1801fc0,0x618001ff,0x70000,0x30001800,0x21840000,0x0,0x0,0x380ffe,0x1fe00ff, + 0xfc0fff00,0x3e03ffc,0x1ff81ff,0xfc0ffe03,0xffc00000,0x0,0x0,0x7ff0,0x3ff803f,0x7ffc03,0xffc3ffc0,0x3fff07ff,0xe03ffc38,0x383ffe0, + 0xff81c07,0x81c000f8,0xf8f80e0,0x7ff81fff,0x81ffe0ff,0xf80fff87,0xfffc7007,0xe00e700,0x70e0387,0x80f0ffff,0xe001c0,0xe000, + 0xfe000000,0xe00000,0x700000,0x38,0x3c,0x1c,0x1c00,0x1c00700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x78000e,0x3c000, + 0x0,0x7ff0000,0x0,0xf100000,0x0,0x7,0xe000000,0x7ffc0000,0x1fffe00,0xffff0,0x0,0x0,0x0,0x0,0x0,0x3,0xf780180,0x0,0x0,0x1801e03c, + 0x1c,0xc000,0xffc00000,0x780f0038,0x786000,0x7f00,0x18380000,0x0,0xfe00,0x30c,0x10,0x70020e00,0x1c000000,0x7f8c000,0x0,0x6c001c38, + 0x60,0xc0000,0x70000700,0x1f8003f,0x7e000,0xfc001f80,0x3f000ff,0xf03ffc1f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ffc, + 0x7c0703f,0xfc07ff80,0xfff01ffe,0x3ffc000,0xffec7,0x70e00e,0x1c01c380,0x38780f07,0xf070,0xe00038,0x1c3800,0x0,0x3e00000,0x0, + 0xe0003800,0x1c380000,0xe0003,0x8001c380,0x3e0,0x3,0x8000e000,0x70e00000,0x0,0x0,0x1c000700,0x3870000,0x38007,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xe3807ff0,0xc0c003c1,0x70000,0x70001c00, + 0x718e0000,0x0,0x0,0x700f1e,0x1ce00c0,0x3c0c0f80,0x7e03800,0x3e08000,0x381e0f03,0xc1e00000,0x0,0x0,0x7078,0x783c03f,0x701e07, + 0xc1c383e0,0x38000700,0x7c1c38,0x3801c00,0x381c0f,0x1c000fc,0x1f8f80e0,0x78781c07,0x81e1e0e0,0x780f0180,0xe007007,0xe00e380, + 0xe0f0783,0x80e0000e,0xe000e0,0xe001,0xef000000,0x0,0x700000,0x38,0x38,0x1c,0x0,0x700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000, + 0x0,0x0,0x0,0x70000e,0x1c000,0x0,0xf830000,0x0,0x1e000000,0x0,0x0,0x10000,0x780c0000,0x3e38000,0xe0,0x0,0x0,0x0,0x0,0x0,0x3, + 0xd580000,0x0,0x0,0xe038,0x1c,0xc000,0xf0400000,0x380e0038,0x702000,0x1ffc0,0xc0000,0x0,0x3ff80,0x606,0x0,0x30000600,0x0, + 0x7f8c000,0x0,0xc001818,0x60,0xc0003,0xe0000700,0x1f8003f,0x7e000,0xfc001f80,0x73801ee,0x7c1c1c,0x38000,0x70000e00,0xe0001, + 0xc0003800,0x700383e,0x7c0703c,0x3c078780,0xf0f01e1e,0x3c3c000,0xf0f87,0x70e00e,0x1c01c380,0x38380e07,0xe038,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xff0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xc380fff0,0xc0c00380,0x70000,0x70001c00,0x3dbc0070,0x0,0x0,0x701e0f,0xe0000,0x1e000380, + 0x6e03800,0x7800000,0x781c0707,0x80e00000,0x0,0x0,0x4038,0xe00c03f,0x700e07,0x4380f0,0x38000700,0x700438,0x3801c00,0x381c0e, + 0x1c000ec,0x1b8fc0e0,0xf03c1c03,0xc3c0f0e0,0x3c1e0000,0xe007007,0xe00e380,0xe070703,0xc1e0001e,0xe000e0,0xe001,0xc7000000, + 0x0,0x700000,0x38,0x38,0x1c,0x0,0x700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x70000e,0x1c000,0x0,0xe010000,0x0, + 0x1c000000,0x10,0x20000,0x6c000,0xf0000000,0x3838000,0x1e0,0x0,0xf000f,0xf1e00,0x78f00000,0x0,0x3,0xdd80000,0x0,0x0,0xf078, + 0x0,0xc001,0xe0000000,0x1c1c0038,0x700000,0x3c1e0,0xc0000,0x0,0x783c0,0x606,0x0,0x30000e00,0x0,0xff8c000,0x0,0xc00300c,0x60, + 0xc0003,0xe0000000,0x1f8003f,0x7e000,0xfc001f80,0x73801ce,0x70041c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700380f,0x7e07078, + 0x1e0f03c1,0xe0783c0f,0x781e000,0x1c0787,0x70e00e,0x1c01c380,0x383c1e07,0xff00e038,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x878, + 0x0,0x0,0x0,0x7,0x80000080,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c, + 0x1c7000,0xc301e630,0xc0c00380,0x70000,0xe0000e00,0xff00070,0x0,0x0,0xe01c07,0xe0000,0xe000380,0xce03800,0x7000000,0x701c0707, + 0x600000,0x0,0x4000010,0x38,0x1c00e07f,0x80700e0e,0x38070,0x38000700,0xe00038,0x3801c00,0x381c1c,0x1c000ec,0x1b8ec0e0,0xe01c1c01, + 0xc38070e0,0x1c1c0000,0xe007007,0x701c380,0xe078e01,0xc1c0003c,0xe00070,0xe003,0x83800000,0x7f,0x71f000,0x3e003e38,0x3f007ff, + 0xe01f1c1c,0x7801fc00,0x3fc00701,0xe01c0077,0x8f071e00,0xf801c7c,0x7c700e,0x3e01fc03,0xfff8380e,0xe007700,0x73c0787,0x387ffc, + 0x70000e,0x1c000,0x0,0xe000000,0x0,0x1c000000,0x10,0x20000,0xc2000,0xe0000000,0x3838000,0x3c0,0x0,0xf000f,0x78e00,0x70e00000, + 0x0,0x3,0xc980fe0,0x1f0,0xf8000007,0xffc07070,0x0,0x3f801,0xc0000000,0x1e3c0038,0x700000,0x70070,0x7fc0000,0x0,0xe00e0,0x606, + 0x1c0000,0x70007c00,0x380e,0xff8c000,0x0,0xc00300c,0x60,0xc0000,0x70000000,0x3fc007f,0x800ff001,0xfe003fc0,0x73801ce,0xe0001c, + 0x38000,0x70000e00,0xe0001,0xc0003800,0x7003807,0x7607070,0xe0e01c1,0xc0383807,0x700e000,0x1c0387,0x70e00e,0x1c01c380,0x381c1c07, + 0xffc0e0f8,0x3f8007f,0xfe001,0xfc003f80,0x7f007e3,0xe003e001,0xf8003f00,0x7e000fc,0xfe001f,0xc003f800,0x7f00003c,0x38f0007, + 0xc000f800,0x1f0003e0,0x7c0007,0x8003f0c3,0x80e0701c,0xe0381c0,0x70700387,0x1f01c00e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c701f,0xfff1c600,0xc0c00380,0x70000,0xe0000e00,0x3c00070,0x0,0x0,0xe03c07, + 0x800e0000,0xe000380,0x1ce03800,0x7000000,0x701c0707,0x7003c0,0x780000,0x3c00001e,0x38,0x18006073,0x80700e0e,0x38070,0x38000700, + 0xe00038,0x3801c00,0x381c38,0x1c000ee,0x3b8ee0e1,0xe01e1c01,0xc78078e0,0x1c1c0000,0xe007007,0x701c387,0xe03de00,0xe3800038, + 0xe00070,0xe007,0x1c00000,0x1ff,0xc077f801,0xff807fb8,0xff807ff,0xe03fdc1d,0xfc01fc00,0x3fc00703,0xc01c007f,0xdf877f00,0x3fe01dfe, + 0xff700e,0xff07ff03,0xfff8380e,0x700f700,0x71e0f03,0x80707ffc,0x70000e,0x1c000,0x0,0x1c000008,0x0,0x1c000000,0x10,0x20000, + 0x82000,0xe0000000,0x7038000,0x80000380,0x2000040,0x7000e,0x38700,0xf1e00000,0x0,0x3,0xc183ff8,0x3fd,0xfc008007,0xffc038e0, + 0x0,0xffc01,0xc0008008,0xe380038,0x380000,0xe3e38,0x1ffc0040,0x80000000,0x1cfc70,0x606,0x1c0000,0xe0007c00,0x380e,0xff8c000, + 0x0,0xc00300c,0x8100060,0xc0000,0x30000700,0x39c0073,0x800e7001,0xce0039c0,0x73801ce,0xe0001c,0x38000,0x70000e00,0xe0001, + 0xc0003800,0x7003807,0x77070f0,0xf1e01e3,0xc03c7807,0x8f00f080,0x83c0787,0x70e00e,0x1c01c380,0x380e3807,0xffe0e1c0,0xffe01ff, + 0xc03ff807,0xff00ffe0,0x1ffc0ff7,0xf01ff807,0xfc00ff80,0x1ff003fe,0xfe001f,0xc003f800,0x7f0003fc,0x3bf801f,0xf003fe00,0x7fc00ff8, + 0x1ff0007,0x8007fd83,0x80e0701c,0xe0381c0,0x70380707,0x7f80e01c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x1c,0x1c701f,0xfff1c600,0x618081c0,0x70000,0xe0000e00,0x3c00070,0x0,0x0,0xe03803,0x800e0000,0xe000380,0x18e03800, + 0xf000000,0xf01c0707,0x7003c0,0x780000,0xfc00001f,0x80000078,0x301e6073,0x80700e1c,0x38038,0x38000700,0x1c00038,0x3801c00, + 0x381c70,0x1c000e6,0x338ee0e1,0xc00e1c01,0xc70038e0,0x1c1c0000,0xe007007,0x701c387,0xe01dc00,0xf7800078,0xe00070,0xe00e,0xe00000, + 0x3ff,0xe07ffc03,0xffc0fff8,0x1ffc07ff,0xe07ffc1d,0xfe01fc00,0x3fc00707,0x801c007f,0xdf877f80,0x7ff01fff,0x1fff00e,0xff07ff03, + 0xfff8380e,0x700e380,0xe0e0e03,0x80707ffc,0x70000e,0x1c000,0x0,0x7ffc001c,0x0,0x1c000000,0x10,0x20000,0x82000,0xe0000000, + 0x7038001,0xc0000780,0x70000e0,0x3800e,0x38700,0xe1c00000,0x0,0x3,0xc183ff8,0x7ff,0xfc01c007,0xffc03de0,0x0,0x1ffc01,0xc001c01c, + 0xf780038,0x3c0000,0xcff18,0x380c00c1,0x80000000,0x18fe30,0x30c,0x1c0001,0xc0000e00,0x380e,0xff8c000,0x0,0xc00300c,0xc180060, + 0xc0000,0x30000700,0x39c0073,0x800e7001,0xce0039c0,0xe1c038e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x877070e0, + 0x71c00e3,0x801c7003,0x8e0071c0,0x1c380fc7,0x70e00e,0x1c01c380,0x380f7807,0x1e0e380,0x1fff03ff,0xe07ffc0f,0xff81fff0,0x3ffe0fff, + 0xf03ffc0f,0xfe01ffc0,0x3ff807ff,0xfe001f,0xc003f800,0x7f0007fe,0x3bfc03f,0xf807ff00,0xffe01ffc,0x3ff8007,0x800fff83,0x80e0701c, + 0xe0381c0,0x70380707,0xffc0e01c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c701f, + 0xfff1c600,0x7f8381e0,0x70000,0xc0000600,0xff00070,0x0,0x0,0x1c03803,0x800e0000,0xe000f00,0x38e03fe0,0xe000000,0xe00e0e07, + 0x7003c0,0x780007,0xf0ffff87,0xf00000f0,0x307fe0f3,0xc0703c1c,0x38038,0x38000700,0x1c00038,0x3801c00,0x381ce0,0x1c000e6,0x338e70e1, + 0xc00e1c01,0xc70038e0,0x3c1e0000,0xe007007,0x783c38f,0x8e01fc00,0x770000f0,0xe00038,0xe01c,0x700000,0x381,0xe07c1e07,0xc0c1e0f8, + 0x3c1e0038,0xf07c1f,0xe001c00,0x1c0070f,0x1c0079,0xf3c7c380,0xf0781f07,0x83c1f00f,0xc10f0300,0x1c00380e,0x700e380,0xe0f1e03, + 0xc0f00078,0x70000e,0x1c000,0x0,0xfff8003e,0x0,0x3c000000,0x10,0x20000,0xc6000,0xf0000000,0x7038003,0xe0000f00,0xf8001f0, + 0x3801c,0x18300,0xe1800000,0x0,0x3,0xc187818,0x70f,0x9e03e000,0x7801dc0,0x1c,0x3cc401,0xc000efb8,0x7f7f0038,0x3f0000,0x1ce11c, + 0x300c01c3,0x80000000,0x38c638,0x3fc,0x1c0003,0x80000600,0x380e,0xff8c000,0x0,0xc00300c,0xe1c0060,0xc0010,0x70000700,0x79e00f3, + 0xc01e7803,0xcf0079e0,0xe1c038e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x873870e0,0x71c00e3,0x801c7003, + 0x8e0070e0,0x38381dc7,0x70e00e,0x1c01c380,0x38077007,0xf0e700,0x1c0f0381,0xe0703c0e,0x781c0f0,0x381e083e,0x787c0c1e,0xf03c1e0, + 0x783c0f07,0x800e0001,0xc0003800,0x7000fff,0x3e1c078,0x3c0f0781,0xe0f03c1e,0x783c000,0x1e0f03,0x80e0701c,0xe0381c0,0x70380f07, + 0xc1e0e03c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1,0x8701c600,0x1e0f01e0,0x1, + 0xc0000700,0x3dbc0070,0x0,0x0,0x1c03803,0x800e0000,0x1e01fe00,0x70e03ff8,0xe3e0001,0xe007fc07,0x80f003c0,0x78001f,0xc0ffff81, + 0xfc0001e0,0x30e1e0e1,0xc07ff81c,0x38038,0x3ffe07ff,0xc1c0003f,0xff801c00,0x381de0,0x1c000e7,0x738e70e1,0xc00e1c03,0xc70038e0, + 0x780f8000,0xe007007,0x383838d,0x8e00f800,0x7f0000e0,0xe00038,0xe000,0x0,0x200,0xf0780e07,0x8041c078,0x380e0038,0xe03c1e, + 0xf001c00,0x1c0071e,0x1c0070,0xe1c783c0,0xe0381e03,0x8380f00f,0xe0000,0x1c00380e,0x381c380,0xe07bc01,0xc0e00078,0x70000e, + 0x1c000,0x0,0x1c000061,0x0,0x38000000,0x10,0x20000,0x7c000,0x7c000000,0x703fc06,0x10000e00,0x18400308,0x1801c,0x1c381,0xc3800000, + 0x0,0x0,0x7000,0xe0f,0xe061000,0x7801fc0,0x1c,0x38c001,0xc0007ff0,0x7fff0038,0x77c000,0x19c00c,0x301c0387,0x0,0x30c618,0xf0, + 0x1c0007,0x600,0x380e,0x7f8c007,0x80000000,0xc001818,0x70e03fc,0x387f871f,0xe0e00700,0x70e00e1,0xc01c3803,0x870070e0,0xe1c038f, + 0xe1c0001f,0xff03ffe0,0x7ffc0fff,0x800e0001,0xc0003800,0x7003803,0x873870e0,0x71c00e3,0x801c7003,0x8e007070,0x703839c7,0x70e00e, + 0x1c01c380,0x3807f007,0x70e700,0x10078200,0xf0401e08,0x3c10078,0x200f001c,0x3878041c,0x70380e0,0x701c0e03,0x800e0001,0xc0003800, + 0x7001e0f,0x3c1e070,0x1c0e0381,0xc070380e,0x701c000,0x1c0f03,0x80e0701c,0xe0381c0,0x701c0e07,0x80e07038,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x8600e600,0x7803f0,0x1,0xc0000700,0x718e0070,0x0,0x0,0x38038c3, + 0x800e0000,0x3c01f800,0x60e03ffc,0xeff8001,0xc001f003,0xc1f003c0,0x7800fe,0xffff80,0x3f8003c0,0x60c0e0e1,0xc07fe01c,0x38038, + 0x3ffe07ff,0xc1c07e3f,0xff801c00,0x381fe0,0x1c000e3,0x638e30e1,0xc00e1c07,0x870038ff,0xf00ff800,0xe007007,0x38381cd,0x9c007000, + 0x3e0001e0,0xe0001c,0xe000,0x0,0x0,0x70780f0f,0x3c078,0x70070038,0x1e03c1c,0x7001c00,0x1c0073c,0x1c0070,0xe1c701c1,0xe03c1e03, + 0xc780f00f,0xe0000,0x1c00380e,0x381c387,0xe03f801,0xc0e000f0,0x70000e,0x1c007,0xe0100000,0x1c0000cd,0x80000003,0xffc00000, + 0x3ff,0x807ff000,0xe0,0x7fc00060,0x703fc0c,0xd8001e00,0x3360066c,0x1c018,0xc181,0x83000000,0x0,0x0,0x7000,0x300e07,0xe0cd800, + 0xf000f80,0x1c,0x78c00f,0xff0038e0,0x3e00038,0xe1e000,0x19800c,0x383c070e,0x7fffc00,0x30fc18,0x0,0xffff80e,0x20e00,0x380e, + 0x7f8c007,0x80000000,0xc001c38,0x38703ff,0xf87fff0f,0xcfe00f00,0x70e00e1,0xc01c3803,0x870070e0,0x1e1e078f,0xe1c0001f,0xff03ffe0, + 0x7ffc0fff,0x800e0001,0xc0003800,0x700ff83,0x871870e0,0x71c00e3,0x801c7003,0x8e007038,0xe03871c7,0x70e00e,0x1c01c380,0x3803e007, + 0x70e700,0x38000,0x70000e00,0x1c00038,0x7001c,0x38f00038,0x3870070,0xe00e1c01,0xc00e0001,0xc0003800,0x7001c07,0x380e0f0,0x1e1e03c3, + 0xc078780f,0xf01e000,0x3c0f03,0x80e0701c,0xe0381c0,0x701c0e07,0x80f07038,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x8600ff00,0x1e00778,0x38000001,0xc0000700,0x21843fff,0xe0000000,0x0,0x38039e3,0x800e0000, + 0x7c01fe00,0xe0e0203e,0xeffc001,0xc00ffe03,0xff700000,0x7f0,0x0,0x7f00380,0x618060e1,0xc07ffc1c,0x38038,0x3ffe07ff,0xc1c07e3f, + 0xff801c00,0x381ff0,0x1c000e3,0x638e38e1,0xc00e1fff,0x870038ff,0xc003fe00,0xe007007,0x38381cd,0x9c00f800,0x3e0003c0,0xe0001c, + 0xe000,0x0,0x0,0x7070070e,0x38038,0x70070038,0x1c01c1c,0x7001c00,0x1c00778,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0xfc000, + 0x1c00380e,0x381c3c7,0x1e01f001,0xe1e001e0,0xf0000e,0x1e01f,0xf8300000,0x1c00019c,0xc0000003,0xffc00000,0x10,0x20000,0x700, + 0x1ff000c0,0x703fc19,0xcc003c00,0x67300ce6,0xc038,0xc181,0x83000000,0x0,0x0,0x7e00,0x180e07,0xe19cc00,0x1e000f80,0x1c,0x70c00f, + 0xff007070,0x3e00038,0xe0f000,0x19800c,0x1fec0e1c,0x7fffc00,0x30f818,0x0,0xffff81f,0xf003fc00,0x380e,0x3f8c007,0x80000000, + 0x7f800ff0,0x1c3803f,0xe007fc00,0xff800e00,0x70e00e1,0xc01c3803,0x870070e0,0x1c0e070f,0xe1c0001f,0xff03ffe0,0x7ffc0fff,0x800e0001, + 0xc0003800,0x700ff83,0x871c70e0,0x71c00e3,0x801c7003,0x8e00701d,0xc038e1c7,0x70e00e,0x1c01c380,0x3803e007,0x70e3c0,0x38000, + 0x70000e00,0x1c00038,0x7001c,0x38e00038,0x3870070,0xe00e1c01,0xc00e0001,0xc0003800,0x7003c07,0x8380e0e0,0xe1c01c3,0x80387007, + 0xe00e1ff,0xfe381b83,0x80e0701c,0xe0381c0,0x701e1e07,0x707878,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x1c,0x3,0xe007fe0,0x7800e3c,0x38000001,0xc0000700,0x1803fff,0xe0000000,0x0,0x70039c3,0x800e0000,0xf8000f80, + 0xc0e0000e,0xf83c003,0xc01e0f01,0xff700000,0x7c0,0x0,0x1f00780,0x618061c0,0xe0701e1c,0x38038,0x38000700,0x1c07e38,0x3801c00, + 0x381e78,0x1c000e3,0xe38e18e1,0xc00e1fff,0x70038ff,0xe0007f80,0xe007007,0x1c701dd,0x9c00f800,0x1c000780,0xe0000e,0xe000,0x0, + 0x7f,0xf070070e,0x38038,0x7fff0038,0x1c01c1c,0x7001c00,0x1c007f8,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x7fc00,0x1c00380e, + 0x1c381c7,0x1c01f000,0xe1c001c0,0xfe0000e,0xfe1f,0xfff00000,0x7ff003fc,0xe0000003,0xffc00000,0x10,0x20000,0x3800,0x3fc0180, + 0x703803f,0xce007800,0xff381fe7,0x30,0x0,0xc0,0x0,0x0,0x3fe0,0xc0e07,0xfe3fce00,0x1c000700,0x1c,0x70c00f,0xff006030,0x1c00000, + 0xe07800,0x19800c,0xfcc1c38,0x7fffc00,0x30d818,0x0,0xffff81f,0xf001f800,0x380e,0xf8c007,0x80000000,0x7f8007e0,0xe1c3fe,0x7fc00f, + 0xf8001e00,0xe0701c0,0xe0381c07,0x380e070,0x1c0e070e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700ff83,0x870c70e0, + 0x71c00e3,0x801c7003,0x8e00700f,0x8038c1c7,0x70e00e,0x1c01c380,0x3801c007,0xf0e3e0,0x3ff807f,0xf00ffe01,0xffc03ff8,0x7ff03ff, + 0xf8e0003f,0xff87fff0,0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e1ff,0xfe383383,0x80e0701c, + 0xe0381c0,0x700e1c07,0x703870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0xc000ff0, + 0x3c1e1c1c,0x38000001,0xc0000700,0x1803fff,0xe0000007,0xf8000000,0x7003803,0x800e0001,0xf0000381,0xc0e00007,0xf01e003,0x801c0700, + 0x7c700000,0x7c0,0x0,0x1f00700,0x618061c0,0xe0700e1c,0x38038,0x38000700,0x1c00e38,0x3801c00,0x381e38,0x1c000e1,0xc38e1ce1, + 0xc00e1ffc,0x70038e0,0xf0000780,0xe007007,0x1c701dd,0xdc01fc00,0x1c000780,0xe0000e,0xe000,0x0,0x1ff,0xf070070e,0x38038,0x7fff0038, + 0x1c01c1c,0x7001c00,0x1c007f8,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x3ff00,0x1c00380e,0x1c381cd,0x9c00e000,0xe1c003c0, + 0xf80000e,0x3e18,0x3ff00000,0xffe007fd,0xf0000000,0x38000000,0x10,0x20000,0x1c000,0x3c0300,0x703807f,0xdf007801,0xff7c3fef, + 0x80000000,0x0,0x3e0,0x7ffe7ff,0xff000000,0x1ff8,0x60e07,0xfe7fdf00,0x3c000700,0x1c,0x70c001,0xc0006030,0x7fff0000,0xf03800, + 0x19800c,0x1c38,0x1c07,0xf830cc18,0x0,0x1c0000,0x0,0x380e,0x18c007,0x80000000,0x0,0xe1cfe0,0x1fc003f,0x80003c00,0xe0701c0, + 0xe0381c07,0x380e070,0x1c0e070e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x870e70e0,0x71c00e3,0x801c7003, + 0x8e007007,0x3981c7,0x70e00e,0x1c01c380,0x3801c007,0x1e0e0f8,0xfff81ff,0xf03ffe07,0xffc0fff8,0x1fff07ff,0xf8e0003f,0xff87fff0, + 0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e1ff,0xfe386383,0x80e0701c,0xe0381c0,0x700e1c07, + 0x703870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x7f,0xffc00678,0x707f9c1e,0x38000001, + 0xc0000700,0x70,0x7,0xf8000000,0xe003803,0x800e0003,0xe00001c3,0x80e00007,0xe00e007,0x80380380,0x700000,0x7f0,0x0,0x7f00700, + 0x618061ff,0xe070071c,0x38038,0x38000700,0x1c00e38,0x3801c00,0x381c3c,0x1c000e1,0xc38e1ce1,0xc00e1c00,0x70038e0,0x700003c0, + 0xe007007,0x1c701d8,0xdc03dc00,0x1c000f00,0xe00007,0xe000,0x0,0x3ff,0xf070070e,0x38038,0x7fff0038,0x1c01c1c,0x7001c00,0x1c007fc, + 0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x3f00,0x1c00380e,0x1c381cd,0x9c01f000,0x73800780,0xfe0000e,0xfe10,0x7c00000,0x1c000ffb, + 0xf8000000,0x38000000,0x10,0x20000,0x20000,0x1e0700,0x70380ff,0xbf80f003,0xfefe7fdf,0xc0000000,0x0,0x3f0,0x7ffe7ff,0xff000000, + 0x1f8,0x30e07,0xfeffbf80,0x78000700,0x1c,0x70c001,0xc0006030,0x7fff0000,0x783800,0x1ce11c,0xe1c,0x1c07,0xf838ce38,0x0,0x1c0000, + 0x0,0x380e,0x18c000,0x0,0x0,0x1c38c00,0x1800030,0x7800,0xfff01ff,0xe03ffc07,0xff80fff0,0x3fff0ffe,0x1c0001c,0x38000,0x70000e00, + 0xe0001,0xc0003800,0x7003803,0x870e70e0,0x71c00e3,0x801c7003,0x8e00700f,0x803b81c7,0x70e00e,0x1c01c380,0x3801c007,0xffe0e03c, + 0x1fff83ff,0xf07ffe0f,0xffc1fff8,0x3fff0fff,0xf8e0003f,0xff87fff0,0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3, + 0x80387007,0xe00e000,0x38c383,0x80e0701c,0xe0381c0,0x70073807,0x701ce0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xffc0063c,0x40619c0f,0x30000001,0xc0000700,0x70,0x7,0xf8000000,0xe003803,0x800e0007,0xc00001c3, + 0xfffc0007,0xe00e007,0x380380,0xf00000,0xfe,0xffff80,0x3f800700,0x618063ff,0xf070071c,0x38038,0x38000700,0x1c00e38,0x3801c00, + 0x381c1e,0x1c000e0,0x38e0ee1,0xc00e1c00,0x70038e0,0x380001c0,0xe007007,0x1ef01d8,0xdc038e00,0x1c001e00,0xe00007,0xe000,0x0, + 0x7c0,0x7070070e,0x38038,0x70000038,0x1c01c1c,0x7001c00,0x1c0079e,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x780,0x1c00380e, + 0xe701cd,0x9c01f000,0x73800f00,0xe0000e,0xe000,0x0,0x1c0007f7,0xf0000000,0x70000000,0x10,0x20000,0x0,0xe0e00,0x703807f,0x7f01e001, + 0xfdfc3fbf,0x80000000,0x0,0x7f0,0x0,0x0,0x3c,0x18e07,0x7f7f00,0xf0000700,0x1c,0x70c001,0xc0007070,0x1c00000,0x3e7000,0xcff18, + 0x3ffc070e,0x1c07,0xf818c630,0x0,0x1c0000,0x0,0x380e,0x18c000,0x0,0x3ffc,0x3870000,0xe000fc00,0x380f000,0x1fff83ff,0xf07ffe0f, + 0xffc1fff8,0x3fff0ffe,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x870770e0,0x71c00e3,0x801c7003,0x8e00701d, + 0xc03f01c7,0x70e00e,0x1c01c380,0x3801c007,0xffc0e01c,0x3e0387c0,0x70f80e1f,0x1c3e038,0x7c071e1c,0xe00038,0x70000,0xe0001c00, + 0xe0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e000,0x398383,0x80e0701c,0xe0381c0,0x70073807,0x701ce0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xffc0061c,0xc0dc07,0xf0000001,0xc0000700, + 0x70,0x0,0x0,0x1c003c07,0x800e000f,0x1c3,0xfffc0007,0xe00e007,0x380380,0xe00000,0x1f,0xc0ffff81,0xfc000700,0x618063ff,0xf070070e, + 0x38070,0x38000700,0xe00e38,0x3801c00,0x381c0e,0x1c000e0,0x38e0ee1,0xe01e1c00,0x78078e0,0x380001c0,0xe007007,0xee01f8,0xfc078f00, + 0x1c001c00,0xe00003,0x8000e000,0x0,0x700,0x7070070e,0x38038,0x70000038,0x1c01c1c,0x7001c00,0x1c0070e,0x1c0070,0xe1c701c1, + 0xc01c1c01,0xc700700e,0x380,0x1c00380e,0xe700ed,0xb803f800,0x77800f00,0x70000e,0x1c000,0x0,0xe0003f7,0xe0000000,0x70000000, + 0x10,0x20000,0x1c0e0,0xe1c00,0x703803f,0x7e01c000,0xfdf81fbf,0x0,0x0,0x3f0,0x0,0x0,0x1c,0x1ce07,0x3f7e00,0xf0000700,0x1c, + 0x70c001,0xc00038e0,0x1c00038,0xf7000,0xe3e38,0x3ffc0387,0x1c00,0x1cc770,0x0,0x1c0000,0x0,0x380e,0x18c000,0x0,0x3ffc,0x70e0001, + 0xe001fe00,0x780e000,0x1fff83ff,0xf07ffe0f,0xffc1fff8,0x3fff0ffe,0xe0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003807, + 0x70770f0,0xf1e01e3,0xc03c7807,0x8f00f038,0xe03e03c7,0x70e00e,0x1c01c380,0x3801c007,0xff00e00e,0x38038700,0x70e00e1c,0x1c38038, + 0x70071c1c,0xe00038,0x70000,0xe0001c00,0xe0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e000,0x3b0383,0x80e0701c, + 0xe0381c0,0x70077807,0x701de0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x1c00061c, + 0xc0de03,0xe0000001,0xc0000700,0x70,0x0,0x0,0x1c001c07,0xe001e,0x1c3,0xfffc0007,0x600e00e,0x380380,0xe00000,0x7,0xf0ffff87, + 0xf0000000,0x60c0e380,0x7070070e,0x38070,0x38000700,0xe00e38,0x3801c00,0x381c0f,0x1c000e0,0x38e06e0,0xe01c1c00,0x38070e0, + 0x1c0001c0,0xe007007,0xee00f8,0xf80f0700,0x1c003c00,0xe00003,0x8000e000,0x0,0x700,0x70780f0f,0x3c078,0x70000038,0x1e03c1c, + 0x7001c00,0x1c0070f,0x1c0070,0xe1c701c1,0xe03c1e03,0xc780f00e,0x380,0x1c00380e,0xe700f8,0xf807bc00,0x3f001e00,0x70000e,0x1c000, + 0x0,0xe0001ff,0xc0000000,0x70000000,0x10,0x20000,0x33110,0xe0e00,0x383801f,0xfc03c000,0x7ff00ffe,0x0,0x0,0x3e0,0x0,0x0,0x1c, + 0x38e07,0x1ffc01,0xe0000700,0x1c,0x78c001,0xc0007ff0,0x1c00038,0x7c000,0x70070,0x1c3,0x80001c00,0xe00e0,0x0,0x1c0000,0x0, + 0x380e,0x18c000,0x0,0x0,0xe1c0001,0xe0010700,0x780e000,0x1c038380,0x70700e0e,0x1c1c038,0x78070e0e,0xe0001c,0x38000,0x70000e00, + 0xe0001,0xc0003800,0x7003807,0x7037070,0xe0e01c1,0xc0383807,0x700e070,0x701c0387,0x70e00e,0x1c01c380,0x3801c007,0xe00e,0x38038700, + 0x70e00e1c,0x1c38038,0x70071c1c,0xf00038,0x70000,0xe0001c00,0xe0001,0xc0003800,0x7003c07,0x8380e0f0,0x1e1e03c3,0xc078780f, + 0xf01e007,0x803e0783,0x80e0701c,0xe0381c0,0x7003f007,0x80f00fc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x6,0x1800061c,0xc0de01,0xc0000000,0xc0000e00,0x70,0xf0000,0x3c00,0x38001c0f,0xe003c,0x3c0,0xe0000e,0x701e00e, + 0x3c0780,0x1e003c0,0x780000,0xfc00001f,0x80000000,0x60e1e780,0x78700f07,0x4380f0,0x38000700,0xf00e38,0x3801c00,0xc0781c07, + 0x81c000e0,0x38e07e0,0xe03c1c00,0x380f0e0,0x1e0003c0,0xe00780f,0xee00f0,0x780e0780,0x1c007800,0xe00001,0xc000e000,0x0,0x700, + 0xf0780e07,0x8041c078,0x38020038,0xe03c1c,0x7001c00,0x1c00707,0x801c0070,0xe1c701c0,0xe0381e03,0x8380f00e,0x80380,0x1c003c1e, + 0x7e00f8,0xf80f1e00,0x3f003c00,0x70000e,0x1c000,0x0,0xf0100f7,0x80078000,0x700078f0,0x10,0x7ff000,0x61208,0x1e0700,0x383800f, + 0x78078000,0x3de007bc,0x0,0x0,0x0,0x0,0x0,0x401c,0x70e0f,0xf7803,0xc0000700,0x1c,0x38c001,0xc000efb8,0x1c00038,0x1e000,0x3c1e0, + 0xc1,0x80000000,0x783c0,0x0,0x0,0x0,0x3c1e,0x18c000,0x0,0x0,0xc180003,0x60000300,0xd80e010,0x3c03c780,0x78f00f1e,0x1e3c03c, + 0x70039c0e,0x70041c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700380f,0x703f070,0x1e0e03c1,0xc078380f,0x701e0e0,0x381c0787, + 0x80f0f01e,0x1e03c3c0,0x7801c007,0xe00e,0x38078700,0xf0e01e1c,0x3c38078,0x700f1c1c,0x78041c,0x1038020,0x70040e00,0x800e0001, + 0xc0003800,0x7001c07,0x380e070,0x1c0e0381,0xc070380e,0x701c007,0x801e0703,0xc1e0783c,0xf0781e0,0xf003f007,0x80e00fc0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xe,0x1801867c,0xc0cf83,0xe0000000,0xe0000e00, + 0x70,0xf0000,0x3c00,0x38000f1e,0xe0070,0x180780,0xe0603e,0x783c01e,0x1e0f01,0x7c003c0,0x780000,0x3c00001e,0x700,0x307fe700, + 0x38701e07,0xc1c383e0,0x38000700,0x7c1e38,0x3801c00,0xe0f01c03,0x81c000e0,0x38e03e0,0x78781c00,0x1e1e0e0,0xe180780,0xe003c1e, + 0x7c00f0,0x781e03c0,0x1c007000,0xe00001,0xc000e000,0x0,0x783,0xf07c1e07,0xc0c1e0f8,0x3e0e0038,0xf07c1c,0x7001c00,0x1c00703, + 0xc01e0070,0xe1c701c0,0xf0781f07,0x83c1f00e,0xe0f80,0x1e003c3e,0x7e00f8,0xf80e0e00,0x3f003800,0x70000e,0x1c000,0x0,0x7830077, + 0xf0000,0x700078f0,0x10,0x20000,0x41208,0xc03c0380,0x3c38007,0x70070000,0x1dc003b8,0x0,0x0,0x0,0x0,0x0,0x707c,0x6070f,0x86077003, + 0x80000700,0x1c,0x3ec401,0xc001c01c,0x1c00038,0xf000,0x1ffc0,0x40,0x80000000,0x3ff80,0x0,0x0,0x0,0x3e3e,0x18c000,0x0,0x0, + 0x8100006,0x60000300,0x1980f070,0x3801c700,0x38e0071c,0xe3801c,0x70039c0e,0x7c1c1c,0x38000,0x70000e00,0xe0001,0xc0003800, + 0x700383e,0x701f03c,0x3c078780,0xf0f01e1e,0x3c3c1c0,0x1c3f0f03,0xc1e0783c,0xf0781e0,0xf001c007,0xe81e,0x3c1f8783,0xf0f07e1e, + 0xfc3c1f8,0x783f1e3e,0x187c0c1f,0x703e0e0,0x7c1c0f83,0x800e0001,0xc0003800,0x7001e0f,0x380e078,0x3c0f0781,0xe0f03c1e,0x783c007, + 0x801e0f03,0xc3e0787c,0xf0f81e1,0xf003f007,0xc1e00fc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x1c,0xe,0x3801fff8,0x6187ff,0xe0000000,0xe0000e00,0x70,0xf0000,0x3c00,0x38000ffe,0x1fff0ff,0xfe1fff80,0xe07ffc,0x3ffc01c, + 0x1fff01,0xff8003c0,0x780000,0x4000010,0x700,0x301e6700,0x387ffe03,0xffc3ffc0,0x3fff0700,0x3ffe38,0x383ffe0,0xfff01c03,0xc1fff8e0, + 0x38e03e0,0x7ff81c00,0x1ffe0e0,0xf1fff80,0xe003ffe,0x7c00f0,0x781c01c0,0x1c00ffff,0xe00001,0xc000e000,0x0,0x3ff,0x707ffc03, + 0xffc0fff8,0x1ffe0038,0x7ffc1c,0x707fff0,0x1c00701,0xc00ff070,0xe1c701c0,0x7ff01fff,0x1fff00e,0xfff00,0xff81fee,0x7e00f0, + 0x781e0f00,0x1e007ffc,0x70000e,0x1c000,0x0,0x3ff003e,0xf0000,0xe00070e0,0x60830010,0x20000,0x41208,0xfffc01c0,0x1fffe03,0xe00ffff0, + 0xf8001f0,0x0,0x0,0x0,0x0,0x0,0x7ff8,0xc07fd,0xfe03e007,0xffc00700,0x1c,0x1ffc1f,0xffc08008,0x1c00038,0x7000,0x7f00,0x0,0x0, + 0xfe00,0x0,0xffff800,0x0,0x3ff7,0x8018c000,0x0,0x0,0x6,0x60000700,0x19807ff0,0x3801c700,0x38e0071c,0xe3801c,0x70039c0f,0xf03ffc1f, + 0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ffc,0x701f03f,0xfc07ff80,0xfff01ffe,0x3ffc080,0x83fff03,0xffe07ffc,0xfff81ff, + 0xf001c007,0xeffc,0x1ffb83ff,0x707fee0f,0xfdc1ffb8,0x3ff70ff7,0xf83ffc0f,0xff01ffe0,0x3ffc07ff,0x83fff87f,0xff0fffe1,0xfffc0ffe, + 0x380e03f,0xf807ff00,0xffe01ffc,0x3ff8007,0x803ffe01,0xfee03fdc,0x7fb80ff,0x7001e007,0xffc00780,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xc,0x3801fff0,0x7f83fe,0x70000000,0xe0000e00,0x0,0xf0000,0x3c00,0x700007fc, + 0x1fff0ff,0xfe1ffe00,0xe07ff8,0x1ff801c,0xffe01,0xff0003c0,0x780000,0x0,0x700,0x38000f00,0x3c7ffc01,0xff83ff80,0x3fff0700, + 0x1ffc38,0x383ffe0,0x7fe01c01,0xe1fff8e0,0x38e03e0,0x3ff01c00,0xffc0e0,0x71fff00,0xe001ffc,0x7c00f0,0x783c01e0,0x1c00ffff, + 0xe00000,0xe000e000,0x0,0x1ff,0x7077f801,0xff807fb8,0xffc0038,0x3fdc1c,0x707fff0,0x1c00701,0xe007f070,0xe1c701c0,0x3fe01dfe, + 0xff700e,0x7fe00,0xff80fee,0x3c0070,0x703c0780,0x1e007ffc,0x70000e,0x1c000,0x0,0x1fe001c,0xe0000,0xe000e1c0,0x71c78010,0x20000, + 0x21318,0xfff800c0,0xfffe01,0xc00ffff0,0x70000e0,0x0,0x0,0x0,0x0,0x0,0x3ff0,0x1803fd,0xfe01c007,0xffc00700,0x1c,0xffc1f,0xffc00000, + 0x1c00038,0x7000,0x0,0x0,0x0,0x0,0x0,0xffff800,0x0,0x3ff7,0x8018c000,0x0,0x0,0xc,0x60000e00,0x31803fe0,0x7801ef00,0x3de007bc, + 0xf7801e,0xf003fc0f,0xf01ff81f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ff8,0x701f01f,0xf803ff00,0x7fe00ffc,0x1ff8000, + 0x67fe01,0xffc03ff8,0x7ff00ff,0xe001c007,0xeff8,0xffb81ff,0x703fee07,0xfdc0ffb8,0x1ff70ff7,0xf81ff807,0xfe00ffc0,0x1ff803ff, + 0x3fff87f,0xff0fffe1,0xfffc07fc,0x380e01f,0xf003fe00,0x7fc00ff8,0x1ff0000,0x37fc00,0xfee01fdc,0x3fb807f,0x7001e007,0x7f800780, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xc,0x30007fc0,0x1e00f8,0x78000000,0x70001c00, + 0x0,0xe0000,0x3c00,0x700001f0,0x1fff0ff,0xfe07f800,0xe01fe0,0x7e0038,0x3f800,0xfc0003c0,0x700000,0x0,0x700,0x18000e00,0x1c7ff000, + 0x7e03fe00,0x3fff0700,0x7f038,0x383ffe0,0x1f801c00,0xf1fff8e0,0x38e01e0,0xfc01c00,0x3f80e0,0x787fc00,0xe0007f0,0x7c00f0,0x387800f0, + 0x1c00ffff,0xe00000,0xe000e000,0x0,0xfc,0x7071f000,0x3f003e38,0x3f00038,0x1f1c1c,0x707fff0,0x1c00700,0xf003f070,0xe1c701c0, + 0x1f801c7c,0x7c700e,0x1f800,0x3f8078e,0x3c0070,0x707803c0,0x1c007ffc,0x70000e,0x1c000,0x0,0x7c0008,0x1e0000,0xe000e1c0,0x71c30010, + 0x20000,0x1e1f0,0x3fe00020,0x3ffe00,0x800ffff0,0x2000040,0x0,0x0,0x0,0x0,0x0,0xfc0,0x3001f0,0x78008007,0xffc00700,0x1c,0x3f81f, + 0xffc00000,0x1c00038,0x407000,0x0,0x0,0x0,0x0,0x0,0xffff800,0x0,0x39c7,0x18c000,0x0,0x0,0x18,0x60001c00,0x61801f80,0x7000ee00, + 0x1dc003b8,0x77000e,0xe001f80f,0xf007e01f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83fc0,0x700f007,0xe000fc00,0x1f8003f0, + 0x7e0000,0xe1f800,0x7f000fe0,0x1fc003f,0x8001c007,0xe7f0,0x7e380fc,0x701f8e03,0xf1c07e38,0xfc703c1,0xe003f001,0xf8003f00, + 0x7e000fc,0x3fff87f,0xff0fffe1,0xfffc03f8,0x380e00f,0xc001f800,0x3f0007e0,0xfc0000,0x61f800,0x78e00f1c,0x1e3803c,0x7001c007, + 0x1f000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x70001c00,0x0, + 0x1c0000,0x0,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0, + 0x0,0x0,0x0,0x0,0xe00000,0x7000e000,0x0,0x0,0x0,0x0,0x0,0x1c00,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x1c000000, + 0x70000e,0x1c000,0x0,0x0,0x1c0000,0xe000c180,0x10,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, + 0x0,0x38,0x70e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x2000,0x0,0x1f,0xf8003800,0x7fe00000,0x0,0x0,0x0,0x0,0x4000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400000, + 0x0,0x0,0x1c007,0x700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x30001800, + 0x0,0x1c0000,0x0,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e000, + 0x0,0x0,0x0,0x0,0x0,0xe00000,0x7000e000,0x0,0x0,0x0,0x0,0x0,0x1c00,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x1c000000, + 0x70000e,0x1c000,0x0,0x0,0x1c0001,0xe001c380,0x10,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, + 0x0,0x38,0x7fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x3000,0x0,0x1f,0xf8007000,0x7fe00000,0x0,0x0,0x0,0x0,0x6000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x1c007,0x700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x38003800, + 0x0,0x380000,0x1,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x0,0x0,0x3c18000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf000, + 0x0,0x0,0x0,0x0,0x0,0xfe0000,0x380fe000,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x38000000, + 0x78000e,0x3c000,0x0,0x0,0x180001,0xc0018300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0, + 0x38,0x1f8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x1800,0x0,0x0,0x6000e000,0x1800000,0x0,0x0,0x0,0x0,0x3000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x38007,0xe00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x18003000, + 0x0,0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0, + 0x0,0x0,0x0,0xfe0000,0xfe000,0x0,0x0,0x0,0x0,0x0,0x607800,0x0,0x3c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x78000000, + 0x3f800e,0x3f8000,0x0,0x0,0x300043,0xc0018200,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, + 0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x11800,0x0,0x0,0x6001ff00,0x1800000,0x0,0x0,0x0,0x0,0x23000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78007, + 0x1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x1c007000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe0000, + 0xfe000,0x0,0x0,0x0,0x0,0x0,0x7ff000,0x0,0x7f800000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xf8000000,0x3f800e,0x3f8000,0x0, + 0x0,0x10007f,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x38,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x3800,0x0,0x1f800,0x0,0x0,0x6001ff00,0x1800000,0x0,0x0,0x0,0x0,0x3f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f8007,0xfe00,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x7fe000,0x0, + 0x7f000000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xf0000000,0xf800e,0x3e0000,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x1f000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x3f0007,0xfc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x1fc000,0x0,0x7e000000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xc0000000,0xe,0x0, + 0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x3800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0007,0xf000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 29x57 font + const unsigned int font29x57[29*57*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x781e00,0x0,0x0,0x7,0x81e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0000,0xf8000,0x7e00000,0x0,0x7, + 0xc0000000,0x0,0x7c00,0xf80,0x7e000,0x0,0x7c00000,0xf80000,0x7e000000,0x0,0x0,0x1f00,0x3e0,0x1f800,0x0,0x0,0x0,0x3,0xe0000000, + 0x7c00003f,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x3c3c00,0x0,0x0,0x3,0xc3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0000, + 0x1f0000,0x7e00000,0xf838001f,0xf80001f,0xf0000000,0x0,0x3e00,0x1f00,0x7e000,0x3e1f000,0x3e00000,0x1f00000,0x7e00003e,0x1f000000, + 0x3e0,0xe0000f80,0x7c0,0x1f800,0x3e0e00,0x7c3e000,0x0,0x1,0xf0000000,0xf800003f,0x1f0f,0x800001f0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e7800,0x0,0x0, + 0x1,0xe7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x1e0000,0xff00001,0xfe38001f,0xf80003f, + 0xf8000000,0x0,0x1e00,0x1e00,0xff000,0x3e1f000,0x1e00000,0x1e00000,0xff00003e,0x1f000000,0x7f8,0xe0000780,0x780,0x3fc00,0x7f8e00, + 0x7c3e000,0x0,0x0,0xf0000000,0xf000007f,0x80001f0f,0x800001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef000,0x0,0x0,0x0,0xef000000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000,0x3c0000,0x1e780003,0xfff8001f,0xf80003c,0x78000000,0x0,0xf00,0x3c00,0x1e7800, + 0x3e1f000,0xf00000,0x3c00001,0xe780003e,0x1f000000,0xfff,0xe00003c0,0xf00,0x79e00,0xfffe00,0x7c3e000,0x0,0x0,0x78000001,0xe00000f3, + 0xc0001f0f,0x800003c0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x78000,0x780000,0x3c3c0003,0x8ff0001f,0xf800078,0x3c000000,0x0,0x780,0x7800,0x3c3c00,0x3e1f000,0x780000,0x7800003,0xc3c0003e, + 0x1f000000,0xe3f,0xc00001e0,0x1e00,0xf0f00,0xe3fc00,0x7c3e000,0x0,0x0,0x3c000003,0xc00001e1,0xe0001f0f,0x80000780,0x0,0x0, + 0x0,0x0,0x0,0x0,0x1f,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc00,0x7e000,0xfe000,0x0,0x3c000,0xf00000,0x781e0003, + 0x83e0001f,0xf800070,0x1c000000,0x0,0x3c0,0xf000,0x781e00,0x3e1f000,0x3c0000,0xf000007,0x81e0003e,0x1f000000,0xe0f,0x800000f0, + 0x3c00,0x1e0780,0xe0f800,0x7c3e000,0x0,0x0,0x1e000007,0x800003c0,0xf0001f0f,0x80000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf8000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ff800,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x78,0xf000000,0x0,0x0,0x780f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ffc00,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x3e000,0x3e00000,0x0,0x78,0x3c000000,0x0,0x1f000,0x3e0, + 0x3e000,0x0,0x1f000000,0x3e0000,0x3e000000,0x0,0x0,0x7c00,0xf8,0xf800,0x0,0x0,0x0,0xf,0x80000000,0x1f00001f,0x0,0x3e,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x781c0000,0x38,0xe000000,0x0,0x0,0x380e0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x39c00,0x1ce000,0x303e00, + 0x0,0x0,0x0,0x0,0x0,0x78,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0, + 0x0,0x0,0xf80000,0x7c000,0x3e00000,0xf0380000,0x70,0x1c000000,0x0,0xf800,0x7c0,0x3e000,0x0,0xf800000,0x7c0000,0x3e000000, + 0x0,0x3c0,0xe0003e00,0x1f0,0xf800,0x3c0e00,0x0,0x0,0x7,0xc0000000,0x3e00001f,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0xff,0x0, + 0xf8,0xf8000,0x1c000,0x0,0x0,0x0,0x0,0x1f,0xc0000000,0x1ff8,0xff00,0x0,0x0,0x3fe000,0x0,0x1fc00001,0xfe000000,0x0,0x0,0x0, + 0x0,0x7f800,0x0,0x0,0x0,0xff00000,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf8000000,0xfe,0x0,0x7f80,0x0,0x0,0x0,0x0,0x0, + 0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x780000,0x1,0xe0000000,0x0,0x780000,0x3,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x3fc00,0x0,0x0,0x1fc000,0x0,0x0,0x0,0x1fc0, + 0x0,0xff000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe1c0000,0x1c,0x1c000000,0x0,0x0,0x1c1c0,0x0,0x0,0x0,0x0,0x1fe0000, + 0x0,0x0,0x1ff,0x1f0f8,0x0,0xff000,0x0,0x0,0x0,0x3f,0xff00000f,0x80000000,0xfe0,0x3f80,0xf00,0x0,0x0,0x0,0x1,0xf8000003,0xe0000000, + 0x1c00,0xe000,0xe00,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000, + 0x7f0000,0x0,0x1fc07000,0x0,0x0,0x0,0x0,0x0,0x3f800,0x780000,0x78000,0x7f00001,0xfc38001f,0xf800070,0x1c000000,0x0,0x7800, + 0x780,0x7f000,0x3e1f000,0x7800000,0x780000,0x7f00003e,0x1f0003f0,0x7f0,0xe0001e00,0x1e0,0x1fc00,0x7f0e00,0x7c3e000,0x0,0x3, + 0xc0000000,0x3c00003f,0x80001f0f,0x80000078,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x1e078000,0x30000000,0x3ff,0xc00001e0,0xf0, + 0x78000,0x1c000,0x0,0x0,0x0,0x0,0x1e0007f,0xf000007e,0x1ffff,0x7ffe0,0x1f80,0x3ffff80,0xfff803,0xfffff800,0xfff80007,0xff800000, + 0x0,0x0,0x0,0x0,0x1ffe00,0x0,0xfe0003,0xfff80000,0x3ffe01ff,0xe00003ff,0xffe01fff,0xff0003ff,0xe01e0007,0x803ffff0,0xfff80, + 0x3c000fc0,0x7800001f,0x8003f07e,0x1e000f,0xfe0007ff,0xf00003ff,0x8007ffe0,0x1fff8,0x7fffffe,0xf0003c1,0xe000079e,0xf1f,0x1f3e0, + 0x1f01ff,0xfff8003f,0xf003c000,0x7fe0,0x3f00,0x0,0x3c0000,0x1,0xe0000000,0x0,0x780000,0xf,0xfe000000,0x78000,0x3c00,0xf000, + 0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xfc0000f0,0x3fe00,0x0,0x0,0xfff00,0x0,0x0,0x3fe000, + 0x0,0x0,0x0,0x1dc0,0x0,0x3fff00,0x0,0x3ffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff1c07ff,0x3c0f001e,0x3c000000, + 0x0,0x0,0x1e3c0,0xf80007c,0x0,0x780000,0x0,0xfff8000,0x3e00,0x1f00000,0x7ff,0xc001f0f8,0x0,0x3ffc00,0x0,0x0,0x0,0x3f,0xff00003f, + 0xe0000000,0x3ff8,0xffe0,0x1e00,0x0,0xfffc00,0x0,0x7,0xf800000f,0xf8000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000, + 0x3f800001,0xfc00003f,0xf80000ff,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc, + 0xfc00,0x3c001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x0,0x7ff8f0f0,0x3c0780,0x1e03c00,0xf01e000,0x783e0001,0xf01e0000,0xffe00, + 0x3c0000,0xf0000,0x7700001,0xfe38001f,0xf800070,0x1c000000,0x0,0x3c00,0xf00,0x77000,0x3e1f000,0x3c00000,0xf00000,0x7700003e, + 0x1f0000f8,0xc0007f8,0xe0000f00,0x3c0,0x1dc00,0x7f8e00,0x7c3e000,0x0,0x1,0xe0000000,0x7800003b,0x80001f0f,0x800000f0,0x1e0000, + 0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x780000,0x3c1e0000,0x1e070000,0x300001f0,0x7ff,0xc00001e0,0x1e0,0x7c000,0x1c000,0x0,0x0,0x0,0x0,0x3c000ff,0xf80007fe, + 0x3ffff,0x801ffff8,0x1f80,0x3ffff80,0x3fff803,0xfffff801,0xfffc000f,0xffc00000,0x0,0x0,0x0,0x0,0x7fff80,0x0,0xfe0003,0xffff0000, + 0xffff01ff,0xfc0003ff,0xffe01fff,0xff000fff,0xf01e0007,0x803ffff0,0xfff80,0x3c001f80,0x7800001f,0xc007f07e,0x1e001f,0xff0007ff, + 0xfc0007ff,0xc007fffc,0x3fffc,0x7fffffe,0xf0003c1,0xf0000f9e,0xf0f,0x8003e1e0,0x1e01ff,0xfff8003f,0xf001e000,0x7fe0,0x3f00, + 0x0,0x1e0000,0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x1fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x3de0,0x0,0x7fff80,0x0,0xfffff80, + 0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe7bc07ff,0x3e1f000f,0x78000000,0x0,0x0,0xf780,0x7800078,0x0,0x780000,0x180000, + 0x1fff8000,0x1e00,0x1e0003c,0xfff,0xc001f0f8,0x0,0x7ffe00,0x0,0x0,0x0,0x3f,0xff00007f,0xf0000000,0x3ffc,0xfff0,0x3c00,0x0, + 0x7fffc00,0x0,0x7,0xf800003f,0xfe000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xe00001ff, + 0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000fc00,0x3c003ffe,0x1fff0, + 0xfff80,0x7ffc00,0x3ffe000,0x0,0xfffce0f0,0x3c0780,0x1e03c00,0xf01e000,0x781e0001,0xe01e0000,0x3fff00,0x1e0000,0x1e0000,0xf780003, + 0xcf78001f,0xf800078,0x3c000000,0x0,0x1e00,0x1e00,0xf7800,0x3e1f000,0x1e00000,0x1e00000,0xf780003e,0x1f0000fc,0x7c000f3d, + 0xe0000780,0x780,0x3de00,0xf3de00,0x7c3e000,0x0,0x0,0xf0000000,0xf000007b,0xc0001f0f,0x800001e0,0x1e0000,0x3e1f00,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, + 0x3c1e0000,0x1e0f0000,0x300007fc,0xfff,0xc00001e0,0x1e0,0x3c000,0x1c000,0x0,0x0,0x0,0x0,0x3c001ff,0xfc001ffe,0x3ffff,0xc01ffffc, + 0x3f80,0x3ffff80,0x7fff803,0xfffff803,0xfffe001f,0xffe00000,0x0,0x0,0x0,0x0,0xffff80,0x7f800,0xfe0003,0xffff8001,0xffff01ff, + 0xff0003ff,0xffe01fff,0xff001fff,0xf01e0007,0x803ffff0,0xfff80,0x3c003f00,0x7800001f,0xc007f07f,0x1e003f,0xff8007ff,0xff000fff, + 0xe007ffff,0x7fffc,0x7fffffe,0xf0003c0,0xf0000f1e,0xf07,0x8003c1f0,0x3e01ff,0xfff8003f,0xf001e000,0x7fe0,0x7f80,0x0,0xe0000, + 0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0, + 0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x3fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x78f0,0x0,0xffff80,0x0,0x3fffff80,0x1f, + 0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc7f80070,0x3e1f0007,0x70000000,0x0,0x0,0x7700,0x7c000f8,0x0,0x780000,0x180000, + 0x3fff8000,0x1f00,0x3e0003c,0x1f03,0xc001f0f8,0x0,0x703f00,0x0,0x0,0x0,0x3f,0xff0000f0,0xf8000000,0x303e,0xc0f8,0x7800,0x0, + 0xffffc00,0x0,0x7,0x3800003e,0x3e000000,0x1c00,0xe000,0x3c00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00000f,0xe00001ff, + 0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000fe00,0x3c007fff,0x3fff8, + 0x1fffc0,0xfffe00,0x7fff000,0x1,0xffffc0f0,0x3c0780,0x1e03c00,0xf01e000,0x781f0003,0xe01e0000,0x3fff80,0xe0000,0x3c0000,0x1e3c0003, + 0x8ff0001f,0xf80003c,0x78000000,0x0,0xe00,0x3c00,0x1e3c00,0x3e1f000,0xe00000,0x3c00001,0xe3c0003e,0x1f00007f,0xf8000e3f,0xc0000380, + 0xf00,0x78f00,0xe3fc00,0x7c3e000,0x0,0x0,0x70000001,0xe00000f1,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0000, + 0x30000ffe,0xf80,0xc00001e0,0x3c0,0x1e000,0x101c040,0x0,0x0,0x0,0x0,0x78003f0,0x7e001ffe,0x3f807,0xe01f00fe,0x3f80,0x3ffff80, + 0x7e01803,0xfffff007,0xe03f003f,0x3f00000,0x0,0x0,0x0,0x0,0xfc0fc0,0x3ffe00,0xfe0003,0xffffc003,0xf81f01ff,0xff8003ff,0xffe01fff, + 0xff003f01,0xf01e0007,0x803ffff0,0xfff80,0x3c007e00,0x7800001f,0xc007f07f,0x1e007e,0xfc007ff,0xff801f83,0xf007ffff,0x800fc07c, + 0x7fffffe,0xf0003c0,0xf0000f0f,0x1e07,0xc007c0f8,0x7c01ff,0xfff8003c,0xf000,0x1e0,0xffc0,0x0,0xf0000,0x1,0xe0000000,0x0,0x780000, + 0x3e,0x0,0x78000,0x3c00,0xf000,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0x800000f0,0x1f80, + 0x0,0x0,0x7e0780,0x0,0x0,0x1f82000,0x0,0x0,0x0,0x7070,0x0,0x1f80f80,0x0,0x7fffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x1,0xc3f80070,0x3f3f0007,0xf0000000,0x0,0x0,0x7f00,0x3e001f0,0x0,0x780000,0x180000,0x7f018000,0xf80,0x7c0003c,0x3e00, + 0x4001f0f8,0xfe00,0x400f00,0x0,0x0,0x0,0x7f000000,0xe0,0x38000000,0x1e,0x38,0x7800,0x0,0x1ffe1c00,0x0,0x0,0x38000078,0xf000000, + 0x1c00,0xe000,0x7f800,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xf00001ff,0xffc03f81,0xf007ffff,0xc03ffffe, + 0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf800fe00,0x3c00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800, + 0x3,0xf07fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x780f8007,0xc01e0000,0x7e0fc0,0xf0000,0x3c0000,0x1c1c0003,0x87f0001f,0xf80003f, + 0xf8000000,0x0,0xf00,0x3c00,0x1c1c00,0x3e1f000,0xf00000,0x3c00001,0xc1c0003e,0x1f00003f,0xc0000e1f,0xc00003c0,0xf00,0x70700, + 0xe1fc00,0x7c3e000,0x0,0x0,0x78000001,0xe00000e0,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0001,0xff801e0f, + 0x1f00,0x1e0,0x3c0,0x1e000,0x3c1c1e0,0x0,0x0,0x0,0x0,0x78007c0,0x1f001f9e,0x3c001,0xf010003e,0x7780,0x3c00000,0xf800000,0xf007, + 0xc01f007c,0x1f80000,0x0,0x0,0x0,0x0,0xe003e0,0x7fff00,0x1ef0003,0xc007e007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x301e0007, + 0x80007800,0x780,0x3c00fc00,0x7800001f,0xe00ff07f,0x1e00f8,0x3e00780,0x1fc03e00,0xf807801f,0xc01f001c,0xf000,0xf0003c0,0xf0000f0f, + 0x1e03,0xc00f8078,0x780000,0xf0003c,0xf000,0x1e0,0x1f3e0,0x0,0x78000,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0, + 0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0xf0,0xf80,0x0,0x0,0xf80180,0x0,0x0,0x1e00000, + 0x0,0x0,0x0,0xe038,0x0,0x3e00380,0x0,0xfe0f0000,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc0f00070,0x3b370003,0xe0000000, + 0x0,0x0,0x3e00,0x1e001e0,0x0,0x780000,0x180000,0x7c000000,0x780,0x780003c,0x3c00,0x0,0x7ffc0,0x780,0x0,0x0,0x3,0xffe00000, + 0x1c0,0x3c000000,0xe,0x38,0xf000,0x0,0x3ffe1c00,0x0,0x0,0x38000078,0xf000000,0x1c00,0xe000,0x7f000,0xf000,0x3de000,0x1ef0000, + 0xf780000,0x7bc00003,0xde00001e,0xf00003e7,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, + 0xe0001e03,0xfc00fe00,0x3c01f007,0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x7,0xc01f80f0,0x3c0780,0x1e03c00,0xf01e000,0x78078007, + 0x801e0000,0x7803c0,0x78000,0x780000,0x380e0003,0x81e00000,0x1f,0xf0000000,0x0,0x780,0x7800,0x380e00,0x0,0x780000,0x7800003, + 0x80e00000,0x1ff,0x80000e07,0x800001e0,0x1e00,0xe0380,0xe07800,0x0,0x0,0x0,0x3c000003,0xc00001c0,0x70000000,0x780,0x1e0000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x780000,0x3c1e0000,0x3c0e0007,0xfff01c07,0x1e00,0x1e0,0x780,0xf000,0x3e1c3e0,0x0,0x0,0x0,0x0,0xf0007c0,0x1f00181e,0x20000, + 0xf000001f,0xf780,0x3c00000,0x1f000000,0x1f00f,0x800f8078,0xf80000,0x0,0x0,0x0,0x0,0x8003e0,0x1fc0f80,0x1ef0003,0xc001e007, + 0x800101e0,0x7e003c0,0x1e00,0x7800,0x101e0007,0x80007800,0x780,0x3c00f800,0x7800001e,0xe00ef07f,0x801e00f0,0x1e00780,0x7c03c00, + 0x78078007,0xc01e0004,0xf000,0xf0003c0,0x78001e0f,0x1e03,0xe00f807c,0xf80000,0x1f0003c,0x7800,0x1e0,0x3e1f0,0x0,0x3c000,0x1, + 0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0, + 0x1e,0xf0,0x780,0x0,0x0,0x1f00080,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x1e03c,0x0,0x3c00080,0x0,0xf80f0000,0x0,0x1f0000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x3bf70003,0xe0000000,0x0,0x0,0x3e00,0x1f003e0,0x0,0x780000,0x180000,0x78000000,0x7c0,0xf80003c, + 0x3c00,0x0,0x1f01f0,0x780,0x0,0x0,0xf,0x80f80000,0x1c0,0x1c000000,0xe,0x38,0x1e000,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,0x7800000, + 0x1c00,0xe000,0x7fc00,0xf000,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x80007800,0x10078000,0x3c0000, + 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00ff00,0x3c01e003,0xc00f001e,0x7800f0,0x3c00780,0x1e003c00, + 0x7,0x800f00f0,0x3c0780,0x1e03c00,0xf01e000,0x7807c00f,0x801e0000,0xf803c0,0x3c000,0xf00000,0x780f0000,0x0,0x7,0xc0000000, + 0x0,0x3c0,0xf000,0x780f00,0x0,0x3c0000,0xf000007,0x80f00000,0x7ff,0xc0000000,0xf0,0x3c00,0x1e03c0,0x0,0x0,0x0,0x0,0x1e000007, + 0x800003c0,0x78000000,0xf00,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c1e001f,0xfff03803,0x80001e00,0x1e0,0x780,0xf000,0xf9cf80, + 0x0,0x0,0x0,0x0,0xf000780,0xf00001e,0x0,0xf800000f,0xe780,0x3c00000,0x1e000000,0x1e00f,0x78078,0x7c0000,0x0,0x0,0x0,0x0,0x1e0, + 0x3f003c0,0x1ef0003,0xc000f00f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,0x3c01f000,0x7800001e,0xe00ef07f, + 0x801e01f0,0x1e00780,0x3c07c00,0x78078003,0xc03e0000,0xf000,0xf0003c0,0x78001e0f,0x1e01,0xf01f003c,0xf00000,0x3e0003c,0x7800, + 0x1e0,0x7c0f8,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000, + 0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x8,0x40,0x0,0x7e0000,0x7c00000,0x1,0xf00f0000, + 0x0,0x3e0000,0x0,0x3f,0xfc0,0xfc3f0,0xfc3f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,0xf003c0,0x0,0x0,0x180000,0xf8000000, + 0x3c0,0xf00003c,0x3c00,0x0,0x3c0078,0x7ff80,0x0,0x0,0x1e,0x3c0000,0x1c0,0x1c000000,0xe,0xf0,0x0,0x0,0x7ffe1c00,0x0,0x0,0x380000f0, + 0x7800000,0x1c00,0xe000,0x3c00,0x0,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x8000f800,0x78000,0x3c0000, + 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00ff00,0x3c03e003,0xc01f001e,0xf800f0,0x7c00780,0x3e003c00, + 0xf,0x800f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803c00f,0x1fffc0,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x307,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x781e003f,0xfff03803, + 0x80001e00,0x1e0,0xf80,0xf000,0x3dde00,0x0,0x0,0x0,0x0,0xf000f00,0x780001e,0x0,0x7800000f,0x1e780,0x3c00000,0x3e000000,0x3e00f, + 0x780f0,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,0x7c001e0,0x3ef8003,0xc000f00f,0x1e0,0xf003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780, + 0x3c03e000,0x7800001e,0xf01ef07b,0xc01e01e0,0xf00780,0x3e07800,0x3c078003,0xe03c0000,0xf000,0xf0003c0,0x78001e0f,0x1e00,0xf01e003e, + 0x1f00000,0x3c0003c,0x7800,0x1e0,0x78078,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0, + 0xe70000,0x7800000,0x1,0xe00f0000,0x0,0x3c0000,0x0,0x3f,0xfc0,0xfc1f0,0x1f83f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0, + 0xf807c0,0x0,0x0,0x180000,0xf0000000,0x3e0,0x1f00003c,0x3e00,0x0,0x70001c,0x3fff80,0x0,0x0,0x38,0xe0000,0x1c0,0x1c000078, + 0x1c,0x1fe0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x7df000,0x3ef8000,0x1f7c0000,0xfbe00007, + 0xdf00003c,0x780003c7,0x8000f000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f780, + 0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0xf80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803e01f,0x1ffff8,0xf001e0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x0,0x0,0x1e0000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x780000,0x3c1e0000,0x781e003e,0x30703803,0x80001e00,0x1e0,0xf00,0x7800,0xff800,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e, + 0x0,0x7800000f,0x3c780,0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x2000000,0x800000,0x1e0,0x78000e0,0x3c78003, + 0xc000f01e,0x1e0,0xf803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x701cf07b,0xc01e01e0,0xf00780,0x1e07800, + 0x3c078001,0xe03c0000,0xf000,0xf0003c0,0x7c003e0f,0x1e00,0xf83e001e,0x1e00000,0x7c0003c,0x3c00,0x1e0,0xf807c,0x0,0x0,0x1fe0001, + 0xe1fc0000,0x7f00003,0xf8780007,0xf000003c,0x7f0,0x783f0,0x0,0x0,0x7800000,0x1e00000,0x3e0f8000,0xfc00007,0xf8000007,0xf00001fc, + 0xf,0xc0003fc0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,0x1818000, + 0x7800000,0x1,0xe00f0000,0x0,0x7c0000,0x0,0x1f,0x80001f80,0x7c1f8,0x1f83e0,0x0,0x0,0x0,0x70,0x38c70007,0xf8000000,0x7f03, + 0xf0000000,0x0,0x780780,0x0,0x0,0xfe0000,0xf0000000,0x1e0,0x1e00003c,0x3f00,0x0,0xe07f0e,0x7fff80,0x0,0x0,0x70,0x70000,0x1c0, + 0x1c000078,0x3c,0x1fc0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x78f000,0x3c78000,0x1e3c0000, + 0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00, + 0xf80f780,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0x1f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801e01e,0x1ffffc, + 0xf007e0,0x3fc000,0x1fe0000,0xff00000,0x7f800003,0xfc00001f,0xe0000fc0,0xfc00007f,0xfe0,0x7f00,0x3f800,0x1fc000,0x0,0x0,0x0, + 0x1,0xf000001f,0x80000ff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x1f80000,0x1fc1e000,0x0,0x0,0x0,0x0,0x1e1fc0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000, + 0x781c007c,0x30003803,0x80001f00,0x1e0,0xf00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,0x0,0x7800000f,0x3c780, + 0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x1e000000,0xf00000,0x3e0,0xf0000e0,0x3c78003,0xc000f01e,0x1e0,0x7803c0, + 0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c0f8000,0x7800001e,0x701cf079,0xe01e01e0,0xf00780,0x1e07800,0x3c078001,0xe03c0000, + 0xf000,0xf0003c0,0x3c003c0f,0x3e00,0x787c001f,0x3e00000,0xf80003c,0x3c00,0x1e0,0x1f003e,0x0,0x0,0x1fffc001,0xe7ff0000,0x3ffe000f, + 0xfe78003f,0xfc001fff,0xfe001ffc,0xf0078ffc,0x1ffc00,0x7ff000,0x7800f80,0x1e0000f,0x7f1fc01e,0x3ff0001f,0xfe00079f,0xfc0007ff, + 0x3c003c7f,0xf001fff8,0x1fffff0,0x3c003c0,0xf0000f1e,0xf1f,0x7c1f0,0x1f00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3c00000,0x100000, + 0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7800000,0x1,0xe00f0000,0x1000000,0xf80000,0x40000002,0xf,0x80001f00,0x7e0f8,0x1f07c0, + 0x0,0x0,0x0,0x70,0x38c7003f,0xff000000,0xff8f,0xf8000100,0xffffe,0x7c0f80,0x0,0x0,0x3ffc000,0xf0000020,0x1001f0,0x3c00003c, + 0x1f80,0x0,0x1c3ffc7,0x7c0780,0x0,0x0,0xe3,0xff038000,0xe0,0x38000078,0x78,0x1ff0,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0, + 0x7800000,0x1c00,0xe000,0xe00,0xf000,0x78f000,0x3c78000,0x1e3c0000,0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000, + 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00, + 0x4000200f,0x3f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801f03e,0x1ffffe,0xf01fe0,0x3fff800,0x1fffc000,0xfffe0007,0xfff0003f, + 0xff8001ff,0xfc003ff3,0xfe0003ff,0xe0007ff8,0x3ffc0,0x1ffe00,0xfff000,0x3ff80001,0xffc0000f,0xfe00007f,0xf000003f,0xf8003c7f, + 0xe0003ffc,0x1ffe0,0xfff00,0x7ff800,0x3ffc000,0x1f80000,0xfff1c03c,0x3c01e0,0x1e00f00,0xf007800,0x781f0001,0xf01e7ff0,0x7c0007c, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, + 0x3c1e003f,0xfffff078,0x30003803,0x80000f00,0x1e0,0x1f00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x3c000f00,0x780001e,0x0,0x7800000f, + 0x78780,0x3c00000,0x3c000000,0x7c00f,0x780f0,0x3c0007,0xe000003f,0x0,0xfe000000,0xfe0000,0x3c0,0x1f000070,0x7c7c003,0xc000f01e, + 0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c1f0000,0x7800001e,0x783cf079,0xe01e03c0,0xf00780,0x1e0f000,0x3c078001, + 0xe03c0000,0xf000,0xf0003c0,0x3c003c07,0x81f03c00,0x7c7c000f,0x87c00000,0xf00003c,0x1e00,0x1e0,0x3e001f,0x0,0x0,0x3fffe001, + 0xefff8000,0x7fff001f,0xff78007f,0xfe001fff,0xfe003ffe,0xf0079ffe,0x1ffc00,0x7ff000,0x7801f00,0x1e0000f,0xffbfe01e,0x7ff8003f, + 0xff0007bf,0xfe000fff,0xbc003cff,0xf803fffc,0x1fffff0,0x3c003c0,0x78001e1e,0xf0f,0x800f80f0,0x1e00ff,0xffe0001e,0xf0,0x780, + 0x0,0x0,0x3c00000,0x380000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1008000,0x7800000,0x3,0xe00f0000,0x3800000,0xf00000,0xe0000007, + 0xf,0x80001f00,0x3e0f8,0x1e07c0,0x0,0x0,0x0,0x70,0x3807007f,0xff800000,0x1ffdf,0xfc000380,0xffffe,0x3e1f00,0x0,0x0,0xfffe000, + 0xf0000030,0x3800f8,0x7c00003c,0xfc0,0x0,0x18780c3,0xf00780,0x80100,0x0,0xc3,0xffc18000,0xf0,0x78000078,0xf0,0xf0,0x0,0x3c003c0, + 0xfffe1c00,0x0,0x0,0x380000f0,0x7800801,0x1c00,0xe000,0x1e00,0xf000,0xf8f800,0x7c7c000,0x3e3e0001,0xf1f0000f,0x8f80007c,0x7c000787, + 0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078001,0xe03c000f, + 0x1e00078,0xf0003c0,0x78001e00,0xe000701f,0x3fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x7800f87c,0x1e007f,0xf07e00,0x7fffc00,0x3fffe001, + 0xffff000f,0xfff8007f,0xffc003ff,0xfe007ff7,0xff0007ff,0xf000fffc,0x7ffe0,0x3fff00,0x1fff800,0x3ff80001,0xffc0000f,0xfe00007f, + 0xf00000ff,0xf8003cff,0xf0007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x1f80001,0xfffb803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001, + 0xe01efff8,0x3c00078,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e003f,0xfffff078,0x30001c07,0xf80,0x1e0,0x1e00,0x3c00,0xff800,0x1e0000,0x0,0x0,0x0,0x3c001e00, + 0x3c0001e,0x0,0x7800001e,0x70780,0x3c00000,0x78000000,0x78007,0x800f00f0,0x3e0007,0xe000003f,0x3,0xfe000000,0xff8000,0x7c0, + 0x1e000070,0x783c003,0xc001f01e,0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c3e0000,0x7800001e,0x3838f079, + 0xe01e03c0,0x780780,0x1e0f000,0x1e078001,0xe03c0000,0xf000,0xf0003c0,0x3c007c07,0x81f03c00,0x3ef80007,0x87800000,0x1f00003c, + 0x1e00,0x1e0,0x7c000f,0x80000000,0x0,0x3ffff001,0xffffc000,0xffff003f,0xff7800ff,0xff001fff,0xfe007ffe,0xf007bffe,0x1ffc00, + 0x7ff000,0x7803e00,0x1e0000f,0xffffe01e,0xfff8007f,0xff8007ff,0xff001fff,0xbc003dff,0xf807fffc,0x1fffff0,0x3c003c0,0x78001e0f, + 0x1e07,0xc01f00f0,0x1e00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7c00000,0x7c0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1018000,0x7800000, + 0x3,0xc00f0000,0x7c00000,0x1f00001,0xf000000f,0x80000007,0xc0003e00,0x1e07c,0x3e0780,0x0,0x0,0x0,0x70,0x380700ff,0xff800000, + 0x3ffff,0xfe0007c0,0xffffe,0x1e1e00,0x0,0x780000,0x1fffe000,0xf0000078,0x7c0078,0x7800003c,0xff0,0x0,0x38e0003,0x80f00780, + 0x180300,0x0,0x1c3,0x81e1c000,0x7f,0xf0000078,0x1e0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800c01,0x80001c00, + 0xe000,0x603e00,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x7800078,0x3c000f87,0x8001e000,0x78000,0x3c0000,0x1e00000, + 0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f01,0xf000f81e, + 0x7bc0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007878,0x1e001f,0xf0f800,0x7fffe00,0x3ffff001,0xffff800f,0xfffc007f,0xffe003ff, + 0xff007fff,0xff800fff,0xf001fffe,0xffff0,0x7fff80,0x3fffc00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00001ff,0xfc003dff,0xf000ffff, + 0x7fff8,0x3fffc0,0x1fffe00,0xffff000,0x1f80003,0xffff803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,0xe01ffffc,0x3c00078,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, + 0x3c1e003f,0xfffff078,0x30001e0f,0x300780,0x1e0,0x1e00,0x3c00,0x3dde00,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf800003e, + 0xf0780,0x3dfc000,0x783f8000,0xf8007,0xc01f00f0,0x3e0007,0xe000003f,0x1f,0xfc000000,0x7ff000,0xf80,0x3e007c70,0x783c003,0xc001e03c, + 0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,0x80007800,0x780,0x3c7c0000,0x7800001e,0x3878f078,0xf01e03c0,0x780780,0x1e0f000,0x1e078001, + 0xe03e0000,0xf000,0xf0003c0,0x1e007807,0x83f03c00,0x3ef00007,0xcf800000,0x3e00003c,0xf00,0x1e0,0xf80007,0xc0000000,0x0,0x3e01f801, + 0xfe07e001,0xf80f007e,0x7f801f8,0x1f801fff,0xfe00fc0f,0xf007f83f,0x1ffc00,0x7ff000,0x7807c00,0x1e0000f,0x87e1e01f,0xe0fc00fc, + 0xfc007f8,0x1f803f03,0xfc003df0,0x3807e03c,0x1fffff0,0x3c003c0,0x78003e0f,0x1e03,0xe03e00f8,0x3e00ff,0xffe0001e,0xf0,0x780, + 0x0,0x0,0x7800000,0xfe0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7c00000,0x3,0xc00f0000,0xfe00000,0x3e00003,0xf800001f, + 0xc0000007,0xc0003e00,0x1e03c,0x3c0f80,0x0,0x0,0x0,0x70,0x380700fc,0x7800000,0x7c1fe,0x3e000fe0,0xffffe,0x1f3e00,0x0,0x780000, + 0x3f98e000,0xf000003c,0xfcf8007c,0xf800003c,0x3ffc,0x0,0x31c0001,0x80f00f80,0x380700,0x0,0x183,0x80e0c000,0x3f,0xe0000078, + 0x3c0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x38000078,0xf000e01,0xc003ffe0,0x1fff00,0x7ffc00,0xf000,0xf07800,0x783c000,0x3c1e0001, + 0xe0f0000f,0x7800078,0x3c000f07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00, + 0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf801f01e,0xf3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007cf8, + 0x1e000f,0x80f0f000,0x7c03f00,0x3e01f801,0xf00fc00f,0x807e007c,0x3f003e0,0x1f80707f,0x8f801f80,0xf003f03f,0x1f81f8,0xfc0fc0, + 0x7e07e00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00003ff,0xfc003fc1,0xf801f81f,0x800fc0fc,0x7e07e0,0x3f03f00,0x1f81f800,0x1f80007, + 0xe07f003c,0x3c01e0,0x1e00f00,0xf007800,0x780f8003,0xe01fe07e,0x3e000f8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3f,0xfffff078,0x30000ffe,0x1f007c0,0x0,0x1e00, + 0x3c00,0xf9cf80,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf00000fc,0x1e0780,0x3fff800,0x78ffe000,0xf0003,0xe03e00f0, + 0x3e0007,0xe000003f,0x7f,0xe01fffff,0xf00ffc00,0x1f80,0x3c01ff70,0x783c003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007, + 0x80007800,0x780,0x3cfc0000,0x7800001e,0x3c78f078,0xf01e03c0,0x780780,0x3e0f000,0x1e078003,0xc01f0000,0xf000,0xf0003c0,0x1e007807, + 0x83f83c00,0x1ff00003,0xcf000000,0x3e00003c,0xf00,0x1e0,0x0,0x0,0x0,0x20007801,0xfc03e003,0xe003007c,0x3f803e0,0x7c0003c, + 0xf807,0xf007e00f,0x3c00,0xf000,0x780f800,0x1e0000f,0x87e1f01f,0x803c00f8,0x7c007f0,0xf803e01,0xfc003f80,0x80f8004,0x3c000, + 0x3c003c0,0x3c003c0f,0x1e03,0xe03e0078,0x3c0000,0x7c0001e,0xf0,0x780,0x0,0x0,0x3ffff800,0x1ff0000,0x0,0x7800000,0x0,0x18, + 0xc0,0x0,0x1818000,0x3e00000,0x3,0xc00f0000,0x1ff00000,0x3e00007,0xfc00003f,0xe0000003,0xc0003c00,0xf03c,0x3c0f00,0x0,0x0, + 0x0,0x70,0x380701f0,0x800000,0x780fc,0x1e001ff0,0x7c,0xf3c00,0x0,0x780000,0x7e182000,0xf000001f,0xfff00ffc,0xffc0003c,0x3cfe, + 0x0,0x31c0001,0x80f01f80,0x780f00,0x0,0x183,0x80e0c000,0xf,0x80000078,0x780,0x38,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x38000078, + 0xf000f01,0xe003ffe0,0x1fff00,0x7ff800,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x78000f8,0x3e000f07,0x8003c000,0x78000, + 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0, + 0x78000f00,0x7c03e01e,0x1e3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78003cf0,0x1e0007,0x80f1e000,0x4000f00,0x20007801,0x3c008, + 0x1e0040,0xf00200,0x780403f,0x7803e00,0x3007c00f,0x803e007c,0x1f003e0,0xf801f00,0x780000,0x3c00000,0x1e000000,0xf00007f0, + 0x3e003f00,0x7801f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e003c,0x3c01e0,0x1e00f00,0xf007800,0x78078003, + 0xc01fc03e,0x1e000f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xf078007c,0x300007fc,0x7e00fe0,0x0,0x1e00,0x3c00,0x3e1c3e0,0x1e0000,0x0,0x0,0x0,0xf0001e00, + 0x3c0001e,0x1,0xf000fff8,0x1e0780,0x3fffe00,0x79fff000,0x1f0001,0xfffc00f0,0x7e0007,0xe000003f,0x3ff,0x801fffff,0xf003ff80, + 0x3f00,0x3c03fff0,0xf01e003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3df80000,0x7800001e, + 0x1c70f078,0x781e03c0,0x780780,0x3c0f000,0x1e078007,0xc01f8000,0xf000,0xf0003c0,0x1e007807,0x83f83c00,0xfe00003,0xff000000, + 0x7c00003c,0x780,0x1e0,0x0,0x0,0x0,0x7c01,0xf801f007,0xc00100f8,0x1f803c0,0x3c0003c,0x1f003,0xf007c00f,0x80003c00,0xf000, + 0x783f000,0x1e0000f,0x3c0f01f,0x3e01f0,0x3e007e0,0x7c07c00,0xfc003f00,0xf0000,0x3c000,0x3c003c0,0x3c003c0f,0x1e01,0xf07c007c, + 0x7c0000,0xfc0001e,0xf0,0x780,0x0,0x0,0x3ffff000,0x3838000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0xff0000,0x3f00000,0x3,0xc00fff00, + 0x38380000,0x7c0000e,0xe000070,0x70000001,0xe0003c00,0xf01e,0x780e00,0x0,0x0,0x0,0x0,0x1e0,0x0,0x780f8,0xf003838,0xfc,0xffc00, + 0x0,0x780000,0x7c180000,0xf000000f,0xffe00fff,0xffc0003c,0x783f,0x80000000,0x6380000,0xc0f83f80,0xf81f00,0x0,0x303,0x80e06000, + 0x0,0x78,0xf00,0x78,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x3800003c,0x3e000f81,0xf003ffe0,0x1fff00,0x1fc000,0xf000,0x1e03c00, + 0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e000f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000, + 0x3c000001,0xe0001e00,0x3c0f0f0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3e07c01e,0x1e3c0f0,0x3c0780,0x1e03c00, + 0xf01e000,0x78003ff0,0x1e0007,0x80f1e000,0xf80,0x7c00,0x3e000,0x1f0000,0xf80000,0x7c0001e,0x3c07c00,0x10078007,0x803c003c, + 0x1e001e0,0xf000f00,0x780000,0x3c00000,0x1e000000,0xf00007c0,0x1e003e00,0x7c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00, + 0xf,0x801f003c,0x3c01e0,0x1e00f00,0xf007800,0x7807c007,0xc01f801f,0x1f001f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xe078003c,0x300001f0,0x3f801ff0,0x0, + 0x3c00,0x1e00,0x3c1c1e0,0x1e0000,0x0,0x0,0x0,0xf0001e0f,0x3c0001e,0x3,0xe000fff0,0x3c0780,0x3ffff00,0x7bfff800,0x1e0000,0x7ff00078, + 0x7e0007,0xe000003f,0x1ffc,0x1fffff,0xf0007ff0,0x7e00,0x3c07c3f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000, + 0x1fffff,0x80007800,0x780,0x3ffc0000,0x7800001e,0x1ef0f078,0x781e03c0,0x780780,0x7c0f000,0x1e07801f,0x800ff000,0xf000,0xf0003c0, + 0xf00f807,0x83b83c00,0xfc00001,0xfe000000,0xf800003c,0x780,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0xc00000f0,0xf80780,0x3c0003c, + 0x1e001,0xf007c007,0x80003c00,0xf000,0x787e000,0x1e0000f,0x3c0f01f,0x1e01e0,0x1e007c0,0x3c07800,0x7c003f00,0xf0000,0x3c000, + 0x3c003c0,0x3e007c07,0x80003c00,0xf8f8003c,0x780000,0xf80001e,0xf0,0x780,0x0,0x0,0x7ffff000,0x601c000,0x3,0xffff0000,0x0, + 0xfff,0xf8007fff,0xc0000000,0x7e003c,0x1fe0000,0xc0003,0xc00fff00,0x601c0000,0xf800018,0x70000c0,0x38000001,0xe0007800,0x701e, + 0x701e00,0x0,0x0,0x0,0x0,0x1e0,0x6,0x700f8,0xf00601c,0xf8,0x7f800,0x0,0x780000,0xf8180000,0xf000000f,0x87c00fff,0xffc0003c, + 0xf01f,0xc0000000,0x6380000,0xc07ff780,0x1f03e03,0xfffffe00,0x303,0x81c06000,0x0,0x1ffff,0xfe001e00,0x180f8,0x0,0x3c003c0, + 0x3ffe1c00,0x3f00000,0x0,0x3800003f,0xfe0007c0,0xf8000000,0x18000000,0xc0000006,0x1f000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e, + 0x3c000f0,0x1e001f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f0f0, + 0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f0f801e,0x3c3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007, + 0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07c00,0xf0007,0x8078003c,0x3c001e0,0x1e000f00,0x780000,0x3c00000, + 0x1e000000,0xf0000f80,0x1f003e00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0xf,0x3f003c,0x3c01e0,0x1e00f00,0xf007800, + 0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe078003f,0xb0000000,0xfc003cf0,0x0,0x3c00,0x1e00,0x101c040,0x1e0000,0x0,0x0,0x1, + 0xe0001e1f,0x83c0001e,0x7,0xe000fff0,0x3c0780,0x3c03f80,0x7fc0fc00,0x1e0000,0xfff80078,0xfe0007,0xe000003f,0x7fe0,0x1fffff, + 0xf0000ffc,0xfc00,0x780f81f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3ffc0000, + 0x7800001e,0x1ef0f078,0x3c1e03c0,0x780780,0x1fc0f000,0x1e07ffff,0x7ff00,0xf000,0xf0003c0,0xf00f007,0xc3b87c00,0x7c00001,0xfe000000, + 0xf800003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0x800000f0,0xf80780,0x1e0003c,0x1e001,0xf0078007,0x80003c00,0xf000,0x78fc000, + 0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,0x3c07800,0x7c003e00,0xf0000,0x3c000,0x3c003c0,0x1e007807,0x80003c00,0x7df0003c,0x780000, + 0x1f00001e,0xf0,0x780,0x0,0x0,0x7800000,0xe7ce000,0x3,0xffff0000,0x0,0xfff,0xf8007fff,0xc0000000,0x1f0,0xffe000,0x1c0003, + 0xc00fff00,0xe7ce0000,0xf800039,0xf38001cf,0x9c000000,0xe0007800,0x780e,0x701c00,0x0,0x0,0x0,0x0,0x1e0,0x7,0xf0078,0xf00e7ce, + 0x1f0,0x7f800,0x0,0x780000,0xf0180000,0xf000000e,0x1c0001f,0xe000003c,0xf007,0xe0000000,0x6380000,0xc03fe780,0x3e07c03,0xfffffe00, + 0x303,0xffc06000,0x0,0x1ffff,0xfe003ffe,0x1fff0,0x0,0x3c003c0,0x1ffe1c00,0x3f00000,0x7,0xffc0001f,0xfc0003e0,0x7c000001,0xfc00000f, + 0xe000007f,0x1e000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0, + 0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e, + 0x783c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07800, + 0xf0003,0xc078001e,0x3c000f0,0x1e000780,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c03c003,0xc01e001e,0xf000f0, + 0x7800780,0x3c003c00,0xf,0x7f003c,0x3c01e0,0x1e00f00,0xf007800,0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe070001f,0xf8000007, + 0xf0007cf8,0x7800000,0x3c00,0x1e00,0x1c000,0x1e0000,0x0,0x0,0x1,0xe0001e1f,0x83c0001e,0xf,0xc000fff8,0x780780,0x2000f80,0x7f803e00, + 0x3e0003,0xfffe007c,0x1fe0000,0x0,0x3ff00,0x0,0x1ff,0x8001f000,0x780f00f0,0x1f00f003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff, + 0xfe03c00f,0xf81fffff,0x80007800,0x780,0x3ffe0000,0x7800001e,0xee0f078,0x3c1e03c0,0x7807ff,0xff80f000,0x1e07fffe,0x3ffe0, + 0xf000,0xf0003c0,0xf00f003,0xc7bc7800,0xfc00000,0xfc000001,0xf000003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xe000f80f,0x800001e0, + 0xf80f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x79f8000,0x1e0000f,0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003e00, + 0xf0000,0x3c000,0x3c003c0,0x1e007807,0x81e03c00,0x7df0003e,0xf80000,0x3e00003e,0xf0,0x7c0,0xfc000,0x80000000,0x7800000,0x1e7cf000, + 0x3,0xffff0000,0x0,0x18,0xc0,0x0,0xf80,0x7ffc00,0x380003,0xc00fff01,0xe7cf0000,0x1f000079,0xf3c003cf,0x9e000000,0xe0007000, + 0x380e,0xe01c00,0x0,0x0,0x0,0x0,0x1e0,0x3,0x800f0078,0xf01e7cf,0x3e0,0x3f000,0x0,0x780000,0xf018001f,0xfff8001e,0x1e0000f, + 0xc000003c,0xf003,0xe0000000,0x6380000,0xc00fc780,0x7c0f803,0xfffffe00,0x303,0xfe006000,0x0,0x1ffff,0xfe003ffe,0x1ffe0,0x0, + 0x3c003c0,0xffe1c00,0x3f00000,0x7,0xffc00007,0xf00001f0,0x3e00001f,0xfc0000ff,0xe00007ff,0x3e000,0x3e01e00,0x1f00f000,0xf8078007, + 0xc03c003e,0x1e001e0,0xf001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8, + 0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000fc0, + 0x1e0007,0x80f1f000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c0f800,0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000, + 0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1e,0xf7803c,0x3c01e0,0x1e00f00, + 0xf007800,0x7803e00f,0x801e000f,0x80f803e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe0f0000f,0xff00001f,0x8000f87c,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80, + 0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x1f,0x800000fe,0xf00780,0x7c0,0x7f001e00,0x3c0007,0xe03f003f,0x3fe0000,0x0,0x3fc00,0x0, + 0x7f,0x8001e000,0x781f00f0,0x1e00f003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3f9f0000,0x7800001e, + 0xfe0f078,0x3c1e03c0,0x7807ff,0xff00f000,0x1e07fff8,0xfff8,0xf000,0xf0003c0,0xf81f003,0xc7bc7800,0xfe00000,0x78000003,0xe000003c, + 0x1e0,0x1e0,0x0,0x0,0x0,0x1fffc01,0xe000780f,0x1e0,0x780f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7bf0000,0x1e0000f, + 0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xf8000,0x3c000,0x3c003c0,0x1f00f807,0x81f03c00,0x3fe0001e,0xf00000,0x7c00007c, + 0xf0,0x3e0,0x3ff801,0x80000000,0x7800000,0x3cfcf800,0x3,0xffff0000,0x0,0x18,0xc0,0x0,0x7c00,0x1fff00,0x700003,0xc00f0003, + 0xcfcf8000,0x3e0000f3,0xf3e0079f,0x9f000000,0xf000,0x1000,0x0,0x0,0x0,0x0,0x0,0x1f0,0x1,0xc00f0078,0xf03cfcf,0x800007c0,0x1e000, + 0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x8000003c,0xf001,0xf0000000,0x6380000,0xc0000000,0xf81f003,0xfffffe00,0x303, + 0x87006000,0x0,0x1ffff,0xfe003ffe,0x7f00,0x0,0x3c003c0,0x3fe1c00,0x3f00000,0x7,0xffc00000,0xf8,0x1f0001ff,0xf0000fff,0x80007ffc, + 0xfc000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf001e07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000, + 0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3fc001e,0x1e03c0f0,0x3c0780, + 0x1e03c00,0xf01e000,0x78000780,0x1e0007,0x80f0fc00,0x3fff80,0x1fffc00,0xfffe000,0x7fff0003,0xfff8001f,0xffc0001e,0x3c0f000, + 0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,0x3c00000,0x1e000000,0xf0001e00,0xf803c00,0x3c078001,0xe03c000f,0x1e00078, + 0xf0003c0,0x78001e07,0xfffffe1e,0x1e7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801e00f,0x1e0007,0x807803c0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00007, + 0xffc0007e,0xf03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x3f,0x3e,0xf00780,0x3c0,0x7e001e00, + 0x7c000f,0x800f001f,0xffde0000,0x0,0x3e000,0x0,0xf,0x8003e000,0x781e0070,0x1e00f003,0xc001f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f, + 0xf81e0007,0x80007800,0x780,0x3f1f0000,0x7800001e,0x7c0f078,0x1e1e03c0,0x7807ff,0xfc00f000,0x1e07fffe,0xffc,0xf000,0xf0003c0, + 0x781e003,0xc71c7800,0x1ff00000,0x78000003,0xe000003c,0x1e0,0x1e0,0x0,0x0,0x0,0xffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c, + 0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7f000,0x3c000, + 0x3c003c0,0xf00f007,0xc1f07c00,0x1fc0001f,0x1f00000,0xfc000ff8,0xf0,0x1ff,0xfffe07,0x80000000,0x7800000,0x7ffcfc00,0x0,0xf000000, + 0x0,0x18,0xc0,0x0,0x3e000,0x1ff80,0xe00003,0xc00f0007,0xffcfc000,0x3e0001ff,0xf3f00fff,0x9f800000,0x6000,0x0,0x0,0x7c000, + 0x0,0x0,0x0,0xfe,0x0,0xe00f007f,0xff07ffcf,0xc0000fc0,0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x80000000,0xf800, + 0xf0000000,0x6380000,0xc0000000,0x1f03c000,0x1e00,0x303,0x83806000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xfe1c00,0x3f00000,0x0, + 0x0,0x3c,0xf801fff,0xfff8,0x7ffc0,0x1f8000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf003c07,0x8003c000,0x78000, + 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0, + 0x78000f00,0x1f8001e,0x1e03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e000f,0x80f0ff00,0x1ffff80,0xffffc00,0x7fffe003, + 0xffff001f,0xfff800ff,0xffc007ff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00, + 0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x3c7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801f01f, + 0x1e0007,0x807c07c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00000,0xfff003f0,0x1f00f03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x7ff80000,0x3, + 0xc0001e0f,0x3c0001e,0x7e,0x1f,0x1e00780,0x3e0,0x7e000f00,0x78000f,0x7800f,0xff9e0000,0x0,0x3fc00,0x0,0x7f,0x8003c000,0x781e0070, + 0x3e00f803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3e0f8000,0x7800001e,0x7c0f078,0x1e1e03c0, + 0x7807ff,0xf000f000,0x1e07807f,0xfe,0xf000,0xf0003c0,0x781e003,0xc71c7800,0x3ef00000,0x78000007,0xc000003c,0x1e0,0x1e0,0x0, + 0x0,0x0,0x1ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e, + 0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7ff80,0x3c000,0x3c003c0,0xf00f003,0xc1f07800,0x1fc0000f,0x1e00000,0xf8000ff0,0xf0, + 0xff,0xffffff,0x80000000,0x3fffc000,0xfff9fe00,0x0,0xf000000,0x0,0x18,0xc0,0x0,0x1f0000,0x1fc0,0x1c00003,0xc00f000f,0xff9fe000, + 0x7c0003ff,0xe7f81fff,0x3fc00000,0x0,0x0,0x0,0xfe000,0x1ffffc0f,0xfffffc00,0x0,0xff,0xf0000000,0x700f007f,0xff0fff9f,0xe0000f80, + 0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00fff,0xffc00000,0xf800,0xf0000000,0x6380000,0xc0ffff80,0x3e078000,0x1e00,0x7ff80303, + 0x83c06000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,0x0,0x7f,0xff00001e,0x7c1fff0,0xfff80,0x7ffc00,0x3f0000,0x7c01f00, + 0x3e00f801,0xf007c00f,0x803e007c,0x1f003e0,0xf803c07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, + 0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f8001e,0x3c03c0f0,0x3c0780,0x1e03c00,0xf01e000, + 0x78000780,0x1e001f,0xf07f80,0x3ffff80,0x1ffffc00,0xffffe007,0xffff003f,0xfff801ff,0xffc03fff,0xffc0f000,0x1fffff,0xc0fffffe, + 0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07, + 0xfffffe1e,0x787803c,0x3c01e0,0x1e00f00,0xf007800,0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x3ff80fc0,0x7fc1e01f, + 0x7800000,0x3c00,0x1e00,0x0,0x7fffff80,0x0,0x7ff80000,0x7,0x80001e00,0x3c0001e,0xfc,0xf,0x1e00780,0x1e0,0x7c000f00,0x78000f, + 0x78007,0xff1e0000,0x0,0x3ff00,0x0,0x1ff,0x8003c000,0x781e0070,0x3c007803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007, + 0x80007800,0x780,0x3c07c000,0x7800001e,0x7c0f078,0xf1e03c0,0x780780,0xf000,0x1e07801f,0x3e,0xf000,0xf0003c0,0x781e003,0xcf1c7800, + 0x3cf80000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,0x0,0x0,0x3ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007, + 0x80003c00,0xf000,0x7ff8000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3fff0,0x3c000,0x3c003c0,0xf81f003, + 0xc3b87800,0xf80000f,0x1e00001,0xf0000ff0,0xf0,0xff,0xf03fff,0x80000000,0x3fff8001,0xfff1ff00,0x0,0xf000000,0x0,0x18,0xc0, + 0x0,0x380000,0x7c0,0x3c00003,0xc00f001f,0xff1ff000,0xf80007ff,0xc7fc3ffe,0x3fe00000,0x0,0x0,0x0,0x1ff000,0x7ffffe1f,0xffffff00, + 0x0,0x7f,0xfe000000,0x780f007f,0xff1fff1f,0xf0001f00,0x1e000,0x0,0x780001,0xe0180000,0xf000001c,0xe00fff,0xffc00000,0x7c00, + 0xf0000000,0x31c0001,0x80ffff80,0x3e078000,0x1e00,0x7ff80183,0x81c0c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000, + 0x0,0x7f,0xff00001e,0x7c7ff03,0xc03ff8fe,0x1ffc0f0,0x7e0000,0x7800f00,0x3c007801,0xe003c00f,0x1e0078,0xf003c0,0x7803c07,0x8003c000, + 0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c, + 0xf0001e0,0x78000f00,0x3fc001e,0x7803c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e007f,0xf03fe0,0x7ffff80,0x3ffffc01, + 0xffffe00f,0xffff007f,0xfff803ff,0xffc07fff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000, + 0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x707803c,0x3c01e0,0x1e00f00,0xf007800, + 0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x30f81f00,0xffe1e00f,0x87800000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000, + 0x7,0x80001e00,0x3c0001e,0x1f8,0x7,0x83c00780,0x1e0,0x7c000f00,0xf8001e,0x3c001,0xfc1e0000,0x0,0x7fe0,0x0,0xffc,0x3c000,0x781e0070, + 0x3ffff803,0xc000783c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x380f078,0xf1e03c0, + 0x780780,0xf000,0x1e07800f,0x8000001e,0xf000,0xf0003c0,0x3c3c003,0xcf1e7800,0x7c780000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0, + 0x0,0x0,0x7f003c01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7f7c000,0x1e0000f,0x3c0f01e, + 0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfff8,0x3c000,0x3c003c0,0x781e003,0xc3b87800,0x1fc00007,0x83e00003,0xe0000ff8,0xf0, + 0x1ff,0xc007fe,0x0,0x7fff8001,0xffe3ff00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x3c0,0x7800003,0xc00f001f,0xfe3ff000,0xf80007ff, + 0x8ffc3ffc,0x7fe00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x1f,0xff000000,0x3c0f007f,0xff1ffe3f,0xf0003e00,0x1e000,0x0,0x780001, + 0xe0180000,0xf000001e,0x1e00fff,0xffc00000,0x3f00,0xf0000000,0x31c0001,0x80ffff80,0x1f03c000,0x1e00,0x7ff80183,0x81c0c000, + 0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x7f,0xff00003c,0xf87f007,0xc03f83ff,0x81fc01f0,0x7c0000,0x7ffff00,0x3ffff801, + 0xffffc00f,0xfffe007f,0xfff003ff,0xff807fff,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, + 0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf003c0f0,0x3c0780,0x1e03c00,0xf01e000, + 0x78000780,0x1ffffe,0xf00ff0,0xfe00780,0x7f003c03,0xf801e01f,0xc00f00fe,0x7807f0,0x3c0ffff,0xffc0f000,0x1fffff,0xc0fffffe, + 0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00, + 0x1e,0xf07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783e,0x1e0007,0x801e0f80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x307c0801,0xe1f1e00f,0x87000000, + 0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,0xf,0x1e00,0x3c0001e,0x3f0,0x7,0x83fffffc,0x1e0,0x7c000f00,0xf0001e,0x3c000,0x3e0000, + 0x0,0x1ffc,0x1fffff,0xf0007ff0,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x3c000,0x781e0007,0x80007800, + 0x780,0x3c03e000,0x7800001e,0xf078,0x79e03c0,0x780780,0xf000,0x1e078007,0x8000000f,0xf000,0xf0003c0,0x3c3c001,0xee0ef000, + 0xf87c0000,0x7800001f,0x3c,0x78,0x1e0,0x0,0x0,0x0,0x7c003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00, + 0xf000,0x7e3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x1ffc,0x3c000,0x3c003c0,0x781e003,0xe3b8f800, + 0x1fc00007,0x83c00007,0xc00000fc,0xf0,0x3e0,0x8001f8,0x0,0x7800000,0xffc7fe00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0, + 0xf000003,0xc00f000f,0xfc7fe001,0xf00003ff,0x1ff81ff8,0xffc00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x3,0xff800000,0x1e0f0078, + 0xffc7f,0xe0007c00,0x1e000,0x0,0x780001,0xe0180000,0xf000000e,0x1c00007,0x80000000,0x1f81,0xe0000000,0x38e0003,0x80000000, + 0xf81f000,0x1e00,0x7ff801c3,0x80e1c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf8,0x1f070007,0xc03803ff,0xc1c001f0, + 0xf80000,0xfffff00,0x7ffff803,0xffffc01f,0xfffe00ff,0xfff007ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000, + 0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f00f,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,0xf003c0f0, + 0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1ffffc,0xf003f8,0xf800780,0x7c003c03,0xe001e01f,0xf00f8,0x7807c0,0x3c0fc1e,0xf000, + 0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078, + 0xf0003c0,0x78001e00,0x1e,0x1e07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783c,0x1e0007,0x801e0f00,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xffff8000,0x303c0001, + 0xc071e007,0xcf000000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0xf,0xf00,0x780001e,0x7e0,0x7,0x83fffffc,0x1e0,0x7c000f00,0x1f0001e, + 0x3c000,0x3c0000,0x0,0x3ff,0x801fffff,0xf003ff80,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007, + 0x80007800,0x780,0x3c01f000,0x7800001e,0xf078,0x79e03c0,0xf00780,0xf000,0x3e078007,0xc000000f,0xf000,0xf0003c0,0x3c3c001, + 0xee0ef000,0xf03e0000,0x7800003e,0x3c,0x78,0x1e0,0x0,0x0,0x0,0xf8003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007, + 0x80003c00,0xf000,0x7c3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfc,0x3c000,0x3c003c0,0x3c3e001,0xe7b8f000, + 0x3fe00007,0xc7c0000f,0xc000003e,0xf0,0x7c0,0x0,0x0,0x7c00000,0x7fcffc00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,0x1e000003, + 0xc00f0007,0xfcffc003,0xe00001ff,0x3ff00ff9,0xff800000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x1f800000,0xf0f0078,0x7fcff, + 0xc000fc00,0x1e000,0x0,0x780001,0xe0180000,0xf000000f,0x87c00007,0x80000000,0xfe3,0xe0000000,0x18780c3,0x0,0x7c0f800,0x1e00, + 0xc3,0x80e18000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x1f0,0x3e00000f,0xc0000303,0xe00003f0,0xf00000,0xfffff80, + 0x7ffffc03,0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000, + 0x3c000001,0xe0001e00,0x780f00f,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1f0f801f,0xe00780f0,0x3c0780,0x1e03c00, + 0xf01e000,0x78000780,0x1ffff8,0xf000f8,0x1f000780,0xf8003c07,0xc001e03e,0xf01f0,0x780f80,0x3c1f01e,0xf000,0x1e0000,0xf00000, + 0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00, + 0x1e,0x3c07803c,0x3c01e0,0x1e00f00,0xf007800,0x78007c7c,0x1e0007,0x801f1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c00000,0x303c0003,0x8039e003,0xef000000, + 0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0xfc0,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000, + 0x0,0x7f,0xe01fffff,0xf00ffc00,0x3c000,0x781f00f0,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,0x80007800, + 0x780,0x3c01f000,0x7800001e,0xf078,0x7de01e0,0xf00780,0x7800,0x3c078003,0xc000000f,0xf000,0xf0003c0,0x3e7c001,0xee0ef001, + 0xf01e0000,0x7800003e,0x3c,0x3c,0x1e0,0x0,0x0,0x0,0xf0003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00, + 0xf000,0x781f000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0x7df00003, + 0xc780000f,0x8000003e,0xf0,0x780,0x0,0x0,0x3c00000,0x3fcff800,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x1f00fc,0x1e0,0x1e000001, + 0xe00f0003,0xfcff8003,0xe00000ff,0x3fe007f9,0xff000000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x7c00000,0xf0f0078,0x3fcff,0x8000f800, + 0x1e000,0x0,0x780001,0xe0180000,0xf000001f,0xffe00007,0x8000003c,0x7ff,0xc0000000,0x1c3ffc7,0x0,0x3e07c00,0x1e00,0xe3,0x80738000, + 0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x3e0,0x7c00001d,0xc0000001,0xe0000770,0x1f00000,0xfffff80,0x7ffffc03, + 0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, + 0xe0001e00,0x780f00f,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0x3e07c01f,0xc00780f0,0x3c0780,0x1e03c00,0xf01e000, + 0x78000780,0x1fffc0,0xf0007c,0x1e000780,0xf0003c07,0x8001e03c,0xf01e0,0x780f00,0x3c1e01e,0xf000,0x1e0000,0xf00000,0x7800000, + 0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1e,0x7807803c, + 0x3c01e0,0x1e00f00,0xf007800,0x78003c78,0x1e0007,0x800f1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x83c00000,0x303c0003,0x8039e001,0xee000000,0x1e00,0x3c00, + 0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0x1f80,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,0x0,0x1f,0xfc1fffff, + 0xf07ff000,0x0,0x780f00f0,0x78003c03,0xc000781e,0x1e0,0xf803c0,0x1e00,0x1e000,0x781e0007,0x80007800,0x780,0x3c00f800,0x7800001e, + 0xf078,0x3de01e0,0xf00780,0x7800,0x3c078003,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfe0ff003,0xe01f0000,0x7800007c,0x3c,0x3c, + 0x1e0,0x0,0x0,0x0,0xf0007c01,0xe000f80f,0x800001e0,0xf80f00,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x780f800,0x1e0000f, + 0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003c00,0x1e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0xf8f80003,0xe780001f,0x1e, + 0xf0,0x780,0x0,0x0,0x3c00000,0x1ffff000,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x3bc1de,0x1e0,0xf000001,0xe00f0001,0xffff0007,0xc000007f, + 0xffc003ff,0xfe000000,0x0,0x0,0x0,0xfe000,0x0,0x0,0x0,0x0,0x3c00000,0x1e0f0078,0x1ffff,0x1f000,0x1e000,0x0,0x780000,0xf0180000, + 0xf000001f,0xfff00007,0x8000003c,0x1ff,0x80000000,0xe0ff0e,0x0,0x1f03e00,0x1e00,0x70,0x70000,0x0,0x78,0x0,0x0,0x0,0x3c003c0, + 0xe1c00,0x0,0x0,0x0,0x7c0,0xf8000019,0xc0000000,0xe0000670,0x1e00000,0xf000780,0x78003c03,0xc001e01e,0xf00f0,0x780780,0x3c0f807, + 0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf80f007,0xbc03c001,0xe01e000f, + 0xf00078,0x78003c0,0x3c001e00,0x7c03e00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80, + 0xf0007c07,0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0xf800,0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000, + 0xf0001e00,0x7803c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1f8001f,0xf00f803c,0x3c01e0,0x1e00f00,0xf007800, + 0x78003e78,0x1e000f,0x800f9e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x3c00000,0x303c0003,0x8039f001,0xfe000000,0x1e00,0x3c00,0x0,0x1e0000,0x0,0x0,0x3c,0xf00, + 0x780001e,0x3f00,0x7,0x80000780,0x3e0,0x3e000f00,0x3c0001e,0x3c000,0x7c0000,0x0,0x3,0xfe000000,0xff8000,0x0,0x3c0f81f0,0xf0001e03, + 0xc000780f,0x1e0,0xf003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x780,0x3c007c00,0x7800001e,0xf078,0x3de01e0,0xf00780,0x7800, + 0x3c078001,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfc07f003,0xe00f0000,0x78000078,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01, + 0xf000f007,0x800000f0,0xf80780,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0, + 0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78001,0xe71df000,0xf8f80001,0xef80003e,0x1e,0xf0,0x780,0x0,0x0,0x3c00000, + 0xfffe000,0x0,0x3e000000,0x0,0x18,0x7fff,0xc0000000,0x60c306,0x1e0,0x7800001,0xe00f0000,0xfffe0007,0x8000003f,0xff8001ff, + 0xfc000000,0x0,0x0,0x0,0x7c000,0x0,0x0,0x0,0x0,0x3c00000,0x3c0f0078,0xfffe,0x3e000,0x1e000,0x0,0x780000,0xf0180000,0xf000003c, + 0xfcf80007,0x8000003c,0x7f,0x0,0x70001c,0x0,0xf81f00,0x0,0x38,0xe0000,0x0,0x0,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf81, + 0xf0000039,0xc0000000,0xe0000e70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,0x8000f000,0x78000, + 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f007,0xbc03c001,0xe01e000f,0xf00078,0x78003c0, + 0x3c001e00,0xf801f00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,0x8003e03c, + 0x1f01e0,0xf80f00,0x7c1e01e,0x7800,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00, + 0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xe00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef8,0x1f000f, + 0x7be00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0xf,0x3c00000,0x307c0003,0x8038f000,0xfc000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e00003c,0x780,0xf00001e, + 0x7e00,0xf,0x80000780,0x3c0,0x3e001e00,0x3c0001f,0x7c000,0x780007,0xe000003f,0x0,0xfe000000,0xfe0000,0x0,0x3c07c3f0,0xf0001e03, + 0xc000f80f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x4000f80,0x3c003c00,0x7800001e,0xf078,0x1fe01f0,0x1f00780, + 0x7c00,0x7c078001,0xf000001f,0xf000,0xf0003c0,0x1e78001,0xfc07f007,0xc00f8000,0x780000f8,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01, + 0xf000f007,0xc00000f0,0xf80780,0x3c,0x1f003,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0, + 0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78000,0xfe0fe001,0xf07c0001,0xef00007c,0x1e,0xf0,0x780,0x0,0x0,0x1e00000, + 0x7cfc000,0xfc00000,0x3c00000f,0xc3f00000,0x18,0x7fff,0xc0000000,0x406303,0x3e0,0x3c00001,0xf00f0000,0x7cfc000f,0x8000001f, + 0x3f0000f9,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x780700f8,0x7cfc,0x7c000,0x1e000,0x0,0x780000,0xf8180000, + 0xf0000070,0x3c0007,0x8000003c,0x3f,0x80000000,0x3c0078,0x0,0x780f00,0x0,0x1e,0x3c0000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,0xe1c00, + 0x0,0x0,0x0,0xf01,0xe0000071,0xc0000000,0xe0001c70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007, + 0x8000f800,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00f003,0xfc03e003,0xe01f001f, + 0xf800f8,0x7c007c0,0x3e003e01,0xf000f80f,0xf00f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07, + 0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0x7c00,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00, + 0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xc00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef0, + 0x1f000f,0x7bc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x780000,0xf,0x3800040,0x30780003,0x8038f800,0x78000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078, + 0x780,0x1f00001e,0xfc00,0x20001f,0x780,0x80007c0,0x1f001e00,0x7c0000f,0x78000,0xf80007,0xe000003f,0x0,0x1e000000,0xf00000, + 0x3c000,0x3c03fff0,0xf0001e03,0xc001f007,0x800101e0,0x7e003c0,0x1e00,0x7800,0x781e0007,0x80007800,0x6000f00,0x3c003e00,0x7800001e, + 0xf078,0x1fe00f0,0x1e00780,0x3c00,0x78078000,0xf020001e,0xf000,0x7800780,0xff0001,0xfc07f00f,0x8007c000,0x780001f0,0x3c,0xf, + 0x1e0,0x0,0x0,0x0,0xf800fc01,0xf801f007,0xc00100f8,0x1f807c0,0x40003c,0xf807,0xf0078007,0x80003c00,0xf000,0x7803e00,0x1f0000f, + 0x3c0f01e,0x1e01f0,0x3e007e0,0x7c07c00,0xfc003c00,0x1e,0x3e000,0x3e007c0,0x1ff8000,0xfe0fe003,0xe03e0001,0xff0000fc,0x1e, + 0xf0,0x780,0x0,0x0,0x1f00080,0x3cf8000,0xfc00000,0x3c00001f,0x83f00000,0x18,0xc0,0x0,0xc06203,0x40003c0,0x1c00000,0xf80f0000, + 0x3cf8001f,0xf,0x3e000079,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x700780fc,0x3cf8,0xfc000,0x1e000,0x0,0x780000, + 0x7c180000,0xf0000020,0x100007,0x8000003c,0xf,0x80000000,0x1f01f0,0x0,0x380700,0x0,0xf,0x80f80000,0x0,0x0,0x0,0x0,0x0,0x3e007c0, + 0xe1c00,0x0,0x0,0x0,0xe01,0xc0000071,0xc0000001,0xc0001c70,0x1e00040,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007, + 0x80007800,0x10078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00f003,0xfc01e003,0xc00f001e, + 0x7800f0,0x3c00780,0x1e003c00,0xe000700f,0x800f0078,0x7803c0,0x3c01e00,0x1e00f000,0xf0000780,0x1e0000,0xf0003c,0x1f001f80, + 0xf800fc07,0xc007e03e,0x3f01f0,0x1f80f80,0xfc1e01f,0x7c00,0x100f8000,0x807c0004,0x3e00020,0x1f000100,0x780000,0x3c00000,0x1e000000, + 0xf0000f80,0x1f003c00,0x3c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,0x1f8000f,0x801f003e,0x7c01f0,0x3e00f80,0x1f007c00, + 0xf8001ff0,0x1f801f,0x7fc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0xf,0x7800078,0x31f80001,0xc070fc00,0xfc000000,0x1e00,0x7c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078, + 0x7c0,0x1f00001e,0x1f000,0x38003f,0x780,0xe000f80,0x1f803e00,0x780000f,0x800f8000,0x1f00007,0xe000003f,0x0,0x2000000,0x800000, + 0x3c000,0x3e01ff71,0xf0001f03,0xc007f007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x781e0007,0x80007800,0x7801f00,0x3c001f00,0x7800001e, + 0xf078,0xfe00f8,0x3e00780,0x3e00,0xf8078000,0xf838003e,0xf000,0x7c00f80,0xff0000,0xfc07e00f,0x8003c000,0x780001e0,0x3c,0xf, + 0x1e0,0x0,0x0,0x0,0xf801fc01,0xfc03e003,0xe003007c,0x3f803e0,0x1c0003c,0xfc0f,0xf0078007,0x80003c00,0xf000,0x7801f00,0xf8000f, + 0x3c0f01e,0x1e00f8,0x7c007f0,0xf803e01,0xfc003c00,0x8003e,0x1f000,0x1e00fc0,0xff0000,0xfe0fe007,0xc01f0000,0xfe0000f8,0x1e, + 0xf0,0x780,0x0,0x0,0xf80180,0x1cf0000,0x1f800000,0x3c00001f,0x83e00000,0x18,0xc0,0x0,0xc06203,0x70007c0,0xe00000,0x7e0f0000, + 0x1cf0001e,0x7,0x3c000039,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x7c00000,0xe00780fc,0x2001cf0,0xf8000,0x1e000,0x0, + 0x780000,0x7e182000,0xf0000000,0x7,0x8000003c,0x7,0xc0000000,0x7ffc0,0x0,0x180300,0x0,0x3,0xffe00000,0x0,0x0,0x0,0x0,0x0, + 0x3f00fc0,0xe1c00,0x0,0x0,0x0,0xc01,0x800000e1,0xc0000003,0xc0003870,0x1f001c0,0x3e0003e1,0xf0001f0f,0x8000f87c,0x7c3e0,0x3e1f00, + 0x1f1e007,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e03,0xfc00f001,0xfc01f007, + 0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x4000201f,0xc01f007c,0xf803e0,0x7c01f00,0x3e00f801,0xf0000780,0x1e0000,0xf0007c, + 0x1f003f80,0xf801fc07,0xc00fe03e,0x7f01f0,0x3f80f80,0x1fc1f03f,0x803e00,0x3007c003,0x803e001c,0x1f000e0,0xf800700,0x780000, + 0x3c00000,0x1e000000,0xf00007c0,0x3e003c00,0x3c01f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e001e,0xfc00f0, + 0x7e00780,0x3f003c01,0xf8000fe0,0x1fc03e,0x3f800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,0xfff00001,0xe0f07f03,0xfe000000,0xf00,0x7800,0x0, + 0x1e0000,0xfc0000,0x0,0x7e0000f0,0x3f0,0x7e000fff,0xfc03ffff,0xf83f00fe,0x780,0xfc03f80,0xfc0fc00,0xf800007,0xe03f0018,0x7e00007, + 0xe000003f,0x0,0x0,0x0,0x3c000,0x1e007c71,0xe0000f03,0xffffe003,0xf01f01ff,0xff8003ff,0xffe01e00,0x3f01,0xf81e0007,0x803ffff0, + 0x7e03f00,0x3c000f00,0x7ffffe1e,0xf078,0xfe007e,0xfc00780,0x1f83,0xf0078000,0x783f00fe,0xf000,0x3f03f00,0xff0000,0xfc07e01f, + 0x3e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7e07fc01,0xfe07e001,0xf80f007e,0x7f801f8,0xfc0003c,0x7ffe,0xf0078007, + 0x807ffffe,0xf000,0x7801f00,0xfff00f,0x3c0f01e,0x1e00fc,0xfc007f8,0x1f803f03,0xfc003c00,0xf80fc,0x1fff0,0x1f83fc0,0xff0000, + 0xfc07e007,0xc01f0000,0xfe0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfe0780,0xfe0000,0x1f000000,0x3c00001f,0x7c00e03,0x81c00018, + 0xc0,0x0,0x406203,0x7e01fc0,0x700000,0x7fffff80,0xfe0003f,0xffffc003,0xf800001f,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f0, + 0x1f800001,0xc007c1fe,0x6000fe0,0x1ffffe,0x1e000,0x0,0x780000,0x3f98e03f,0xffff8000,0x7,0x8000003c,0x7,0xc0000000,0xfe00, + 0x0,0x80100,0x0,0x0,0x7f000000,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3f83fe8,0xe1c00,0x0,0x0,0x0,0x801,0xc1,0xc0000007,0x80003070, + 0xfc0fc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc03f01,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003, + 0xffff001f,0xfff800ff,0xffc01fff,0xf800f001,0xfc00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,0x1f,0xf07e003f,0x3f001f8, + 0x1f800fc0,0xfc007e07,0xe0000780,0x1e0000,0xf301f8,0xfc0ff80,0x7e07fc03,0xf03fe01f,0x81ff00fc,0xff807e0,0x7fc0f87f,0x81801f80, + 0xf003f01f,0x801f80fc,0xfc07e0,0x7e03f00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff807e0,0x7e003c00,0x3c01f81f,0x800fc0fc,0x7e07e0, + 0x3f03f00,0x1f81f800,0x1f8000f,0xe07e001f,0x83fc00fc,0x1fe007e0,0xff003f07,0xf8000fe0,0x1fe07e,0x3f800,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f, + 0xffe00000,0xffe03fff,0xdf000000,0xf00,0x7800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0x1ff,0xfc000fff,0xfc03ffff,0xf83ffffc,0x780, + 0xfffff00,0x7fff800,0xf000007,0xffff001f,0xffe00007,0xe000003f,0x0,0x0,0x0,0x3c000,0x1e000001,0xe0000f03,0xffffc001,0xffff01ff, + 0xff0003ff,0xffe01e00,0x1fff,0xf81e0007,0x803ffff0,0x7fffe00,0x3c000f80,0x7ffffe1e,0xf078,0xfe003f,0xff800780,0xfff,0xf0078000, + 0x7c3ffffc,0xf000,0x3ffff00,0xff0000,0xf803e01e,0x1e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7fffbc01,0xffffc000, + 0xffff003f,0xfff800ff,0xffc0003c,0x3ffe,0xf0078007,0x807ffffe,0xf000,0x7800f80,0x7ff00f,0x3c0f01e,0x1e007f,0xff8007ff,0xff001fff, + 0xbc003c00,0xffffc,0x1fff0,0x1fffbc0,0xff0000,0x7c07c00f,0x800f8000,0x7e0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7fff80,0x7c0000, + 0x1f000000,0x3c00001e,0x7c00f07,0xc1e00018,0xc0,0x0,0x60e303,0x7ffff80,0x380000,0x3fffff80,0x7c0003f,0xffffc001,0xf000000f, + 0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff800003,0x8003ffff,0xfe0007c0,0x1ffffe,0x1e000,0x0,0x780000,0x1fffe03f,0xffff8000, + 0x7,0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3fffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x1c1, + 0xc000000f,0x7070,0x7fffc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0, + 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000f001,0xfc007fff,0x3fff8,0x1fffc0,0xfffe00,0x7fff000,0x3b,0xfffc003f, + 0xfff001ff,0xff800fff,0xfc007fff,0xe0000780,0x1e0000,0xf3fff8,0xffff780,0x7fffbc03,0xfffde01f,0xffef00ff,0xff7807ff,0xfbc0ffff, + 0xff800fff,0xf001ffff,0x800ffffc,0x7fffe0,0x3ffff00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff803ff,0xfc003c00,0x3c00ffff,0x7fff8, + 0x3fffc0,0x1fffe00,0xffff000,0x1f,0xfffc001f,0xffbc00ff,0xfde007ff,0xef003fff,0x780007e0,0x1ffffc,0x1f800,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x700003f, + 0xffc00000,0x7fc01fff,0x9f800000,0xf80,0xf800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0xff,0xf8000fff,0xfc03ffff,0xf83ffff8,0x780, + 0xffffe00,0x7fff000,0xf000003,0xfffe001f,0xffc00007,0xe000003f,0x0,0x0,0x0,0x3c000,0xf000003,0xe0000f83,0xffff0000,0xffff01ff, + 0xfc0003ff,0xffe01e00,0xfff,0xf01e0007,0x803ffff0,0x7fffc00,0x3c0007c0,0x7ffffe1e,0xf078,0x7e003f,0xff000780,0x7ff,0xe0078000, + 0x3c3ffff8,0xf000,0x1fffe00,0x7e0000,0xf803e03e,0x1f000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x3fff3c01,0xefff8000, + 0x7ffe001f,0xff78007f,0xff80003c,0x1ffc,0xf0078007,0x807ffffe,0xf000,0x78007c0,0x3ff00f,0x3c0f01e,0x1e003f,0xff0007bf,0xfe000fff, + 0xbc003c00,0xffff8,0xfff0,0xfff3c0,0x7e0000,0x7c07c01f,0x7c000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3fff80,0x380000, + 0x3e000000,0x7c00003e,0x7801f07,0xc1e00018,0xc0,0x0,0x39c1ce,0x7ffff00,0x1c0000,0xfffff80,0x380003f,0xffffc000,0xe0000007, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff000007,0x1ffcf,0xfe000380,0x1ffffe,0x1e000,0x0,0x780000,0xfffe03f,0xffff8000,0x7, + 0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x381, + 0xc000001e,0xe070,0x7fff80,0x7c0001f3,0xe0000f9f,0x7cf8,0x3e7c0,0x1f3e00,0xfbe007,0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0, + 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000f000,0xfc007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x79,0xfff8001f, + 0xffe000ff,0xff0007ff,0xf8003fff,0xc0000780,0x1e0000,0xf3fff0,0x7ffe780,0x3fff3c01,0xfff9e00f,0xffcf007f,0xfe7803ff,0xf3c07ff3, + 0xff8007ff,0xe000ffff,0x7fff8,0x3fffc0,0x1fffe00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff801ff,0xf8003c00,0x3c007ffe,0x3fff0, + 0x1fff80,0xfffc00,0x7ffe000,0x1d,0xfff8000f,0xff3c007f,0xf9e003ff,0xcf001ffe,0x780007c0,0x1efff8,0x1f000,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0xf000003, + 0xfe000000,0x1f000fff,0xfc00000,0x780,0xf000,0x0,0x0,0xf80000,0x0,0x7e0001e0,0x7f,0xf0000fff,0xfc03ffff,0xf81ffff0,0x780, + 0x7fff800,0x1ffe000,0x1f000000,0xfff8001f,0xff000007,0xe000003e,0x0,0x0,0x0,0x3c000,0xf800003,0xc0000783,0xfff80000,0x3ffe01ff, + 0xe00003ff,0xffe01e00,0x7ff,0xc01e0007,0x803ffff0,0x3fff800,0x3c0003c0,0x7ffffe1e,0xf078,0x7e000f,0xfe000780,0x3ff,0xc0078000, + 0x3e1fffe0,0xf000,0x7ff800,0x7e0000,0xf803e07c,0xf800,0x780003ff,0xfffc003c,0x3,0xc00001e0,0x0,0x0,0x0,0xffe3c01,0xe7ff0000, + 0x3ffc000f,0xfe78003f,0xfe00003c,0x7f0,0xf0078007,0x807ffffe,0xf000,0x78003e0,0xff00f,0x3c0f01e,0x1e001f,0xfe00079f,0xfc0007ff, + 0x3c003c00,0x7ffe0,0x1ff0,0x7fe3c0,0x7e0000,0x7c07c03e,0x3e000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfff00,0x100000, + 0x3e000000,0x7800003c,0xf800f07,0xc1e00018,0xc0,0x0,0x1f80fc,0x3fffc00,0xc0000,0x3ffff80,0x100003f,0xffffc000,0x40000002, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xfc000006,0xff87,0xfc000100,0x1ffffe,0x1e000,0x0,0x780000,0x3ffc03f,0xffff8000,0x7, + 0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dff9f8,0xe1c00,0x0,0x0,0x0,0x0,0x3ff, + 0xf800003c,0xfffe,0x1ffe00,0x780000f3,0xc000079e,0x3cf0,0x1e780,0xf3c00,0x7bc007,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0, + 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,0xf000,0xfc001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x70,0xfff00007, + 0xff80003f,0xfc0001ff,0xe0000fff,0x780,0x1e0000,0xf3ffe0,0x1ffc780,0xffe3c00,0x7ff1e003,0xff8f001f,0xfc7800ff,0xe3c03fe1, + 0xff0003ff,0xc0007ffc,0x3ffe0,0x1fff00,0xfff800,0xfffffc07,0xffffe03f,0xffff01ff,0xfff800ff,0xf0003c00,0x3c003ffc,0x1ffe0, + 0xfff00,0x7ff800,0x3ffc000,0x38,0xfff00007,0xfe3c003f,0xf1e001ff,0x8f000ffc,0x780007c0,0x1e7ff0,0x1f000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000, + 0x1fc,0x0,0x780,0xf000,0x0,0x0,0x1f80000,0x0,0x1e0,0x1f,0xc0000000,0x0,0x1ff80,0x0,0xffc000,0x7f8000,0x0,0x3fe00007,0xfc000000, + 0x7e,0x0,0x0,0x0,0x0,0x7c00000,0x0,0x0,0xff00000,0x0,0x0,0xfe,0x0,0x0,0x3fc000,0x0,0x0,0x0,0x3,0xf8000000,0xff,0xc0000000, + 0x1ff00,0x0,0x1fe000,0x0,0x0,0x0,0x0,0x3c,0x3,0xc00001e0,0x0,0x0,0x0,0x3f80000,0x1fc0000,0x7f00003,0xf8000007,0xf0000000, + 0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x7,0xf8000787,0xf00001fc,0x3c000000,0x7f80,0x0,0x1f8000,0x0,0x0,0x0,0x7c000000,0x1e, + 0xf0,0x780,0x0,0x0,0x3fc00,0x0,0x3c000000,0x7800003c,0xf000601,0xc00018,0xc0,0x0,0x0,0x3fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf0000000,0x7e03,0xf0000000,0x0,0x0,0x0,0x0,0xfe0000,0x0,0x0,0x3c,0x2007,0x80000000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c7e0f0,0xe1c00,0x0,0x3800000,0x0,0x0,0x3ff,0xf8000078,0xfffe,0x7f800,0x0,0x0,0x0,0x0, + 0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,0x7f0000,0x70,0x3fc00001,0xfe00000f,0xf000007f, + 0x800003fc,0x0,0x0,0xff00,0x7f0000,0x3f80000,0x1fc00000,0xfe000007,0xf000003f,0x80001f80,0xfc00007f,0xfe0,0x7f00,0x3f800, + 0x1fc000,0x0,0x0,0x0,0x3f,0xc0000000,0xff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x78,0x3fc00001,0xf800000f,0xc000007e,0x3f0,0x7c0, + 0x1e1fc0,0x1f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xe0000000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,0x1e,0xf0,0x780,0x0,0x0,0x0,0x0,0x3c000000,0x78000078,0xf000000,0x18,0xc0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3c0f,0x80000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0x1800000,0x0,0x0,0x3ff,0xf80000f0,0xfffe,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x780,0x1e0000,0x1e000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000, + 0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x1f80000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000000, + 0x1f,0xf0,0xf80,0x0,0x0,0x0,0x0,0x78000000,0xf8000078,0x1e000000,0x8,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3fff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x3c00000,0xe1c00,0x0,0x1c00000,0x0,0x0,0x1,0xc00001e0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x1e0000,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x3c000,0x0,0x0,0x1f00000, + 0x0,0x780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0xfe0100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0xf0007fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000, + 0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x1f,0x800000f0,0x1f80,0x0,0x0,0x0,0x0, + 0x78000000,0xf0000070,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3ffe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000, + 0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0xf00,0x1e0000,0x3c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x7c000,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x7fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4003,0xe0000000,0x0,0x1f000,0x0,0x0, + 0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x1,0xf0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0x70000001,0xf00000e0, + 0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000, + 0x0,0x0,0x3c,0xff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,0x0,0x0,0x1,0xc00003ff, + 0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00,0x1e0000, + 0x7c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0xf0,0x78000,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0, + 0x0,0x0,0x0,0x1fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f, + 0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780f,0xc0000000,0x0,0x3e000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0, + 0x0,0x0,0x0,0x0,0x3,0xe0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0xf0000103,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x21e00000,0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e00,0x1e0000,0xf8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0, + 0xf8,0xf8000,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x1fe00,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x7fff,0xc0000000,0x0,0x3ffe000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0xe0000000,0x7,0xfc0000f0, + 0x3fe00,0x0,0x0,0x0,0x0,0x600001ff,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0, + 0x3fe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x7fe00,0x1e0000,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff,0x80000000,0x0,0x3ffc000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0, + 0x0,0x0,0x0,0x0,0x7f,0xc0000000,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x0,0x0,0x1ff,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3fc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fc00,0x1e0000,0x1ff0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x3ffe,0x0,0x0,0x3ff8000,0x0,0x0,0x0, + 0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0x80000000,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x80000000,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f800,0x1e0000,0x1fe0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8,0x0,0x0,0x3fe0000, + 0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x1e0000,0x1f80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 40x38 'danger' color logo + const unsigned char logo40x38[4576] = { + 177,200,200,200,3,123,123,0,36,200,200,200,1,123,123,0,2,255,255,0,1,189,189,189,1,0,0,0,34,200,200,200, + 1,123,123,0,4,255,255,0,1,189,189,189,1,0,0,0,1,123,123,123,32,200,200,200,1,123,123,0,5,255,255,0,1,0,0, + 0,2,123,123,123,30,200,200,200,1,123,123,0,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,29,200,200,200, + 1,123,123,0,7,255,255,0,1,0,0,0,2,123,123,123,28,200,200,200,1,123,123,0,8,255,255,0,1,189,189,189,1,0,0,0, + 2,123,123,123,27,200,200,200,1,123,123,0,9,255,255,0,1,0,0,0,2,123,123,123,26,200,200,200,1,123,123,0,10,255, + 255,0,1,189,189,189,1,0,0,0,2,123,123,123,25,200,200,200,1,123,123,0,3,255,255,0,1,189,189,189,3,0,0,0,1,189, + 189,189,3,255,255,0,1,0,0,0,2,123,123,123,24,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,3,255,255,0,1,189, + 189,189,1,0,0,0,2,123,123,123,23,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,4,255,255,0,1,0,0,0,2,123,123,123, + 22,200,200,200,1,123,123,0,5,255,255,0,5,0,0,0,4,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,21,200,200,200, + 1,123,123,0,5,255,255,0,5,0,0,0,5,255,255,0,1,0,0,0,2,123,123,123,20,200,200,200,1,123,123,0,6,255,255,0,5,0,0, + 0,5,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,19,200,200,200,1,123,123,0,6,255,255,0,1,123,123,0,3,0,0,0,1, + 123,123,0,6,255,255,0,1,0,0,0,2,123,123,123,18,200,200,200,1,123,123,0,7,255,255,0,1,189,189,189,3,0,0,0,1,189, + 189,189,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,17,200,200,200,1,123,123,0,8,255,255,0,3,0,0,0,8,255,255, + 0,1,0,0,0,2,123,123,123,16,200,200,200,1,123,123,0,9,255,255,0,1,123,123,0,1,0,0,0,1,123,123,0,8,255,255,0,1,189, + 189,189,1,0,0,0,2,123,123,123,15,200,200,200,1,123,123,0,9,255,255,0,1,189,189,189,1,0,0,0,1,189,189,189,9,255,255, + 0,1,0,0,0,2,123,123,123,14,200,200,200,1,123,123,0,11,255,255,0,1,0,0,0,10,255,255,0,1,189,189,189,1,0,0,0,2,123, + 123,123,13,200,200,200,1,123,123,0,23,255,255,0,1,0,0,0,2,123,123,123,12,200,200,200,1,123,123,0,11,255,255,0,1,189, + 189,189,2,0,0,0,1,189,189,189,9,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,11,200,200,200,1,123,123,0,11,255,255, + 0,4,0,0,0,10,255,255,0,1,0,0,0,2,123,123,123,10,200,200,200,1,123,123,0,12,255,255,0,4,0,0,0,10,255,255,0,1,189,189, + 189,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,12,255,255,0,1,189,189,189,2,0,0,0,1,189,189,189,11,255,255,0,1, + 0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,27,255,255,0,1,0,0,0,3,123,123,123,8,200,200,200,1,123,123,0,26,255, + 255,0,1,189,189,189,1,0,0,0,3,123,123,123,9,200,200,200,1,123,123,0,24,255,255,0,1,189,189,189,1,0,0,0,4,123,123, + 123,10,200,200,200,1,123,123,0,24,0,0,0,5,123,123,123,12,200,200,200,27,123,123,123,14,200,200,200,25,123,123,123,86, + 200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0}; + + // Display a warning message if parameter 'cond' is true. + inline void warn(const bool cond, const char *format,...) { + if (cimg::exception_mode()>=1 && cond) { + std::va_list ap; + va_start(ap,format); + std::fprintf(stderr,"\n "); + std::vfprintf(stderr,format,ap); + std::fputc('\n',stderr); + va_end(ap); + } + } + + inline int xln(const int x) { + return x>0?(int)(1+std::log10((double)x)):1; + } + + inline char uncase(const char x) { + return (char)((x<'A'||x>'Z')?x:x-'A'+'a'); + } + + inline float atof(const char *str) { + float x=0,y=1; + if (!str) return 0; else { std::sscanf(str,"%g/%g",&x,&y); return x/y; } + } + + inline int strlen(const char *s) { + if (s) { int k; for (k=0; s[k]; k++) ; return k; } + return -1; + } + + inline int strncmp(const char *s1,const char *s2,const int l) { + if (s1 && s2) { int n=0; for (int k=0; k=0 && s[l]!=c; l--) ; + return l; + } + return -1; + } + + inline const char* basename(const char *s) { + return (cimg_OS!=2)?(s?s+1+cimg::strfind(s,'/'):0):(s?s+1+cimg::strfind(s,'\\'):0); + } + + inline void system(const char *command, const char *module_name=0) { +#if cimg_OS==2 + PROCESS_INFORMATION pi; + STARTUPINFO si; + std::memset(&pi, 0, sizeof(PROCESS_INFORMATION)); + std::memset(&si, 0, sizeof(STARTUPINFO)); + GetStartupInfo(&si); + si.cb = sizeof(si); + si.wShowWindow = SW_HIDE; + si.dwFlags |= SW_HIDE; + const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi); + if (res) { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + } else +#endif + std::system(command); + command=module_name=0; + } + + //! Return path of the ImageMagick's \c convert tool. + /** + If you have installed the ImageMagick package + in a standard directory, this function should return the correct path of the \c convert tool + used by the %CImg Library to load and save compressed image formats. + Conversely, if the \c convert executable is not auto-detected by the function, + you can define the macro \c cimg_imagemagick_path with the correct path + of the \c convert executable, before including CImg.h in your program : + \code + #define cimg_imagemagick_path "/users/thatsme/local/bin/convert" + #include "CImg.h" + + int main() { + CImg<> img("my_image.jpg"); // Read a JPEG image file. + return 0; + } + \endcode + + Note that non compressed image formats can be read without installing ImageMagick. + + \sa temporary_path(), get_load_imagemagick(), load_imagemagick(), save_imagemagick(). + **/ + inline const char* imagemagick_path() { + static char *st_imagemagick_path = 0; + if (!st_imagemagick_path) { + st_imagemagick_path = new char[1024]; + bool path_found = false; + std::FILE *file = 0; +#ifdef cimg_imagemagick_path + std::strcpy(st_imagemagick_path,cimg_imagemagick_path); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } +#endif +#if cimg_OS==2 + if (!path_found) { + std::sprintf(st_imagemagick_path,".\\convert.exe"); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + } + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"C:\\IMAGEM~1.%u-Q\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"C:\\IMAGEM~1.%u\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"C:\\IMAGEM~1.%u-Q\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"C:\\IMAGEM~1.%u\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"C:\\PROGRA~1\\IMAGEM~1.%u-Q\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"C:\\PROGRA~1\\IMAGEM~1.%u\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"C:\\PROGRA~1\\IMAGEM~1.%u-Q\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"C:\\PROGRA~1\\IMAGEM~1.%u\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"D:\\IMAGEM~1.%u-Q\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"D:\\IMAGEM~1.%u\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"D:\\IMAGEM~1.%u-Q\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"D:\\IMAGEM~1.%u\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"D:\\PROGRA~1\\IMAGEM~1.%u-Q\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"D:\\PROGRA~1\\IMAGEM~1.%u\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"D:\\PROGRA~1\\IMAGEM~1.%u-Q\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_imagemagick_path,"D:\\PROGRA~1\\IMAGEM~1.%u\\VISUA~1\\BIN\\convert.exe",k); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + if (!path_found) std::strcpy(st_imagemagick_path,"convert.exe"); +#else + if (!path_found) { + std::sprintf(st_imagemagick_path,"./convert"); + if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_imagemagick_path,"convert"); +#endif + } + return st_imagemagick_path; + } + + //! Return path of the GraphicsMagick's \c gm tool. + /** + If you have installed the GraphicsMagick package + in a standard directory, this function should return the correct path of the \c gm tool + used by the %CImg Library to load and save compressed image formats. + Conversely, if the \c gm executable is not auto-detected by the function, + you can define the macro \c cimg_graphicsmagick_path with the correct path + of the \c gm executable, before including CImg.h in your program : + \code + #define cimg_graphicsmagick_path "/users/thatsme/local/bin/gm" + #include "CImg.h" + + int main() { + CImg<> img("my_image.jpg"); // Read a JPEG image file. + return 0; + } + \endcode + + Note that non compressed image formats can be read without installing ImageMagick. + + \sa temporary_path(), get_load_imagemagick(), load_imagemagick(), save_imagemagick(). + **/ + inline const char* graphicsmagick_path() { + static char *st_graphicsmagick_path = 0; + if (!st_graphicsmagick_path) { + st_graphicsmagick_path = new char[1024]; + bool path_found = false; + std::FILE *file = 0; +#ifdef cimg_graphicsmagick_path + std::strcpy(st_graphicsmagick_path,cimg_graphicsmagick_path); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } +#endif +#if cimg_OS==2 + if (!path_found) { + std::sprintf(st_graphicsmagick_path,".\\gm.exe"); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + } + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"C:\\GRAPHI~1.%u-Q\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"C:\\GRAPHI~1.%u\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"C:\\GRAPHI~1.%u-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"C:\\GRAPHI~1.%u\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"C:\\PROGRA~1\\GRAPHI~1.%u-Q\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"C:\\PROGRA~1\\GRAPHI~1.%u\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"C:\\PROGRA~1\\GRAPHI~1.%u-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"C:\\PROGRA~1\\GRAPHI~1.%u\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"D:\\GRAPHI~1.%u-Q\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"D:\\GRAPHI~1.%u\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"D:\\GRAPHI~1.%u-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"D:\\GRAPHI~1.%u\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"D:\\PROGRA~1\\GRAPHI~1.%u-Q\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"D:\\PROGRA~1\\GRAPHI~1.%u\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"D:\\PROGRA~1\\GRAPHI~1.%u-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + { for (unsigned int k=0; k<=9 && !path_found; k++) { + std::sprintf(st_graphicsmagick_path,"D:\\PROGRA~1\\GRAPHI~1.%u\\VISUA~1\\BIN\\gm.exe",k); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + }} + if (!path_found) std::strcpy(st_graphicsmagick_path,"gm.exe"); +#else + if (!path_found) { + std::sprintf(st_graphicsmagick_path,"./gm"); + if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_graphicsmagick_path,"gm"); +#endif + } + return st_graphicsmagick_path; + } + + //! Return path of the \c XMedcon tool. + /** + If you have installed the XMedcon package + in a standard directory, this function should return the correct path of the \c medcon tool + used by the %CIg Library to load DICOM image formats. + Conversely, if the \c medcon executable is not auto-detected by the function, + you can define the macro \c cimg_medcon_path with the correct path + of the \c medcon executable, before including CImg.h in your program : + \code + #define cimg_medcon_path "/users/thatsme/local/bin/medcon" + #include "CImg.h" + + int main() { + CImg<> img("my_image.dcm"); // Read a DICOM image file. + return 0; + } + \endcode + + Note that \c medcon is only needed if you want to read DICOM image formats. + + \sa temporary_path(), get_load_dicom(), load_dicom(). + **/ + inline const char* medcon_path() { + static char *st_medcon_path = 0; + if (!st_medcon_path) { + st_medcon_path = new char[1024]; + bool path_found = false; + std::FILE *file = 0; +#ifdef cimg_medcon_path + std::strcpy(st_medcon_path,cimg_medcon_path); + if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } +#endif +#if cimg_OS==2 + if (!path_found) { + std::sprintf(st_medcon_path,".\\medcon.bat"); + if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } + } + if (!path_found) { + std::sprintf(st_medcon_path,"C:\\PROGRA~1\\XMedCon\\bin\\medcon.bat"); + if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } + } + if (!path_found) { + std::sprintf(st_medcon_path,"D:\\PROGRA~1\\XMedCon\\bin\\medcon.bat"); + if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_medcon_path,"medcon.bat"); +#else + if (!path_found) { + std::sprintf(st_medcon_path,"./medcon"); + if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(st_medcon_path,"medcon"); +#endif + } + return st_medcon_path; + } + + //! Return path to store temporary files. + /** + If you are running on a standard Unix or Windows system, this function should return a correct path + where temporary files can be stored. If such a path is not auto-detected by this function, + you can define the macro \c cimg_temporary_path with a correct path, before including CImg.h + in your program : + \code + #define cimg_temporary_path "/users/thatsme/tmp" + #include "CImg.h" + + int main() { + CImg<> img("my_image.jpg"); // Read a JPEG image file (using the defined temporay path). + return 0; + } + \endcode + + A temporary path is necessary to load and save compressed image formats, using \c convert + or \c medcon. + + \sa imagemagick_path(), get_load_imagemagick(), load_imagemagick(), save_imagemagick(), get_load_dicom(), load_dicom(). + **/ + inline const char* temporary_path() { + +#define cimg_test_temporary_path(p) \ + if (!path_found) { \ + std::sprintf(st_temporary_path,p); \ + std::sprintf(tmp,"%s%s%s",st_temporary_path,cimg_OS==2?"\\":"/",filetmp); \ + if ((file=std::fopen(tmp,"wb"))!=0) { std::fclose(file); std::remove(tmp); path_found = true; } \ + } + + static char *st_temporary_path = 0; + if (!st_temporary_path) { + st_temporary_path = new char[1024]; + bool path_found = false; + char tmp[1024], filetmp[512]; + std::FILE *file = 0; + std::sprintf(filetmp,"CImg%.4d.tmp",std::rand()%10000); +#ifdef cimg_temporary_path + cimg_test_temporary_path(cimg_temporary_path); +#endif +#if cimg_OS==2 + cimg_test_temporary_path("C:\\WINNT\\Temp"); + cimg_test_temporary_path("C:\\WINDOWS\\Temp"); + cimg_test_temporary_path("C:\\Temp"); + cimg_test_temporary_path("C:"); + cimg_test_temporary_path("D:\\WINNT\\Temp"); + cimg_test_temporary_path("D:\\WINDOWS\\Temp"); + cimg_test_temporary_path("D:\\Temp"); + cimg_test_temporary_path("D:"); +#else + cimg_test_temporary_path("/tmp"); + cimg_test_temporary_path("/var/tmp"); +#endif + if (!path_found) { + st_temporary_path[0]='\0'; + std::strcpy(tmp,filetmp); + if ((file=std::fopen(tmp,"wb"))!=0) { std::fclose(file); std::remove(tmp); path_found = true; } + } + if (!path_found) + throw CImgIOException("cimg::temporary_path() : Unable to find a temporary path accessible for writing\n" + "you have to set the macro 'cimg_temporary_path' to a valid path where you have writing access :\n" + "#define cimg_temporary_path \"path\" (before including 'CImg.h')"); + } + return st_temporary_path; + } + + inline const char *filename_split(const char *const filename, char *const body=0) { + if (!filename) { if (body) body[0]='\0'; return 0; } + int l = cimg::strfind(filename,'.'); + if (l>=0) { if (body) { std::strncpy(body,filename,l); body[l]='\0'; }} + else { if (body) std::strcpy(body,filename); l=(int)std::strlen(filename)-1; } + return filename+l+1; + } + + inline char* filename_number(const char *const filename, const int number, const unsigned int n, char *const string) { + if (!filename) { if (string) string[0]='\0'; return 0; } + char format[1024],body[1024]; + const char *ext = cimg::filename_split(filename,body); + if (n>0) std::sprintf(format,"%s_%%.%ud.%s",body,n,ext); + else std::sprintf(format,"%s_%%d.%s",body,ext); + std::sprintf(string,format,number); + return string; + } + + inline std::FILE *fopen(const char *const path,const char *const mode) { + if(!path || !mode) + throw CImgArgumentException("cimg::fopen() : File '%s' cannot be opened with mode '%s'.", + path?path:"(null)",mode?mode:"(null)"); + if (path[0]=='-') return (mode[0]=='r')?stdin:stdout; + std::FILE *dest = std::fopen(path,mode); + if (!dest) + throw CImgIOException("cimg::fopen() : File '%s' cannot be opened%s", + path,mode[0]=='r'?" for reading.":(mode[0]=='w'?" for writing.":"."),path); + return dest; + } + + inline int fclose(std::FILE *file) { + warn(!file,"cimg::fclose() : Can't close (null) file"); + if (!file || file==stdin || file==stdout) return 0; + const int errn=std::fclose(file); + warn(errn!=0,"cimg::fclose() : Error %d during file closing",errn); + return errn; + } + + template inline int fread(T *const ptr, const unsigned int nmemb, std::FILE *stream) { + if (!ptr || nmemb<=0 || !stream) + throw CImgArgumentException("cimg::fread() : Can't read %u x %u bytes of file pointer '%p' in buffer '%p'", + nmemb,sizeof(T),stream,ptr); + const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + unsigned int toread=nmemb, alread=0, ltoread=0, lalread=0; + do { + ltoread = (toread*sizeof(T))0); + cimg::warn(toread>0,"cimg::fread() : File reading problems, only %u/%u elements read",alread,nmemb); + return alread; + } + + template inline int fwrite(const T *ptr, const unsigned int nmemb, std::FILE *stream) { + if (!ptr || !stream) + throw CImgArgumentException("cimg::fwrite() : Can't write %u x %u bytes of file pointer '%p' from buffer '%p'", + nmemb,sizeof(T),stream,ptr); + if (nmemb<=0) return 0; + const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + unsigned int towrite=nmemb, alwrite=0, ltowrite=0, lalwrite=0; + do { + ltowrite = (towrite*sizeof(T))0); + cimg::warn(towrite>0,"cimg::fwrite() : File writing problems, only %u/%u elements written",alwrite,nmemb); + return alwrite; + } + + // Exchange the values of variables \p a and \p b + template inline void swap(T& a,T& b) { T t=a; a=b; b=t; } + template inline void swap(T1& a1,T1& b1,T2& a2,T2& b2) { + cimg::swap(a1,b1); cimg::swap(a2,b2); + } + template inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3) { + cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3); + } + template + inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3,T4& a4,T4& b4) { + cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4); + } + template + inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3,T4& a4,T4& b4,T5& a5,T5& b5) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5); + } + template + inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3,T4& a4,T4& b4,T5& a5,T5& b5,T6& a6,T6& b6) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6); + } + + template inline void endian_swap(T* const buffer, const unsigned int size) { + switch (sizeof(T)) { + case 1: break; + case 2: { + for (unsigned short *ptr = (unsigned short*)buffer+size; ptr>(unsigned short*)buffer;) { + const unsigned short val = *(--ptr); + *ptr = (val>>8)|((val<<8)); + } + } break; + case 4: { + for (unsigned int *ptr = (unsigned int*)buffer+size; ptr>(unsigned int*)buffer;) { + const unsigned int val = *(--ptr); + *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24); + } + } break; + default: { + for (T* ptr = buffer+size; ptr>buffer; --ptr) { + unsigned char *pb=(unsigned char*)(--ptr), *pe=pb+sizeof(T); + for (int i=0; i<(int)sizeof(T)/2; i++) cimg::swap(*(pb++),*(--pe)); + } break; + } + } + } + template inline T& endian_swap(T& a) { endian_swap(&a,1); return a; } + + inline const char* option(const char *const name, const int argc, char **argv, + const char *defaut, const char *const usage=0) { + static bool first=true, visu=false; + const char *res = 0; + if (first) { + first=false; + visu = (cimg::option("-h",argc,argv,(char*)0)!=0); + visu |= (cimg::option("-help",argc,argv,(char*)0)!=0); + visu |= (cimg::option("--help",argc,argv,(char*)0)!=0); + } + if (!name && visu) { + if (usage) { + std::fprintf(stderr,"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal); + std::fprintf(stderr," : %s",usage); + std::fprintf(stderr," (%s, %s)\n\n",__DATE__,__TIME__); + } + if (defaut) std::fprintf(stderr,"%s\n",defaut); + } + if (name) { + if (argc>0) { + int k=0,i; + while (k CPU endianness : %s%s Endian%s\n", + cimg::t_bold, + cimg::endian()?"Big":"Little", + cimg::t_normal); + + std::fprintf(stderr," > Operating System : %s%-13s%s %s('cimg_OS'=%d)%s\n", + cimg::t_bold, + cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"), + cimg::t_normal,cimg::t_purple, + cimg_OS, + cimg::t_normal); + +#ifdef cimg_use_visualcpp6 + std::fprintf(stderr," > Using Visual C++ 6.0 : %s%-13s%s %s('cimg_use_visual_cpp6' defined)%s\n", + cimg::t_bold,"Yes",cimg::t_normal,cimg::t_purple,cimg::t_normal); +#endif + + std::fprintf(stderr," > Display type : %s%-13s%s %s('cimg_display_type'=%d)%s\n", + cimg::t_bold, + cimg_display_type==0?"No display":(cimg_display_type==1?"X11":(cimg_display_type==2?"Windows GDI":"Unknow")), + cimg::t_normal,cimg::t_purple, + cimg_display_type, + cimg::t_normal); + + std::fprintf(stderr," > Color terminal : %s%-13s%s %s('cimg_color_terminal' %s)%s\n", + cimg::t_bold, +#ifdef cimg_color_terminal + "Yes",cimg::t_normal,cimg::t_purple,"defined", +#else + "No",cimg::t_normal,cimg::t_purple,"undefined", +#endif + cimg::t_normal); + + std::fprintf(stderr," > Debug messages : %s%-13s%s %s('cimg_debug'=%d)%s\n", + cimg::t_bold, + cimg_debug==0?"No":(cimg_debug==1 || cimg_debug==2?"Yes":(cimg_debug==3?"Yes+":"Unknown")), + cimg::t_normal,cimg::t_purple, + cimg_debug, + cimg::t_normal); + +#if cimg_display_type==1 + std::fprintf(stderr," > Using XShm for X11 : %s%-13s%s %s('cimg_use_xshm' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xshm + "Yes",cimg::t_normal,cimg::t_purple,"defined", +#else + "No",cimg::t_normal,cimg::t_purple,"undefined", +#endif + cimg::t_normal); + + std::fprintf(stderr," > Using XRand for X11 : %s%-13s%s %s('cimg_use_xrandr' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xrandr + "Yes",cimg::t_normal,cimg::t_purple,"defined", +#else + "No",cimg::t_normal,cimg::t_purple,"undefined", +#endif + cimg::t_normal); +#endif + + std::fprintf(stderr," > Using PNG library : %s%-13s%s %s('cimg_use_png' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_png + "Yes",cimg::t_normal,cimg::t_purple,"defined", +#else + "No",cimg::t_normal,cimg::t_purple,"undefined", +#endif + cimg::t_normal); + std::fprintf(stderr," > Using JPEG library : %s%-13s%s %s('cimg_use_jpeg' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_jpeg + "Yes",cimg::t_normal,cimg::t_purple,"defined", +#else + "No",cimg::t_normal,cimg::t_purple,"undefined", +#endif + cimg::t_normal); + + std::fprintf(stderr," > Using TIFF library : %s%-13s%s %s('cimg_use_tiff' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_tiff + "Yes",cimg::t_normal,cimg::t_purple,"defined", +#else + "No",cimg::t_normal,cimg::t_purple,"undefined", +#endif + cimg::t_normal); + + std::fprintf(stderr," > Using Magick++ library : %s%-13s%s %s('cimg_use_magick' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_magick + "Yes",cimg::t_normal,cimg::t_purple,"defined", +#else + "No",cimg::t_normal,cimg::t_purple,"undefined", +#endif + cimg::t_normal); + + std::fprintf(stderr," > Using FFTW3 library : %s%-13s%s %s('cimg_use_fftw3' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_fftw3 + "Yes",cimg::t_normal,cimg::t_purple,"defined", +#else + "No",cimg::t_normal,cimg::t_purple,"undefined", +#endif + cimg::t_normal); + + std::sprintf(tmp,"\"%.1020s\"",cimg::imagemagick_path()); + std::fprintf(stderr," > Path of ImageMagick : %s%-13s%s %s('cimg_imagemagick_path'%s)%s\n", + cimg::t_bold, + tmp, + cimg::t_normal, +#ifdef cimg_imagemagick_path + cimg::t_purple,"=\""cimg_imagemagick_path"\"", +#else + cimg::t_purple," undefined", +#endif + cimg::t_normal); + + std::sprintf(tmp,"\"%.1020s\"",cimg::graphicsmagick_path()); + std::fprintf(stderr," > Path of GraphicsMagick : %s%-13s%s %s('cimg_graphicsmagick_path'%s)%s\n", + cimg::t_bold, + tmp, + cimg::t_normal, +#ifdef cimg_graphicsmagick_path + cimg::t_purple,"=\""cimg_graphicsmagick_path"\"", +#else + cimg::t_purple," undefined", +#endif + cimg::t_normal); + + std::sprintf(tmp,"\"%.1020s\"",cimg::medcon_path()); + std::fprintf(stderr," > Path of 'medcon' : %s%-13s%s %s('cimg_medcon_path'%s)%s\n", + cimg::t_bold, + tmp, + cimg::t_normal, +#ifdef cimg_medcon_path + cimg::t_purple,"=\""cimg_medcon_path"\"", +#else + cimg::t_purple," undefined", +#endif + cimg::t_normal); + + std::sprintf(tmp,"\"%.1020s\"",cimg::temporary_path()); + std::fprintf(stderr," > Temporary path : %s%-13s%s %s('cimg_temporary_path'%s)%s\n", + cimg::t_bold, + tmp, + cimg::t_normal, +#ifdef cimg_temporary_path + cimg::t_purple,"=\""cimg_temporary_path"\"", +#else + cimg::t_purple," undefined", +#endif + cimg::t_normal); + + std::fprintf(stderr,"\n"); + } + + //! Get the value of a system timer with a millisecond precision. + inline unsigned long time() { +#if cimg_OS==1 + struct timeval st_time; + gettimeofday(&st_time,0); + return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000); +#elif cimg_OS==2 + static SYSTEMTIME st_time; + GetSystemTime(&st_time); + return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour))); +#else + return 0; +#endif + } + + //! Sleep for a certain numbers of milliseconds. + /** + This function frees the CPU ressources during the sleeping time. + It may be used to temporize your program properly, without wasting CPU time. + \sa wait(), time(). + **/ + inline void sleep(const unsigned int milliseconds) { +#if cimg_OS==1 + struct timespec tv; + tv.tv_sec = milliseconds/1000; + tv.tv_nsec = (milliseconds%1000)*1000000; + nanosleep(&tv,0); +#elif cimg_OS==2 + Sleep(milliseconds); +#endif + } + + inline unsigned int wait(const unsigned int milliseconds, unsigned long& timer) { + if (!timer) timer = cimg::time(); + const unsigned long current_time = cimg::time(); + if (current_time>=timer+milliseconds) { timer = current_time; return 0; } + const unsigned long time_diff = timer + milliseconds - current_time; + timer = current_time + time_diff; + cimg::sleep(time_diff); + return (unsigned int)time_diff; + } + + //! Wait for a certain number of milliseconds since the last call. + /** + This function is equivalent to sleep() but the waiting time is computed with regard to the last call + of wait(). It may be used to temporize your program properly. + \sa sleep(), time(). + **/ + inline unsigned int wait(const unsigned int milliseconds) { + static unsigned long timer = 0; + if (!timer) timer = cimg::time(); + return wait(milliseconds,timer); + } + + template inline const T rol(const T& a, const unsigned int n=1) { + return (T)((a<>((sizeof(T)<<3)-n))); + } + + template inline const T ror(const T& a, const unsigned int n=1) { + return (T)((a>>n)|(a<<((sizeof(T)<<3)-n))); + } + + //! Return the absolute value of \p a + template inline T abs(const T a) { return a>=0?a:-a; } + inline bool abs(const bool a) { return a; } + inline unsigned char abs(const unsigned char a) { return a; } + inline unsigned short abs(const unsigned short a) { return a; } + inline unsigned int abs(const unsigned int a) { return a; } + inline unsigned long abs(const unsigned long a) { return a; } + inline double abs(const double a) { return std::fabs(a); } + inline float abs(const float a) { return (float)std::fabs((double)a); } + inline int abs(const int a) { return std::abs(a); } + + //! Return the minimum between \p a and \p b. + template inline const T min(const T a,const T b) { return a<=b?a:b; } + + //! Return the minimum between \p a,\p b and \a c. + template inline const T min(const T a,const T b,const T c) { return cimg::min(cimg::min(a,b),c); } + + //! Return the minimum between \p a,\p b,\p c and \p d. + template inline const T min(const T a,const T b,const T c,const T d) { return cimg::min(cimg::min(a,b,c),d); } + + //! Return the maximum between \p a and \p b. + template inline const T max(const T a,const T b) { return a>=b?a:b; } + + //! Return the maximum between \p a,\p b and \p c. + template inline const T max(const T a,const T b,const T c) { return cimg::max(cimg::max(a,b),c); } + + //! Return the maximum between \p a,\p b,\p c and \p d. + template inline const T max(const T a,const T b,const T c,const T d) { return cimg::max(cimg::max(a,b,c),d); } + + //! Return the sign of \p x. + template inline T sign(const T x) { return (x<0)?(T)(-1):(x==0?(T)0:(T)1); } + + //! Return the nearest power of 2 higher than \p x. + template inline unsigned long nearest_pow2(const T& x) { + unsigned long i=1; + while (x>i) i<<=1; + return i; + } + + //! Return \p x modulo \p m (generic modulo). + /** + This modulo function accepts negative and floating-points modulo numbers \p m. + **/ + inline double mod(const double x, const double m) { return x-m*std::floor(x/m); } + inline float mod(const float x, const float m) { return (float)(x-m*std::floor((double)x/m)); } + inline int mod(const int x, const int m) { return x>=0?x%m:(x%m?m+x%m:0); } + + //! Return minmod(\p a,\p b). + /** + The operator minmod(\p a,\p b) is defined to be : + - minmod(\p a,\p b) = min(\p a,\p b), if (\p a * \p b)>0. + - minmod(\p a,\p b) = 0, if (\p a * \p b)<=0 + **/ + template inline T minmod(const T& a,const T& b) { return a*b<=0?0:(a>0?(aabsb) { const double tmp = absb/absa; return absa*std::sqrt(1.0+tmp*tmp); } + else { const double tmp = absa/absb; return (absb==0?0:absb*std::sqrt(1.0+tmp*tmp)); } + } + + // End of the 'cimg' namespace + } + + /* + #---------------------------------------- + # + # + # + # Definition of the CImgStats structure + # + # + # + #---------------------------------------- + */ + //! Class used to compute basic statistics on pixel values of a \ref CImg image. + /** + Constructing a CImgStats instance from an image CImg or a list CImgList + will compute the minimum, maximum and average pixel values of the input object. + Optionally, the variance of the pixel values can be computed. + Coordinates of the pixels whose values are minimum and maximum are also stored. + The example below shows how to use CImgStats objects to retrieve simple statistics of an image : + \code + const CImg img("my_image.jpg"); // Read JPEG image file. + const CImgStats stats(img); // Compute basic statistics on the image. + stats.print("My statistics"); // Display statistics. + std::printf("Max-Min = %lf",stats.max-stats.min); // Compute the difference between extremum values. + \endcode + + Note that statistics are computed by considering the set of \a scalar values of the image pixels. + No vector-valued statistics are computed. + **/ + struct CImgStats { + double min; //!< Minimum of the pixel values. + double max; //!< Maximum of the pixel values. + double mean; //!< Mean of the pixel values. + double variance; //!< Variance of the pixel values. + int xmin; //!< X-coordinate of the pixel with minimum value. + int ymin; //!< Y-coordinate of the pixel with minimum value. + int zmin; //!< Z-coordinate of the pixel with minimum value. + int vmin; //!< V-coordinate of the pixel with minimum value. + int lmin; //!< Image number (for a list) containing the minimum pixel. + int xmax; //!< X-coordinate of the pixel with maximum value. + int ymax; //!< Y-coordinate of the pixel with maximum value. + int zmax; //!< Z-coordinate of the pixel with maximum value. + int vmax; //!< V-coordinate of the pixel with maximum value. + int lmax; //!< Image number (for a list) containing the maximum pixel. + +#ifdef cimgstats_plugin +#include cimgstats_plugin +#endif + + //! Default constructor. + CImgStats():min(0),max(0),mean(0),variance(0),xmin(-1),ymin(-1),zmin(-1),vmin(-1),lmin(-1), + xmax(-1),ymax(-1),zmax(-1),vmax(-1),lmax(-1) {} + + //! In-place version of the default constructor + CImgStats& assign() { + min = max = mean = variance = 0; + xmin = ymin = zmin = vmin = lmin = xmax = ymax = zmax = vmax = lmax = -1; + return *this; + } + + //! Copy constructor. + CImgStats(const CImgStats& stats) { + assign(stats); + }; + + //! In-place version of the copy constructor. + CImgStats& assign(const CImgStats& stats) { + min = stats.min; + max = stats.max; + mean = stats.mean; + variance = stats.variance; + xmin = stats.xmin; ymin = stats.ymin; zmin = stats.zmin; vmin = stats.vmin; lmin = stats.lmin; + xmax = stats.xmax; ymax = stats.ymax; zmax = stats.zmax; vmax = stats.vmax; lmax = stats.lmax; + return *this; + } + + //! Constructor that computes statistics of an input image \p img. + /** + \param img The input image. + \param compute_variance If true, the \c variance field is computed, else it is set to 0. + **/ + template CImgStats(const CImg& img, const bool compute_variance=true) { + assign(img,compute_variance); + } + + //! In-place version of the previous constructor. + template CImgStats& assign(const CImg& img, const bool compute_variance=true) { + if (img.is_empty()) + throw CImgArgumentException("CImgStats::CImgStats() : Specified input image (%u,%u,%u,%u,%p) is empty.", + img.width,img.height,img.depth,img.dim,img.data); + mean = variance = 0; + lmin = lmax = -1; + T pmin=img[0], pmax=pmin, *ptrmin=img.data, *ptrmax=ptrmin; + cimg_for(img,ptr,T) { + const T& a=*ptr; + mean+=(double)a; + if (apmax) { pmax=a; ptrmax = ptr; } + } + mean/=img.size(); + min=(double)pmin; + max=(double)pmax; + unsigned long offmin = (unsigned long)(ptrmin-img.data), offmax = (unsigned long)(ptrmax-img.data); + const unsigned long whz = img.width*img.height*img.depth, wh = img.width*img.height; + vmin = offmin/whz; offmin%=whz; zmin = offmin/wh; offmin%=wh; ymin = offmin/img.width; xmin = offmin%img.width; + vmax = offmax/whz; offmax%=whz; zmax = offmax/wh; offmax%=wh; ymax = offmax/img.width; xmax = offmax%img.width; + if (compute_variance) { + cimg_for(img,ptr,T) { const double tmpf=(*ptr)-mean; variance+=tmpf*tmpf; } + const unsigned int siz = img.size(); + if (siz>1) variance/=(siz-1); else variance=0; + } + return *this; + } + + //! Constructor that computes statistics of an input image list \p list. + /** + \param list The input list of images. + \param compute_variance If true, the \c variance field is computed, else it is set to 0. + **/ + template CImgStats(const CImgList& list, const bool compute_variance=true) { + assign(list,compute_variance); + } + + //! In-place version of the previous constructor. + template CImgStats& assign(const CImgList& list,const bool compute_variance=true) { + if (list.is_empty()) + throw CImgArgumentException("CImgStats::CImgStats() : Specified input list (%u,%p) is empty.", + list.size,list.data); + mean = variance = lmin = lmax = 0; + T pmin = list[0][0], pmax = pmin, *ptrmin = list[0].data, *ptrmax = ptrmin; + int psize = 0; + cimglist_for(list,l) { + cimg_for(list[l],ptr,T) { + const T& a=*ptr; + mean+=(double)a; + if (apmax) { pmax=a; ptrmax = ptr; lmax = l; } + } + psize+=list[l].size(); + } + mean/=psize; + min=(double)pmin; + max=(double)pmax; + const CImg &imin = list[lmin], &imax = list[lmax]; + unsigned long offmin = (ptrmin-imin.data), offmax = (ptrmax-imax.data); + const unsigned long whz1 = imin.width*imin.height*imin.depth, wh1 = imin.width*imin.height; + vmin = offmin/whz1; offmin%=whz1; zmin = offmin/wh1; offmin%=wh1; ymin = offmin/imin.width; xmin = offmin%imin.width; + const unsigned long whz2 = imax.width*imax.height*imax.depth, wh2 = imax.width*imax.height; + vmax = offmax/whz2; offmax%=whz2; zmax = offmax/wh2; offmax%=wh2; ymax = offmax/imax.width; xmax = offmax%imax.width; + if (compute_variance) { + cimglist_for(list,l) cimg_for(list[l],ptr,T) { const double tmpf=(*ptr)-mean; variance+=tmpf*tmpf; } + if (psize>1) variance/=(psize-1); else variance=0; + } + return *this; + } + + //! Assignement operator. + CImgStats& operator=(const CImgStats& stats) { + return assign(stats); + } + + //! Return true if the current instance contains valid statistics + bool is_empty() const { + return (xmin>=0 && ymin>=0 && zmin>=0 && vmin>=0 && xmax>=0 && ymax>=0 && zmax>=0 && vmax>=0); + } + + //! Print the current statistics. + /** + Printing is done on the standart error output. + **/ + const CImgStats& print(const char* title=0) const { + if (lmin>=0 && lmax>=0) + std::fprintf(stderr,"%-8s(this=%p) : { min=%g, mean=%g [var=%g], max=%g, " + "pmin=[%d](%d,%d,%d,%d), pmax=[%d](%d,%d,%d,%d) }\n", + title?title:"CImgStats",(void*)this,min,mean,variance,max, + lmin,xmin,ymin,zmin,vmin,lmax,xmax,ymax,zmax,vmax); + else + std::fprintf(stderr,"%-8s(this=%p) : { min=%g, mean=%g [var=%g], max=%g, " + "pmin=(%d,%d,%d,%d), pmax=(%d,%d,%d,%d) }\n", + title?title:"CImgStats",(void*)this,min,mean,variance,max, + xmin,ymin,zmin,vmin,xmax,ymax,zmax,vmax); + return *this; + } + + }; + + /* + #------------------------------------------- + # + # + # + # Definition of the CImgDisplay structure + # + # + # + #------------------------------------------- + */ + + //! This class represents a window which can display \ref CImg images and handles mouse and keyboard events. + /** + Creating a \c CImgDisplay instance opens a window that can be used to display a \c CImg image + of a \c CImgList image list inside. When a display is created, associated window events + (such as mouse motion, keyboard and window size changes) are handled and can be easily + detected by testing specific \c CImgDisplay data fields. + See \ref cimg_displays for a complete tutorial on using the \c CImgDisplay class. + **/ + + struct CImgDisplay { + + //! Width of the display + unsigned int width; + + //! Height of the display + unsigned int height; + + //! Normalization type used for the display + unsigned int normalization; + + //! Range of events detected by the display + unsigned int events; + + //! Fullscreen state of the display + bool is_fullscreen; + + //! Display title + char* title; + + //! X-pos of the display on the screen + volatile int window_x; + + //! Y-pos of the display on the screen + volatile int window_y; + + //! Width of the underlying window + volatile unsigned int window_width; + + //! Height of the underlying window + volatile unsigned int window_height; + + //! X-coordinate of the mouse pointer on the display + volatile int mouse_x; + + //! Y-coordinate of the mouse pointer on the display + volatile int mouse_y; + + //! Button state of the mouse + volatile unsigned int buttons[256]; + volatile unsigned int& button; + + //! Wheel state of the mouse + volatile int wheel; + + //! Key value if pressed + volatile unsigned int& key; + volatile unsigned int keys[256]; + + //! Key value if released + volatile unsigned int& released_key; + volatile unsigned int released_keys[256]; + + //! Closed state of the window + volatile bool is_closed; + + //! Resized state of the window + volatile bool is_resized; + + //! Moved state of the window + volatile bool is_moved; + + //! Event state of the window + volatile bool is_event; + + float fps_fps, min, max; + unsigned long timer, fps_frames, fps_timer; + +#ifdef cimgdisplay_plugin +#include cimgdisplay_plugin +#endif + + //! Create a display window with a specified size \p pwidth x \p height. + /** \param dimw : Width of the display window. + \param dimh : Height of the display window. + \param title : Title of the display window. + \param normalization_type : Normalization type of the display window (see CImgDisplay::normalize). + \param events_type : Type of events handled by the display window. + \param fullscreen_flag : Fullscreen mode. + \param closed_flag : Initially visible mode. + A black image will be initially displayed in the display window. + **/ + CImgDisplay(const unsigned int dimw, const unsigned int dimh, const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false): + width(0),height(0),normalization(0),events(0),is_fullscreen(false),title(0), + window_x(0),window_y(0),window_width(0),window_height(0), + mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), + is_closed(true),is_resized(false),is_moved(false),is_event(false), + min(0),max(0) { + assign(dimw,dimh,title,normalization_type,events_type,fullscreen_flag,closed_flag); + } + + //! Create a display window from an image. + /** \param img : Image that will be used to create the display window. + \param title : Title of the display window + \param normalization_type : Normalization type of the display window. + \param events_type : Type of events handled by the display window. + \param fullscreen_flag : Fullscreen mode. + \param closed_flag : Initially visible mode. + **/ + template + CImgDisplay(const CImg& img, const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false): + width(0),height(0),normalization(0),events(0),is_fullscreen(false),title(0), + window_x(0),window_y(0),window_width(0),window_height(0), + mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), + is_closed(true),is_resized(false),is_moved(false),is_event(false),min(0),max(0) { + assign(img,title,normalization_type,events_type,fullscreen_flag,closed_flag); + } + + //! Create a display window from an image list. + /** \param list : The list of images to display. + \param title : Title of the display window + \param normalization_type : Normalization type of the display window. + \param events_type : Type of events handled by the display window. + \param fullscreen_flag : Fullscreen mode. + \param closed_flag : Initially visible mode. + **/ + template + CImgDisplay(const CImgList& list,const char *title=0, + const unsigned int normalization_type=3,const unsigned int events_type=3, + const bool fullscreen_flag=false,const bool closed_flag=false): + width(0),height(0),normalization(0),events(0),is_fullscreen(false),title(0), + window_x(0),window_y(0),window_width(0),window_height(0), + mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), + is_closed(true),is_resized(false),is_moved(false),is_event(false),min(0),max(0) { + assign(list,title,normalization_type,events_type,fullscreen_flag,closed_flag); + } + + //! Create a display window by copying another one. + /** \param win : Display window to copy. + \param title : Title of the new display window. + **/ + CImgDisplay(const CImgDisplay& disp): + width(0),height(0),normalization(0),events(0),is_fullscreen(false),title(0), + window_x(0),window_y(0),window_width(0),window_height(0), + mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), + is_closed(true),is_resized(false),is_moved(false),is_event(false),min(0),max(0) { + assign(disp); + } + + //! Destructor + ~CImgDisplay() { + _assign(); + } + + //! Assignement operator + CImgDisplay& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return display width + int dimx() const { + return (int)width; + } + + //! Return display height + int dimy() const { + return (int)height; + } + + //! Return display window width + int window_dimx() const { + return (int)window_width; + } + + //! Return display window height + int window_dimy() const { + return (int)window_height; + } + + //! Return X-coordinate of the window + int window_posx() const { + return window_x; + } + + //! Return Y-coordinate of the window + int window_posy() const { + return window_y; + } + + //! Synchronized waiting function. Same as cimg::wait(). + /** \see cimg::wait() + **/ + CImgDisplay& wait(const unsigned int milliseconds) { + cimg::wait(milliseconds, timer); + return *this; + } + + //! Wait for an event occuring on the current display + CImgDisplay& wait() { + wait(*this); + return *this; + } + + //! Wait for any event occuring on the display \c disp1 + static void wait(CImgDisplay& disp1) { + disp1.is_event = 0; + while (!disp1.is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1 or \c disp2 + static void wait(CImgDisplay& disp1, CImgDisplay& disp2) { + disp1.is_event = disp2.is_event = 0; + while (!disp1.is_event && !disp2.is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3 + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) { + disp1.is_event = disp2.is_event = disp3.is_event = 0; + while (!disp1.is_event && !disp2.is_event && !disp3.is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4 + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) { + disp1.is_event = disp2.is_event = disp3.is_event = disp4.is_event = 0; + while (!disp1.is_event && !disp2.is_event && !disp3.is_event && !disp4.is_event) wait_all(); + } + + //! Return the frame per second rate + float frames_per_second() { + if (!fps_timer) fps_timer = cimg::time(); + const float delta = (cimg::time()-fps_timer)/1000.0f; + fps_frames++; + if (delta>=1.0f) { + fps_fps = fps_frames/delta; + fps_frames = 0; + fps_timer = cimg::time(); + } + return fps_fps; + } + + //! Display an image list CImgList into a display window. + /** First, all images of the list are appended into a single image used for visualization, + then this image is displayed in the current display window. + \param list : The list of images to display. + \param axe : The axe used to append the image for visualization. Can be 'x' (default),'y','z' or 'v'. + \param align : Defines the relative alignment of images when displaying images of different sizes. + Can be '\p c' (centered, which is the default), '\p p' (top alignment) and '\p n' (bottom aligment). + + \see CImg::get_append() + **/ + template CImgDisplay& display(const CImgList& list,const char axe='x',const char align='c') { + return display(list.get_append(axe,align)); + } + + //! Display an image CImg into a display window. + template CImgDisplay& operator<<(const CImg& img) { + return display(img); + } + + //! Display an image CImg into a display window. + template CImgDisplay& operator<<(const CImgList& list) { + return display(list); + } + + //! Resize a display window with the size of an image. + /** \param img : Input image. \p image.width and \p image.height give the new dimensions of the display window. + \param redraw : If \p true (default), the current displayed image in the display window will + be bloc-interpolated to fit the new dimensions. If \p false, a black image will be drawn in the resized window. + \see CImgDisplay::is_resized, CImgDisplay::resizedimx(), CImgDisplay::resizedimy() + **/ + template CImgDisplay& resize(const CImg& img, const bool redraw=true) { + return resize(img.width,img.height,redraw); + } + + //! Resize a display window using the size of the given display \p disp + CImgDisplay& resize(const CImgDisplay& disp, const bool redraw=true) { + return resize(disp.width,disp.height,redraw); + } + + //! Resize a display window in its current size. + CImgDisplay& resize(const bool redraw=true) { + resize(window_width,window_height,redraw); + return *this; + } + + //! Display a 3d object + template + CImgDisplay& display_object3d(const tp& points, const CImgList& primitives, + const CImgList& colors, const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, + const float focale=500.0f, const float ambiant_light=0.05f, + const bool display_axes = true, float *const pose_matrix=0) { + CImg(width,height,1,3,0).display_object3d(points,primitives,colors,opacities,*this, + centering,render_static,render_motion, + double_sided,focale,ambiant_light,display_axes,pose_matrix); + return *this; + } + + //! Display a 3D object. + template + CImgDisplay& display_object3d(const tp& points, const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, + const float focale=500.0f, const float ambiant_light=0.05f, + const float opacity=1.0f, const bool display_axes = true, float *const pose_matrix=0) { + typedef typename cimg::largest::type to; + CImg(width,height,1,3,0).display_object3d(points,primitives,colors, + CImg(primitives.size)=(to)opacity,*this, + centering,render_static,render_motion, + double_sided,focale,ambiant_light,display_axes,pose_matrix); + return *this; + } + + //! Toggle fullscreen mode + CImgDisplay& toggle_fullscreen() { + return assign(width,height,title,normalization,events,!is_fullscreen,is_closed); + } + + // Inner routine used for fast resizing of buffer to display size. + template static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs, + t *ptrd, const unsigned int wd, const unsigned int hd) { + unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy; + float s, curr, old; + s = (float)ws/wd; + poffx = offx; curr=0; for (unsigned int x=0; x CImgDisplay& assign(const CImg& img, const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + fps_timer = 0*(unsigned long)(img.width + title + normalization_type + events_type + (int)fullscreen_flag + (int)closed_flag); + return assign(0,0); + } + + //! In-place version of the previous constructor + template CImgDisplay& assign(const CImgList& list, const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + fps_timer = 0*(unsigned long)(list.size + title + normalization_type + events_type + (int)fullscreen_flag + (int)closed_flag); + return assign(0,0); + } + + //! In-place version of the previous constructor + CImgDisplay& assign(const CImgDisplay &disp) { + return assign(disp.width,disp.height); + } + + // In-place version of the destructor (should not be used by the user). + CImgDisplay& _assign() { + return *this; + } + + //! Display an image in a window. + template CImgDisplay& display(const CImg& img) { + fps_timer = 0*img.width; + return *this; + } + + //! Resize window + CImgDisplay& resize(const int width, const int height, const bool redraw=true) { + fps_timer = 0*width*height*(int)redraw; + return *this; + } + + //! Move window + CImgDisplay& move(const int posx, const int posy) { + fps_timer = 0*posx*posy; + return *this; + } + + //! Move mouse pointer to a specific location + CImgDisplay& set_mouse(const int posx, const int posy) { + fps_timer = 0*posx*posy; + return *this; + } + + //! Hide mouse pointer + CImgDisplay& hide_mouse() { + return *this; + } + + //! Show mouse pointer + CImgDisplay& show_mouse() { + return *this; + } + + //! Wait for a window event in any CImg window + static void wait_all() {} + + //! Show a closed display + CImgDisplay& show() { + return *this; + } + + //! Close a visible display + CImgDisplay& close() { + return *this; + } + + //! Set the window title + CImgDisplay& set_title(const char *format,...) { + fps_timer = 0*(unsigned long)format; + return *this; + } + + //! Re-paint image content in window + CImgDisplay& paint() { + return *this; + } + + //! Render image buffer into GDI native image format + template CImgDisplay& render(const CImg& img) { + fps_timer = 0*img.width; + return *this; + } + + // X11-based display + //------------------- +#elif cimg_display_type==1 + void *data; + Window window; + Window background_window; + XImage *image; + Colormap colormap; + Atom wm_delete_window, wm_delete_protocol; +#ifdef cimg_use_xshm + XShmSegmentInfo *shminfo; +#endif + + static int screen_dimx() { + int res = 0; + if (!cimg::X11attr().display) { + Display *disp = XOpenDisplay((std::getenv("DISPLAY") ? std::getenv("DISPLAY") : ":0.0")); + if (!disp) throw CImgDisplayException("CImgDisplay::screen_dimx() : Can't open X11 display"); + res = DisplayWidth(disp,DefaultScreen(disp)); + XCloseDisplay(disp); + } else { +#ifdef cimg_use_xrandr + if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) + res = cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].width; + else +#endif + res = DisplayWidth(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); + } + return res; + } + + static int screen_dimy() { + int res = 0; + if (!cimg::X11attr().display) { + Display *disp = XOpenDisplay((std::getenv("DISPLAY") ? std::getenv("DISPLAY") : ":0.0")); + if (!disp) throw CImgDisplayException("CImgDisplay::screen_dimy() : Can't open X11 display"); + res = DisplayHeight(disp,DefaultScreen(disp)); + XCloseDisplay(disp); + } else { +#ifdef cimg_use_xrandr + if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) + res = cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].height; else +#endif + res = DisplayHeight(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); + } + return res; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) + throw CImgArgumentException("CImgDisplay::assign() : Specified window size (%u,%u) is not valid.",dimw,dimh); + assign_lowlevel(dimw,dimh,title,normalization_type,events_type,fullscreen_flag,closed_flag); + min = max = 0; + std::memset(data,0,(cimg::X11attr().nb_bits==8?sizeof(unsigned char): + (cimg::X11attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*width*height); + return paint(); + } + + template CImgDisplay& assign(const CImg& img, const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (img.is_empty()) + throw CImgArgumentException("CImgDisplay::CImgDisplay() : Specified input image (%u,%u,%u,%u,%p) is empty.", + img.width,img.height,img.depth,img.dim,img.data); + CImg tmp; + const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + assign_lowlevel(nimg.width,nimg.height,title,normalization_type,events_type,fullscreen_flag,closed_flag); + if (normalization==2) { const CImgStats st(nimg,false); min = (float)st.min; max = (float)st.max; } + return render(nimg).paint(); + } + + template CImgDisplay& assign(const CImgList& list, const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (list.is_empty()) + throw CImgArgumentException("CImgDisplay::CImgDisplay() : Specified input list (%u,%p) is empty.", + list.size,list.data); + CImg tmp; + const CImg img = list.get_append('x'), + &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + assign_lowlevel(nimg.width,nimg.height,title,normalization_type,events_type,fullscreen_flag,closed_flag); + if (normalization==2) { const CImgStats st(nimg,false); min = (float)st.min; max = (float)st.max; } + return render(nimg).paint(); + } + + CImgDisplay& assign(const CImgDisplay& win) { + assign_lowlevel(win.width,win.height,win.title,win.normalization,win.events,win.is_fullscreen,win.is_closed); + std::memcpy(data,win.data,(cimg::X11attr().nb_bits==8?sizeof(unsigned char): + cimg::X11attr().nb_bits==16?sizeof(unsigned short): + sizeof(unsigned int))*width*height); + return paint(); + } + + CImgDisplay& _assign() { + if (width && height) { + pthread_mutex_lock(cimg::X11attr().mutex); + + // Remove display window from event thread list + unsigned int i; + for (i=0; ishmaddr); + shmctl(shminfo->shmid,IPC_RMID,0); + delete shminfo; + shminfo = 0; + } else +#endif + XDestroyImage(image); + data = 0; + image = 0; + if (cimg::X11attr().nb_bits==8) XFreeColormap(cimg::X11attr().display,colormap); + colormap = 0; + XSync(cimg::X11attr().display, False); + + // Reset display variables + if (title) delete[] title; + width = height = normalization = events = 0; + is_fullscreen = is_resized = is_moved = is_event = false; + is_closed = true; + title = 0; + window_x = window_y = window_width = window_height = mouse_x = mouse_y = wheel = 0; + std::memset((void*)buttons,0,256*sizeof(unsigned int)); + std::memset((void*)keys,0,256*sizeof(unsigned int)); + std::memset((void*)released_keys,0,256*sizeof(unsigned int)); + min = max = 0; + + // End event thread and close display if necessary + if (!cimg::X11attr().nb_wins) { + + // Kill event thread + pthread_cancel(*cimg::X11attr().event_thread); + pthread_mutex_unlock(cimg::X11attr().mutex); + pthread_join(*cimg::X11attr().event_thread,0); + delete cimg::X11attr().event_thread; + cimg::X11attr().event_thread = 0; + pthread_mutex_destroy(cimg::X11attr().mutex); + delete cimg::X11attr().mutex; + cimg::X11attr().mutex = 0; + XSync(cimg::X11attr().display, False); + XCloseDisplay(cimg::X11attr().display); + cimg::X11attr().display=0; + delete cimg::X11attr().gc; + cimg::X11attr().gc = 0; + } else pthread_mutex_unlock(cimg::X11attr().mutex); + } + return *this; + } + + template CImgDisplay& display(const CImg& img) { + return render(img).paint(false); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { + if (!(nwidth && nheight)) + throw CImgArgumentException("CImgDisplay::resize() : Specified window size (%d,%d) is not valid.", + nwidth,nheight); + const unsigned int + tmpdimx=(nwidth>0)?nwidth:(-nwidth*width/100), + tmpdimy=(nheight>0)?nheight:(-nheight*height/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + const bool + is_disp_different = (width!=dimx || height!=dimy), + is_win_different = (window_width!=dimx || window_height!=dimy); + if (is_disp_different || is_win_different) { + pthread_mutex_lock(cimg::X11attr().mutex); + XResizeWindow(cimg::X11attr().display,window,dimx,dimy); + window_width = dimx; + window_height = dimy; + is_resized = false; + if (is_disp_different) { + switch (cimg::X11attr().nb_bits) { + case 8: { unsigned char foo=0; _resize(foo,dimx,dimy,redraw); } break; + case 16: { unsigned short foo=0; _resize(foo,dimx,dimy,redraw); } break; + default: { unsigned int foo=0; _resize(foo,dimx,dimy,redraw); } break; + } + width = dimx; + height = dimy; + } + pthread_mutex_unlock(cimg::X11attr().mutex); + if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); + if (redraw) return paint(); + } + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + show(); + pthread_mutex_lock(cimg::X11attr().mutex); + XMoveWindow(cimg::X11attr().display,window,posx,posy); + is_moved = false; + window_x = posx; + window_y = posy; + pthread_mutex_unlock(cimg::X11attr().mutex); + return paint(); + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (!is_closed && posx>=0 && posy>=0) { + pthread_mutex_lock(cimg::X11attr().mutex); + XWarpPointer(cimg::X11attr().display,None,window,0,0,0,0,posx,posy); + is_moved = false; + mouse_x = posx; + mouse_y = posy; + XSync(cimg::X11attr().display, False); + pthread_mutex_unlock(cimg::X11attr().mutex); + } + return *this; + } + + CImgDisplay& hide_mouse() { + pthread_mutex_lock(cimg::X11attr().mutex); + const char pix_data[8] = { 0 }; + XColor col; + col.red = col.green = col.blue = 0; + Pixmap pix = XCreateBitmapFromData(cimg::X11attr().display,window,pix_data,8,8); + Cursor cur = XCreatePixmapCursor(cimg::X11attr().display,pix,pix,&col,&col,0,0); + XFreePixmap(cimg::X11attr().display,pix); + XDefineCursor(cimg::X11attr().display,window,cur); + pthread_mutex_unlock(cimg::X11attr().mutex); + return *this; + } + + CImgDisplay& show_mouse() { + pthread_mutex_lock(cimg::X11attr().mutex); + XDefineCursor(cimg::X11attr().display,window,None); + pthread_mutex_unlock(cimg::X11attr().mutex); + return *this; + } + + static void wait_all() { + pthread_mutex_lock(cimg::X11attr().mutex); + bool flag = true; + XEvent event; + while (flag) { + for (unsigned int i=0; iis_event = false; + const unsigned int xevent_type = (cimg::X11attr().wins[i]->events)&3; + const unsigned int emask = + ((xevent_type>=1)?ExposureMask|StructureNotifyMask:0)| + ((xevent_type>=2)?ButtonPressMask|KeyPressMask|PointerMotionMask|LeaveWindowMask:0)| + ((xevent_type>=3)?ButtonReleaseMask|KeyReleaseMask:0); + XSelectInput(cimg::X11attr().display,cimg::X11attr().wins[i]->window,emask); + } + XNextEvent(cimg::X11attr().display, &event); + for (unsigned int i=0; iis_closed && event.xany.window==cimg::X11attr().wins[i]->window) { + cimg::X11attr().wins[i]->_handle_events(&event); + if (cimg::X11attr().wins[i]->is_event) flag = false; + } + } + pthread_mutex_unlock(cimg::X11attr().mutex); + } + + CImgDisplay& show() { + if (is_closed) { + pthread_mutex_lock(cimg::X11attr().mutex); + if (is_fullscreen) _init_fullscreen(); + _map_window(); + is_closed = false; + pthread_mutex_unlock(cimg::X11attr().mutex); + } + return paint(); + } + + CImgDisplay& close() { + if (!is_closed) { + pthread_mutex_lock(cimg::X11attr().mutex); + if (is_fullscreen) _desinit_fullscreen(); + XUnmapWindow(cimg::X11attr().display,window); + window_x = window_y = -1; + is_closed = true; + pthread_mutex_unlock(cimg::X11attr().mutex); + } + return *this; + } + + CImgDisplay& set_title(const char *format,...) { + char tmp[1024]={0}; + va_list ap; + va_start(ap, format); + std::vsprintf(tmp,format,ap); + va_end(ap); + if (title) delete[] title; + const int s = cimg::strlen(tmp)+1; + title = new char[s]; + std::memcpy(title,tmp,s*sizeof(char)); + pthread_mutex_lock(cimg::X11attr().mutex); + XStoreName(cimg::X11attr().display,window,tmp); + pthread_mutex_unlock(cimg::X11attr().mutex); + return *this; + } + + CImgDisplay& paint(const bool wait_expose=true) { + pthread_mutex_lock(cimg::X11attr().mutex); + _paint(wait_expose); + pthread_mutex_unlock(cimg::X11attr().mutex); + return *this; + } + + template CImgDisplay& render(const CImg& img, const bool flag8=false) { + if (img.is_empty()) + throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", + img.width,img.height,img.depth,img.dim,img.data); + if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + if (cimg::X11attr().nb_bits==8 && (img.width!=width || img.height!=height)) return render(img.get_resize(width,height,1,-100,1)); + if (cimg::X11attr().nb_bits==8 && !flag8 && img.dim==3) return render(img.get_RGBtoLUT(true),true); + + const unsigned int xymax = img.width*img.height; + const T + *data1 = img.ptr(), + *data2 = (img.dim>=2)?img.ptr(0,0,0,1):data1, + *data3 = (img.dim>=3)?img.ptr(0,0,0,2):data1; + if (cimg::X11attr().blue_first) cimg::swap(data1,data3); + pthread_mutex_lock(cimg::X11attr().mutex); + + if (!normalization || (normalization==3 && cimg::type::id()==cimg::type::id())) { + min = max = 0; + switch (cimg::X11attr().nb_bits) { + case 8: { + _set_colormap(colormap,img.dim); + unsigned char *const ndata = (img.width==width && img.height==height)?(unsigned char*)data:new unsigned char[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + switch (img.dim) { + case 1: for (unsigned int xy=0; xy>4); + } break; + default: for (unsigned int xy=0; xy>5)<<2) | (B>>6); + } break; + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned char*)data,width,height); delete[] ndata; } + } break; + case 16: { + unsigned short *const ndata = (img.width==width && img.height==height)?(unsigned short*)data:new unsigned short[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + if (cimg::X11attr().byte_order) for (unsigned int xy=0; xy>2; + *(ptrd++) = (unsigned char)*(data1++)&M | (G>>3); + *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); + } else for (unsigned int xy=0; xy>2; + *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); + *(ptrd++) = (unsigned char)*(data1++)&M | (G>>3); + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned short*)data,width,height); delete[] ndata; } + } break; + default: { + unsigned int *const ndata = (img.width==width && img.height==height)?(unsigned int*)data:new unsigned int[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + if (cimg::X11attr().byte_order) for (unsigned int xy=0; xy::is_float()) { const CImgStats st(img,false); min = (float)st.min; max = (float)st.max; } + else { min = (float)cimg::type::min(); max = (float)cimg::type::max(); } + } else if ((min>max) || normalization==1) { const CImgStats st(img,false); min = (float)st.min; max = (float)st.max; } + const float delta = max-min, mm = delta?delta:1.0f; + switch (cimg::X11attr().nb_bits) { + case 8: { + _set_colormap(colormap,img.dim); + unsigned char *const ndata = (img.width==width && img.height==height)?(unsigned char*)data:new unsigned char[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + switch (img.dim) { + case 1: for (unsigned int xy=0; xy>4); + } break; + default: + for (unsigned int xy=0; xy>5)<<2) | (B>>6); + } break; + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned char*)data,width,height); delete[] ndata; } + } break; + case 16: { + unsigned short *const ndata = (img.width==width && img.height==height)?(unsigned short*)data:new unsigned short[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + if (cimg::X11attr().byte_order) for (unsigned int xy=0; xy>2; + *(ptrd++) = (unsigned char)(255*(*(data1++)-min)/mm)&M | (G>>3); + *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-min)/mm)>>3); + } else for (unsigned int xy=0; xy>2; + *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-min)/mm)>>3); + *(ptrd++) = (unsigned char)(255*(*(data1++)-min)/mm)&M | (G>>3); + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned short*)data,width,height); delete[] ndata; } + } break; + default: { + unsigned int *const ndata = (img.width==width && img.height==height)?(unsigned int*)data:new unsigned int[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + if (cimg::X11attr().byte_order) for (unsigned int xy=0; xyred_maskblue_mask) cimg::X11attr().blue_first = true; + cimg::X11attr().byte_order = ImageByteOrder(cimg::X11attr().display); + + pthread_mutex_lock(cimg::X11attr().mutex); + cimg::X11attr().event_thread = new pthread_t; + pthread_create(cimg::X11attr().event_thread,0,_events_thread,0); + } else pthread_mutex_lock(cimg::X11attr().mutex); + + // Set display variables + width = dimw; + height = dimh; + normalization = normalization_type%4; + events = events_type%4; + is_fullscreen = fullscreen_flag; + title = tmp_title; + window_x = window_y = wheel = 0; + mouse_x = mouse_y = -1; + std::memset((void*)buttons,0,256*sizeof(unsigned int)); + std::memset((void*)keys,0,256*sizeof(unsigned int)); + std::memset((void*)released_keys,0,256*sizeof(unsigned int)); + is_resized = is_moved = is_event = false; + is_closed = closed_flag; + fps_timer = fps_frames = timer = 0; + fps_fps = 0; + + // Create X11 window and palette (if 8bits display) + if (is_fullscreen) { + _init_fullscreen(); + const unsigned int sx = screen_dimx(), sy = screen_dimy(); + XSetWindowAttributes winattr; + winattr.override_redirect = True; + window = XCreateWindow(cimg::X11attr().display, + RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + (sx-width)/2,(sy-height)/2, + width,height,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); + } else + window = XCreateSimpleWindow(cimg::X11attr().display, + RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + 0,0,width,height,2,0,0x0L); + XStoreName(cimg::X11attr().display,window,title?title:" "); + if (cimg::X11attr().nb_bits==8) { + colormap = XCreateColormap(cimg::X11attr().display,window,DefaultVisual(cimg::X11attr().display, + DefaultScreen(cimg::X11attr().display)),AllocAll); + _set_colormap(colormap,3); + XSetWindowColormap(cimg::X11attr().display,window,colormap); + } + window_width = width; + window_height = height; + + // Create XImage + const unsigned int bufsize = width*height*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); +#ifdef cimg_use_xshm + shminfo = 0; + if (XShmQueryExtension(cimg::X11attr().display)) { + shminfo = new XShmSegmentInfo; + image = XShmCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,shminfo,width,height); + if (!image) { delete shminfo; shminfo = 0; } + else { + shminfo->shmid = shmget(IPC_PRIVATE, bufsize, IPC_CREAT | 0777); + if (shminfo->shmid==-1) { XDestroyImage(image); delete shminfo; shminfo = 0; } + else { + shminfo->shmaddr = image->data = (char*)(data = shmat(shminfo->shmid,0,0)); + if (shminfo->shmaddr==(char*)-1) { XDestroyImage(image); shmctl(shminfo->shmid,IPC_RMID,0); delete shminfo; shminfo = 0; } + shminfo->readOnly = False; + cimg::X11attr().shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_lowlevel_xshm); + XShmAttach(cimg::X11attr().display, shminfo); + XSync(cimg::X11attr().display, False); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11attr().shm_enabled) { + XDestroyImage(image); + shmdt(shminfo->shmaddr); + shmctl(shminfo->shmid,IPC_RMID,0); + delete shminfo; shminfo = 0; + } + } + } + } + if (!shminfo) +#endif + { + data = std::malloc(bufsize); + image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,(char*)data,width,height,8,0); + } + + if (!is_closed) _map_window(); else { window_x = window_y = cimg::type::min(); } + + if (events) { + wm_delete_window = XInternAtom(cimg::X11attr().display, "WM_DELETE_WINDOW", False); + wm_delete_protocol = XInternAtom(cimg::X11attr().display, "WM_PROTOCOLS", False); + XSetWMProtocols(cimg::X11attr().display, window, &wm_delete_window, 1); + if (is_fullscreen) XGrabKeyboard(cimg::X11attr().display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime); + } + cimg::X11attr().wins[cimg::X11attr().nb_wins++]=this; + pthread_mutex_unlock(cimg::X11attr().mutex); + } + + void _map_window() { + XWindowAttributes attr; + XEvent event; + XSelectInput(cimg::X11attr().display,window,ExposureMask | StructureNotifyMask); + bool exposed = false, mapped = false; + XMapRaised(cimg::X11attr().display,window); + XSync(cimg::X11attr().display,False); + do { + XWindowEvent(cimg::X11attr().display,window,StructureNotifyMask | ExposureMask,&event); + switch (event.type) { + case MapNotify: mapped = true; break; + case Expose: exposed = true; break; + default: XSync(cimg::X11attr().display, False); cimg::sleep(10); + } + } while (!(exposed && mapped)); + do { + XGetWindowAttributes(cimg::X11attr().display, window, &attr); + if (attr.map_state!=IsViewable) { XSync(cimg::X11attr().display,False); cimg::sleep(10); } + } while (attr.map_state != IsViewable); + window_x = attr.x; + window_y = attr.y; + } + + void _set_colormap(Colormap& colormap, const unsigned int dim) { + XColor palette[256]; + switch (dim) { + case 1: // palette for greyscale images + for (unsigned int index=0; index<256; index++) { + palette[index].pixel = index; + palette[index].red = palette[index].green = palette[index].blue = index<<8; + palette[index].flags = DoRed | DoGreen | DoBlue; + } + break; + case 2: // palette for RG images + for (unsigned int index=0, r=8; r<256; r+=16) + for (unsigned int g=8; g<256; g+=16) { + palette[index].pixel = index; + palette[index].red = palette[index].blue = r<<8; + palette[index].green = g<<8; + palette[index++].flags = DoRed | DoGreen | DoBlue; + } + break; + default: // palette for RGB images + for (unsigned int index=0, r=16; r<256; r+=32) + for (unsigned int g=16; g<256; g+=32) + for (unsigned int b=32; b<256; b+=64) { + palette[index].pixel = index; + palette[index].red = r<<8; + palette[index].green = g<<8; + palette[index].blue = b<<8; + palette[index++].flags = DoRed | DoGreen | DoBlue; + } + break; + } + XStoreColors(cimg::X11attr().display,colormap,palette,256); + } + + void _paint(const bool wait_expose=true) { + if (!is_closed) { + if (wait_expose) { + static XEvent event; + event.xexpose.type = Expose; + event.xexpose.serial = 0; + event.xexpose.send_event = True; + event.xexpose.display = cimg::X11attr().display; + event.xexpose.window = window; + event.xexpose.x = 0; + event.xexpose.y = 0; + event.xexpose.width = (int)width; + event.xexpose.height = (int)height; + event.xexpose.count = 0; + XSendEvent(cimg::X11attr().display, window, False, 0, &event); + } else { +#if cimg_use_xshm + if (shminfo) XShmPutImage(cimg::X11attr().display,window,*cimg::X11attr().gc,image,0,0,0,0,width,height,False); + else +#endif + XPutImage(cimg::X11attr().display,window,*cimg::X11attr().gc,image,0,0,0,0,width,height); + XSync(cimg::X11attr().display, False); + } + } + } + + template void _resize(T foo, const unsigned int ndimx, const unsigned int ndimy, const bool redraw) { + foo = 0; +#ifdef cimg_use_xshm + if (shminfo) { + XShmSegmentInfo *nshminfo = new XShmSegmentInfo; + XImage *nimage = XShmCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy); + nshminfo->shmid = shmget(IPC_PRIVATE, ndimx*ndimy*sizeof(T), IPC_CREAT | 0777); + nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0); + nshminfo->readOnly = False; + XShmAttach(cimg::X11attr().display, nshminfo); + XSync(cimg::X11attr().display, False); + T *const ndata = (T*)nimage->data; + if (redraw) _render_resize((T*)data,width,height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + XShmDetach(cimg::X11attr().display, shminfo); + XDestroyImage(image); + shmdt(shminfo->shmaddr); + shmctl(shminfo->shmid,IPC_RMID,0); + delete shminfo; + shminfo = nshminfo; + image = nimage; + data = (void*)ndata; + } else +#endif + { + T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T)); + if (redraw) _render_resize((T*)data,width,height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + data = (void*)ndata; + XDestroyImage(image); + image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,(char*)data,ndimx,ndimy,8,0); + } + } + + void _init_fullscreen() { + background_window = 0; + if (is_fullscreen && !is_closed) { +#ifdef cimg_use_xrandr + int foo; + if (XRRQueryExtension(cimg::X11attr().display,&foo,&foo)) { + XRRRotations(cimg::X11attr().display, DefaultScreen(cimg::X11attr().display), &cimg::X11attr().curr_rotation); + if (!cimg::X11attr().resolutions) { + cimg::X11attr().resolutions = XRRSizes(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display),&foo); + cimg::X11attr().nb_resolutions = (unsigned int)foo; + } + if (cimg::X11attr().resolutions) { + cimg::X11attr().curr_resolution = 0; + for (unsigned int i=0; i=width && nh>=height && + nw<=(unsigned int)(cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].width) && + nh<=(unsigned int)(cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].height)) + cimg::X11attr().curr_resolution = i; + } + if (cimg::X11attr().curr_resolution>0) { + XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11attr().display, DefaultRootWindow(cimg::X11attr().display)); + XRRSetScreenConfig(cimg::X11attr().display, config, DefaultRootWindow(cimg::X11attr().display), + cimg::X11attr().curr_resolution, cimg::X11attr().curr_rotation, CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(cimg::X11attr().display, False); + } + } + } + cimg::warn(!cimg::X11attr().resolutions,"CImgDisplay::_create_window() : Xrandr extension is not supported by the X server."); +#endif + const unsigned int sx = screen_dimx(), sy = screen_dimy(); + XSetWindowAttributes winattr; + winattr.override_redirect = True; + if (sx!=width || sy!=height) { + background_window = XCreateWindow(cimg::X11attr().display, + RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)),0,0, + sx,sy,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); + const unsigned int bufsize = sx*sy*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); + void *background_data = std::malloc(bufsize); + std::memset(background_data,0,bufsize); + XImage *background_image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,(char*)background_data,sx,sy,8,0); + XEvent event; + XSelectInput(cimg::X11attr().display,background_window,StructureNotifyMask); + XMapRaised(cimg::X11attr().display,background_window); + do XWindowEvent(cimg::X11attr().display,background_window,StructureNotifyMask,&event); + while (event.type!=MapNotify); + XPutImage(cimg::X11attr().display,background_window,*cimg::X11attr().gc,background_image,0,0,0,0,sx,sy); + XWindowAttributes attr; + XGetWindowAttributes(cimg::X11attr().display, background_window, &attr); + while (attr.map_state != IsViewable) XSync(cimg::X11attr().display, False); + XDestroyImage(background_image); + } + } + } + + void _desinit_fullscreen() { + if (is_fullscreen) { + XUngrabKeyboard(cimg::X11attr().display,CurrentTime); +#if cimg_use_xrandr + if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) { + XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11attr().display, DefaultRootWindow(cimg::X11attr().display)); + XRRSetScreenConfig(cimg::X11attr().display, config, DefaultRootWindow(cimg::X11attr().display), + 0, cimg::X11attr().curr_rotation, CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(cimg::X11attr().display, False); + cimg::X11attr().curr_resolution = 0; + } +#endif + if (background_window) XDestroyWindow(cimg::X11attr().display,background_window); + background_window = 0; + is_fullscreen = false; + } + } + + void _handle_events(const XEvent *const pevent) { + XEvent event=*pevent; + switch (event.type) { + case ClientMessage: + if ((int)event.xclient.message_type==(int)wm_delete_protocol && + (int)event.xclient.data.l[0]==(int)wm_delete_window) { + XUnmapWindow(cimg::X11attr().display,window); + mouse_x = mouse_y = -1; + std::memmove((void*)(buttons+1),(void*)buttons,255); + std::memmove((void*)(keys+1),(void*)keys,255); + if (key) { std::memmove((void*)(released_keys+1),(void*)released_keys,255); released_key = key; } + key = button = 0; + is_closed = is_event = true; + } + break; + case ConfigureNotify: { + while (XCheckWindowEvent(cimg::X11attr().display,window,StructureNotifyMask,&event)); + const unsigned int + nw = event.xconfigure.width, + nh = event.xconfigure.height; + const int + nx = event.xconfigure.x, + ny = event.xconfigure.y; + if (nw && nh && (nw!=window_width || nh!=window_height)) { + window_width = nw; + window_height = nh; + mouse_x = mouse_y = -1; + XResizeWindow(cimg::X11attr().display,window,window_width,window_height); + is_resized = is_event = true; + } + if (nx!=window_x || ny!=window_y) { + window_x = nx; + window_y = ny; + is_moved = is_event = true; + } + } break; + case Expose: { + while (XCheckWindowEvent(cimg::X11attr().display,window,ExposureMask,&event)); + _paint(false); + if (is_fullscreen) { + XWindowAttributes attr; + XGetWindowAttributes(cimg::X11attr().display, window, &attr); + while (attr.map_state != IsViewable) XSync(cimg::X11attr().display, False); + XSetInputFocus(cimg::X11attr().display, window, RevertToParent, CurrentTime); + } + } break; + case ButtonPress: { + do { + switch (event.xbutton.button) { + case 1: std::memmove((void*)(buttons+1),(void*)buttons,255); button|=1; is_event = true; break; + case 2: std::memmove((void*)(buttons+1),(void*)buttons,255); button|=4; is_event = true; break; + case 3: std::memmove((void*)(buttons+1),(void*)buttons,255); button|=2; is_event = true; break; + default: break; + } + } while (XCheckWindowEvent(cimg::X11attr().display,window,ButtonPressMask,&event)); + } break; + case ButtonRelease: { + do { + switch (event.xbutton.button) { + case 1: std::memmove((void*)(buttons+1),(void*)buttons,255); button&=~1U; is_event = true; break; + case 2: std::memmove((void*)(buttons+1),(void*)buttons,255); button&=~4U; is_event = true; break; + case 3: std::memmove((void*)(buttons+1),(void*)buttons,255); button&=~2U; is_event = true; break; + case 4: wheel++; is_event = true; break; + case 5: wheel--; is_event = true; break; + default: break; + } + } while (XCheckWindowEvent(cimg::X11attr().display,window,ButtonReleaseMask,&event)); + } break; + case KeyPress: { + char tmp; + KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + std::memmove((void*)(keys+1),(void*)keys,255); key = (unsigned int)ksym; + std::memmove((void*)(released_keys+1),(void*)released_keys,255); released_key = 0; + is_event = true; + } break; + case KeyRelease: { + char tmp; + KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + std::memmove((void*)(keys+1),(void*)keys,255); key = 0; + std::memmove((void*)(released_keys+1),(void*)released_keys,255); released_key = (unsigned int)ksym; + is_event = true; + } break; + case LeaveNotify: + while (XCheckWindowEvent(cimg::X11attr().display,window,LeaveWindowMask,&event)); + mouse_x = mouse_y =-1; + is_event = true; + break; + case MotionNotify: + while (XCheckWindowEvent(cimg::X11attr().display,window,PointerMotionMask,&event)); + mouse_x = event.xmotion.x; + mouse_y = event.xmotion.y; + if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; + is_event = true; + break; + } + } + + static void* _events_thread(void *arg) { + arg = 0; + XEvent event; + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); + for (;;) { + pthread_mutex_lock(cimg::X11attr().mutex); + for (unsigned int i=0; ievents)&3; + const unsigned int emask = + ((xevent_type>=1)?ExposureMask|StructureNotifyMask:0)| + ((xevent_type>=2)?ButtonPressMask|KeyPressMask|PointerMotionMask|LeaveWindowMask:0)| + ((xevent_type>=3)?ButtonReleaseMask|KeyReleaseMask:0); + XSelectInput(cimg::X11attr().display,cimg::X11attr().wins[i]->window,emask); + } + bool event_flag = XCheckTypedEvent(cimg::X11attr().display, ClientMessage, &event); + if (!event_flag) event_flag = XCheckMaskEvent(cimg::X11attr().display, + ExposureMask|StructureNotifyMask|ButtonPressMask| + KeyPressMask|PointerMotionMask|LeaveWindowMask|ButtonReleaseMask| + KeyReleaseMask,&event); + if (event_flag) { + for (unsigned int i=0; iis_closed && event.xany.window==cimg::X11attr().wins[i]->window) + cimg::X11attr().wins[i]->_handle_events(&event); + } + pthread_mutex_unlock(cimg::X11attr().mutex); + pthread_testcancel(); + cimg::sleep(7); + } + return 0; + } + + // Windows-based display + //----------------------- +#elif cimg_display_type==2 + CLIENTCREATESTRUCT ccs; + BITMAPINFO bmi; + unsigned int *data; + DEVMODE curr_mode; + HWND window; + HWND background_window; + HDC hdc; + HANDLE thread; + HANDLE created; + HANDLE mutex; + bool visible_cursor; + + static int screen_dimx() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return mode.dmPelsWidth; + } + + static int screen_dimy() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return mode.dmPelsHeight; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) + throw CImgArgumentException("CImgDisplay::assign() : Specified window size (%u,%u) is not valid.",dimw,dimh); + assign_lowlevel(dimw,dimh,title,normalization_type,events_type,fullscreen_flag,closed_flag); + min = max = 0; + std::memset(data,0,sizeof(unsigned int)*width*height); + return paint(); + } + + template CImgDisplay& assign(const CImg& img, const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (img.is_empty()) + throw CImgArgumentException("CImgDisplay::CImgDisplay() : Specified input image (%u,%u,%u,%u,%p) is empty.", + img.width,img.height,img.depth,img.dim,img.data); + CImg tmp; + const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + assign_lowlevel(nimg.width,nimg.height,title,normalization_type,events_type,fullscreen_flag,closed_flag); + if (normalization==2) { const CImgStats st(nimg,false); min = (float)st.min; max = (float)st.max; } + return display(nimg); + } + + template CImgDisplay& assign(const CImgList& list,const char *title=0, + const unsigned int normalization_type=3, const unsigned int events_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (list.is_empty()) + throw CImgArgumentException("CImgDisplay::CImgDisplay() : Specified input list (%u,%p) is empty.", + list.size,list.data); + CImg tmp; + const CImg img = list.get_append('x'), + &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + assign_lowlevel(nimg.width,nimg.height,title,normalization_type,events_type,fullscreen_flag,closed_flag); + if (normalization==2) { const CImgStats st(nimg,false); min = (float)st.min; max = (float)st.max; } + return display(nimg); + } + + CImgDisplay& assign(const CImgDisplay& win) { + assign_lowlevel(win.width,win.height,win.title,win.normalization,win.events,win.is_fullscreen,win.is_closed); + std::memcpy(data,win.data,sizeof(unsigned int)*width*height); + return paint(); + } + + CImgDisplay& _assign() { + DestroyWindow(window); + if (events) TerminateThread(thread,0); + if (data) delete[] data; + if (title) delete[] title; + if (is_fullscreen) _desinit_fullscreen(); + width = height = normalization = events = 0; + is_fullscreen = is_resized = is_moved = is_event = false; + is_closed = true; + title = 0; + window_x = window_y = window_width = window_height = mouse_x = mouse_y = wheel = 0; + std::memset((void*)buttons,0,256*sizeof(unsigned int)); + std::memset((void*)keys,0,256*sizeof(unsigned int)); + std::memset((void*)released_keys,0,256*sizeof(unsigned int)); + min = max = 0; + return *this; + } + + template CImgDisplay& display(const CImg& img) { + return render(img).paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { + if (!(nwidth && nheight)) + throw CImgArgumentException("CImgDisplay::resize() : Specified window size (%d,%d) is not valid.", + nwidth,nheight); + const unsigned int + tmpdimx=(nwidth>0)?nwidth:(-nwidth*width/100), + tmpdimy=(nheight>0)?nheight:(-nheight*height/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + const bool + is_disp_different = (width!=dimx || height!=dimy), + is_win_different = (window_width!=dimx || window_height!=dimy); + + if (is_disp_different || is_win_different) { + RECT rect; rect.left=rect.top=0; rect.right = dimx-1; rect.bottom = dimy-1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int cwidth = rect.right-rect.left+1, cheight = rect.bottom-rect.top+1; + SetWindowPos(window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); + window_width = dimx; + window_height = dimy; + is_resized = false; + if (is_disp_different) { + unsigned int *ndata = new unsigned int[dimx*dimy]; + if (redraw) _render_resize(data,width,height,ndata,dimx,dimy); + else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); + delete[] data; + data = ndata; + bmi.bmiHeader.biWidth = dimx; + bmi.bmiHeader.biHeight = -(int)dimy; + width = dimx; + height = dimy; + } + if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); + if (redraw) return paint(); + } + return *this; + } + + CImgDisplay& move(const int posx,const int posy) { + if (!is_fullscreen) { + RECT rect; rect.left=rect.top=0; rect.right=window_width-1; rect.bottom=window_height-1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int border1 = (rect.right-rect.left+1-width)/2, border2 = rect.bottom-rect.top+1-height-border1; + SetWindowPos(window,0,posx-border1,posy-border2,0,0,SWP_NOSIZE | SWP_NOZORDER); + } else SetWindowPos(window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); + window_x = posx; + window_y = posy; + is_moved = false; + return show(); + } + + // Internal routine to retrieve the position of the current window. + CImgDisplay& _update_window_pos() { + if (!is_closed) { + RECT rect; + rect.left = rect.top = 0; rect.right = width-1; rect.bottom = height-1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int border1 = (rect.right-rect.left+1-width)/2, border2 = rect.bottom-rect.top+1-height-border1; + GetWindowRect(window,&rect); + window_x = rect.left + border1; + window_y = rect.top + border2; + } else window_x = window_y = -1; + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (!is_closed && posx>=0 && posy>=0) { + _update_window_pos(); + SetCursorPos(window_x+posx,window_y+posy); + mouse_x = posx; + mouse_y = posy; + } + return *this; + } + + CImgDisplay& hide_mouse() { + visible_cursor = false; + ShowCursor(FALSE); + SendMessage(window,WM_SETCURSOR,0,0); + return *this; + } + + CImgDisplay& show_mouse() { + visible_cursor = true; + ShowCursor(TRUE); + SendMessage(window,WM_SETCURSOR,0,0); + return *this; + } + + static void wait_all() { + WaitForSingleObject(cimg::Win32attr().wait_event,INFINITE); + } + + CImgDisplay& show() { + if (is_closed) { + is_closed = false; + if (is_fullscreen) _init_fullscreen(); + ShowWindow(window,SW_SHOW); + _update_window_pos(); + } + return paint(); + } + + CImgDisplay& close() { + if (!is_closed && !is_fullscreen) { + if (is_fullscreen) _desinit_fullscreen(); + ShowWindow(window,SW_HIDE); + is_closed = true; + window_x = window_y = 0; + } + return *this; + } + + CImgDisplay& set_title(const char *format,...) { + char tmp[1024]={0}; + va_list ap; + va_start(ap, format); + std::vsprintf(tmp,format,ap); + va_end(ap); + if (title) delete[] title; + const int s = cimg::strlen(tmp)+1; + title = new char[s]; + std::memcpy(title,tmp,s*sizeof(char)); + SetWindowTextA(window, tmp); + return *this; + } + + CImgDisplay& paint() { + if (!is_closed) { + WaitForSingleObject(mutex,INFINITE); + SetDIBitsToDevice(hdc,0,0,width,height,0,0,0,height,data,&bmi,DIB_RGB_COLORS); + ReleaseMutex(mutex); + } + return *this; + } + + template CImgDisplay& render(const CImg& img) { + if (img.is_empty()) + throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", + img.width,img.height,img.depth,img.dim,img.data); + if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + + const T + *data1 = img.ptr(), + *data2 = (img.dim>=2)?img.ptr(0,0,0,1):data1, + *data3 = (img.dim>=3)?img.ptr(0,0,0,2):data1; + + WaitForSingleObject(mutex,INFINITE); + unsigned int + *const ndata = (img.width==width && img.height==height)?data:new unsigned int[img.width*img.height], + *ptrd = ndata; + + if (!normalization || (normalization==3 && cimg::type::id()==cimg::type::id())) { + min = max = 0; + for (unsigned int xy = img.width*img.height; xy>0; xy--) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); + } else { + if (normalization==3) { + if (cimg::type::is_float()) { const CImgStats st(img,false); min = (float)st.min; max = (float)st.max; } + else { min = (float)cimg::type::min(); max = (float)cimg::type::max(); } + } else if ((min>max) || normalization==1) { const CImgStats st(img,false); min = (float)st.min; max = (float)st.max; } + const float delta = max-min, mm = delta?delta:1.0f; + for (unsigned int xy = img.width*img.height; xy>0; xy--) { + const unsigned char + R = (unsigned char)(255*(*(data1++)-min)/mm), + G = (unsigned char)(255*(*(data2++)-min)/mm), + B = (unsigned char)(255*(*(data3++)-min)/mm); + *(ptrd++) = (R<<16) | (G<<8) | (B); + } + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,data,width,height); delete[] ndata; } + ReleaseMutex(mutex); + return *this; + } + + CImgDisplay& assign_lowlevel(const unsigned int dimw,const unsigned int dimh,const char *ptitle=0, + const unsigned int normalization_type=3,const unsigned int events_type=3, + const bool fullscreen_flag=false,const bool closed_flag=false) { + + + // Allocate space for window title + const int s = cimg::strlen(ptitle)+1; + char *tmp_title = s?new char[s]:0; + if (s) std::memcpy(tmp_title,ptitle,s*sizeof(char)); + + // Destroy previous window if existing + if (width && height) _assign(); + + // Set display variables + width = dimw; + height = dimh; + normalization = normalization_type%4; + events = events_type%4; + is_fullscreen = fullscreen_flag; + title = tmp_title; + window_x = window_y = wheel = 0; + mouse_x = mouse_y = -1; + std::memset((void*)buttons,0,256*sizeof(unsigned int)); + std::memset((void*)keys,0,256*sizeof(unsigned int)); + std::memset((void*)released_keys,0,256*sizeof(unsigned int)); + is_resized = is_moved = is_event = false; + is_closed = closed_flag; + fps_timer = fps_frames = timer = 0; + fps_fps = 0; + visible_cursor = true; + + if (is_fullscreen) _init_fullscreen(); + + // Create event thread + void *arg = (void*)(new void*[2]); + ((void**)arg)[0]=(void*)this; + ((void**)arg)[1]=(void*)title; + if (events) { + unsigned long ThreadID = 0; + mutex = CreateMutex(0,FALSE,0); + created = CreateEvent(0,FALSE,FALSE,0); + thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID); + WaitForSingleObject(created,INFINITE); + } else _events_thread(arg); + + return *this; + } + + static LRESULT APIENTRY _handle_events(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) { +#ifdef _WIN64 + CImgDisplay* disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); +#else + CImgDisplay* disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA); +#endif + MSG st_msg; + + switch(msg) { + case WM_CLOSE: + disp->mouse_x = disp->mouse_y = -1; + disp->window_x = disp->window_y = 0; + std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,255); + std::memmove((void*)(disp->keys+1),(void*)disp->keys,255); + if (disp->key) { + std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,255); + disp->released_key = disp->key; + } + disp->key = disp->button = 0; + disp->is_closed=true; + ReleaseMutex(disp->mutex); + ShowWindow(disp->window,SW_HIDE); + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + return 0; + case WM_SIZE: { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)); + WaitForSingleObject(disp->mutex,INFINITE); + const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam); + if (nw && nh && (nw!=disp->width || nh!=disp->height)) { + disp->window_width = nw; + disp->window_height = nh; + disp->mouse_x = disp->mouse_y = -1; + disp->is_resized = disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + } + ReleaseMutex(disp->mutex); + } break; + case WM_MOVE: { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)); + WaitForSingleObject(disp->mutex,INFINITE); + const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam)); + if (nx!=disp->window_x || ny!=disp->window_y) { + disp->window_x = nx; + disp->window_y = ny; + disp->is_moved = disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + } + ReleaseMutex(disp->mutex); + } break; + case WM_PAINT: + disp->paint(); + break; + } + if (disp->events>=2) switch(msg) { + case WM_KEYDOWN: + std::memmove((void*)(disp->keys+1),(void*)disp->keys,255); disp->key = (int)wParam; + std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,255); disp->released_key = 0; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_MOUSEMOVE: { + while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)); + disp->mouse_x = LOWORD(lParam); + disp->mouse_y = HIWORD(lParam); + if (disp->mouse_x<0 || disp->mouse_y<0 || disp->mouse_x>=disp->dimx() || disp->mouse_y>=disp->dimy()) + disp->mouse_x=disp->mouse_y=-1; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + } break; + case WM_LBUTTONDOWN: + std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,255); + disp->button|=1U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_RBUTTONDOWN: + std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,255); + disp->button|=2U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_MBUTTONDOWN: + std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,255); + disp->button|=4U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case 0x020A: // WM_MOUSEWHEEL: + disp->wheel+=(int)((short)HIWORD(wParam))/120; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + } + if (disp->events>=3) switch(msg) { + case WM_KEYUP: + std::memmove((void*)(disp->keys+1),(void*)disp->keys,255); disp->key = 0; + std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,255); disp->released_key = (int)wParam; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_LBUTTONUP: + std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,255); + disp->button&=~1U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_RBUTTONUP: + std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,255); + disp->button&=~2U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_MBUTTONUP: + std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,255); + disp->button&=~4U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_SETCURSOR: + if (disp->visible_cursor) ShowCursor(TRUE); + else ShowCursor(FALSE); + break; + } + return DefWindowProc(window,msg,wParam,lParam); + } + + static DWORD WINAPI _events_thread(void* arg) { + CImgDisplay *disp = (CImgDisplay*)(((void**)arg)[0]); + const char *title = (const char*)(((void**)arg)[1]); + MSG msg; + delete[] (void**)arg; + disp->bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); + disp->bmi.bmiHeader.biWidth=disp->width; + disp->bmi.bmiHeader.biHeight=-(int)disp->height; + disp->bmi.bmiHeader.biPlanes=1; + disp->bmi.bmiHeader.biBitCount=32; + disp->bmi.bmiHeader.biCompression=BI_RGB; + disp->bmi.bmiHeader.biSizeImage=0; + disp->bmi.bmiHeader.biXPelsPerMeter=1; + disp->bmi.bmiHeader.biYPelsPerMeter=1; + disp->bmi.bmiHeader.biClrUsed=0; + disp->bmi.bmiHeader.biClrImportant=0; + disp->data = new unsigned int[disp->width*disp->height]; + if (!disp->is_fullscreen) { // Normal window + RECT rect; + rect.left=rect.top=0; rect.right=disp->width-1; rect.bottom=disp->height-1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int border1 = (rect.right-rect.left+1-disp->width)/2, border2 = rect.bottom-rect.top+1-disp->height-border1; + disp->window = CreateWindowA("MDICLIENT",title?title:" ", + WS_OVERLAPPEDWINDOW | (disp->is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT, + disp->width + 2*border1, disp->height + border1 + border2, + 0,0,0,&(disp->ccs)); + if (!disp->is_closed) { + GetWindowRect(disp->window,&rect); + disp->window_x = rect.left + border1; + disp->window_y = rect.top + border2; + } else disp->window_x = disp->window_y = 0; + } else { // Fullscreen window + const unsigned int sx = screen_dimx(), sy = screen_dimy(); + disp->window = CreateWindowA("MDICLIENT",title?title:" ", + WS_POPUP | (disp->is_closed?0:WS_VISIBLE), (sx-disp->width)/2, (sy-disp->height)/2, + disp->width,disp->height,0,0,0,&(disp->ccs)); + disp->window_x = disp->window_y = 0; + } + SetForegroundWindow(disp->window); + disp->hdc = GetDC(disp->window); + disp->window_width = disp->width; + disp->window_height = disp->height; + disp->mouse_x = disp->mouse_y = -1; + disp->wheel = 0; + std::memset((void*)disp->buttons,0,256*sizeof(unsigned int)); + std::memset((void*)disp->keys,0,256*sizeof(unsigned int)); + std::memset((void*)disp->released_keys,0,256*sizeof(unsigned int)); + disp->is_resized = disp->is_moved = disp->is_event = false; + if (disp->events) { +#ifdef _WIN64 + SetWindowLongPtr(disp->window,GWLP_USERDATA,(LONG_PTR)disp); + SetWindowLongPtr(disp->window,GWLP_WNDPROC,(LONG_PTR)_handle_events); +#else + SetWindowLong(disp->window,GWL_USERDATA,(LONG)disp); + SetWindowLong(disp->window,GWL_WNDPROC,(LONG)_handle_events); +#endif + SetEvent(disp->created); + while( GetMessage(&msg,0,0,0) ) DispatchMessage( &msg ); + } + return 0; + } + + void _init_fullscreen() { + background_window = 0; + if (is_fullscreen && !is_closed) { + DEVMODE mode; + unsigned int imode=0, ibest=0, bestbpp=0, bw=~0U, bh=~0U; + for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); imode++) { + const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight; + if (nw>=width && nh>=height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) { + bestbpp = mode.dmBitsPerPel; + ibest = imode; + bw = nw; bh = nh; + } + } + if (bestbpp) { + curr_mode.dmSize = sizeof(DEVMODE); curr_mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&curr_mode); + EnumDisplaySettings(0,ibest,&mode); + ChangeDisplaySettings(&mode,0); + } else curr_mode.dmSize = 0; + + const unsigned int sx = screen_dimx(), sy = screen_dimy(); + if (sx!=width || sy!=height) { + CLIENTCREATESTRUCT background_ccs; + background_window = CreateWindowA("MDICLIENT"," ",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs); + SetForegroundWindow(background_window); + } + } else curr_mode.dmSize = 0; + } + + void _desinit_fullscreen() { + if (is_fullscreen) { + if (background_window) DestroyWindow(background_window); + background_window = 0; + if (curr_mode.dmSize) ChangeDisplaySettings(&curr_mode,0); + is_fullscreen = false; + } + } + +#endif + + }; + + /* + #-------------------------------------- + # + # + # + # Definition of the CImg structure + # + # + # + #-------------------------------------- + */ + + //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T. + /** + This is the main class of the %CImg Library. It declares and constructs + an image, allows access to its pixel values, and is able to perform various image operations. + + \par Image representation + + A %CImg image is defined as an instance of the container \ref CImg, which contains a regular grid of pixels, + each pixel value being of type \c T. The image grid can have up to 4 dimensions : width, height, depth + and number of channels. + Usually, the three first dimensions are used to describe spatial coordinates (x,y,z), while the number of channels + is rather used as a vector-valued dimension (it may describe the R,G,B color channels for instance). + If you need a fifth dimension, you can use image lists \ref CImgList rather than simple images \ref CImg. + + Thus, the \ref CImg class is able to represent volumetric images of vector-valued pixels, + as well as images with less dimensions (1D scalar signal, 2D color images, ...). + Most member functions of the class CImg are designed to handle this maximum case of (3+1) dimensions. + + Concerning the pixel value type \c T : + fully supported template types are the basic C++ types : unsigned char, char, short, unsigned int, int, + unsigned long, long, float, double, ... . + Typically, fast image display can be done using CImg images, + while complex image processing algorithms may be rather coded using CImg or CImg + images that have floating-point pixel values. The default value for the template T is \c float. + Using your own template types may be possible. However, you will certainly have to define the complete set + of arithmetic and logical operators for your class. + + \par Image structure + + The \ref CImg<\c T> structure contains \a five fields : + - \ref width defines the number of \a columns of the image (size along the X-axis). + - \ref height defines the number of \a rows of the image (size along the Y-axis). + - \ref depth defines the number of \a slices of the image (size along the Z-axis). + - \ref dim defines the number of \a channels of the image (size along the V-axis). + - \ref data defines a \a pointer to the \a pixel \a data (of type \c T). + + You can access these fields publicly although it is recommended to use the dedicated functions + dimx(), dimy(), dimz(), dimv() and ptr() to do so. + Image dimensions are not limited to a specific range (as long as you got enough available memory). + A value of \e 1 usually means that the corresponding dimension is \a flat. + If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty. + Empty images should not contain any pixel data and thus, will not be processed by CImg member functions + (a CImgInstanceException will be thrown instead). + Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage). + + \par Image declaration and construction + + Declaring an image can be done by using one of the several available constructors. + Here is a list of the most used : + + - Construct images from arbitrary dimensions : + - CImg img; declares an empty image. + - CImg img(128,128); declares a 128x128 greyscale image with + \c unsigned \c char pixel values. + - CImg img(3,3); declares a 3x3 matrix with \c double coefficients. + - CImg img(256,256,1,3); declares a 256x256x1x3 (color) image + (colors are stored as an image with three channels). + - CImg img(128,128,128); declares a 128x128x128 volumetric and greyscale image + (with \c double pixel values). + - CImg<> img(128,128,128,3); declares a 128x128x128 volumetric color image + (with \c float pixels, which is the default value of the template parameter \c T). + - \b Note : images pixels are not automatically initialized to 0. You may use the function \ref fill() to + do it, or use the specific constructor taking 5 parameters like this : + CImg<> img(128,128,128,3,0); declares a 128x128x128 volumetric color image with all pixel values to 0. + + - Construct images from filenames : + - CImg img("image.jpg"); reads a JPEG color image from the file "image.jpg". + - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr". + - \b Note : You need to install ImageMagick + to be able to read common compressed image formats (JPG,PNG,...) (See \ref cimg_files_io). + + - Construct images from C-style arrays : + - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer + \c data_buffer (of size 256x256=65536). + - CImg img(data_buffer,256,256,1,3,false); constructs a 256x256 color image + from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). + - CImg img(data_buffer,256,256,1,3,true); constructs a 256x256 color image + from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed). + + The complete list of constructors can be found here. + + \par Most useful functions + + The \ref CImg class contains a lot of functions that operates on images. + Some of the most useful are : + + - operator()(), operator[]() : allows to access or write pixel values. + - display() : displays the image in a new window. + + \sa CImgList, CImgStats, CImgDisplay, CImgException. + + **/ + template struct CImg { + + //! Variable representing the width of the instance image (i.e. dimensions along the X-axis). + /** + \remark + - Prefer using the function CImg::dimx() to get information about the width of an image. + - Use function CImg::resize() to set a new width for an image. Setting directly the variable \c width would probably + result in a library crash. + - Empty images have \c width defined to \c 0. + **/ + unsigned int width; + + //! Variable representing the height of the instance image (i.e. dimensions along the Y-axis). + /** + \remark + - Prefer using the function CImg::dimy() to get information about the height of an image. + - Use function CImg::resize() to set a new height for an image. Setting directly the variable \c height would probably + result in a library crash. + - 1D signals have \c height defined to \c 1. + - Empty images have \c height defined to \c 0. + **/ + unsigned int height; + + //! Variable representing the depth of the instance image (i.e. dimensions along the Z-axis). + /** + \remark + - Prefer using the function CImg::dimz() to get information about the depth of an image. + - Use function CImg::resize() to set a new depth for an image. Setting directly the variable \c depth would probably + result in a library crash. + - Classical 2D images have \c depth defined to \c 1. + - Empty images have \c depth defined to \c 0. + **/ + unsigned int depth; + + //! Variable representing the number of channels of the instance image (i.e. dimensions along the V-axis). + /** + \remark + - Prefer using the function CImg::dimv() to get information about the depth of an image. + - Use function CImg::resize() to set a new vector dimension for an image. Setting directly the variable \c dim would probably + result in a library crash. + - Scalar-valued images (one value per pixel) have \c dim defined to \c 1. + - Empty images have \c depth defined to \c 0. + **/ + unsigned int dim; + + //! Variable telling if pixel buffer of the instance image is shared with another one. + bool is_shared; + + //! Pointer to the first pixel of the pixel buffer. + T *data; + + //! Iterator type for CImg. + /** + \remark + - An \p iterator is a T* pointer (address of a pixel value in the pixel buffer). + - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL. + **/ + typedef T* iterator; + + //! Const iterator type for CImg. + /** + \remark + - A \p const_iterator is a const T* pointer (address of a pixel value in the pixel buffer). + - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL. + **/ + typedef const T* const_iterator; + + //! Get value type + typedef T value_type; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimg_plugin +#include cimg_plugin +#endif + //@} + + //-------------------------------------- + // + //! \name Constructors-Destructor-Copy + //@{ + //-------------------------------------- + + //! Default constructor. + /** + The default constructor creates an empty instance image. + \remark + - An empty image does not contain any data and has all of its dimensions \ref width, \ref height, \ref depth, \ref dim + set to 0 as well as its pointer to the pixel buffer \ref data. + - An empty image is non-shared. + \see ~CImg(), assign(), is_empty(). + **/ + CImg(): + width(0),height(0),depth(0),dim(0),is_shared(false),data(0) {} + + //! Destructor. + /** + The destructor destroys the instance image. + \remark + - Destructing an empty or shared image does nothing. + - Otherwise, all memory used to store the pixel data of the instance image is freed. + - When destroying a non-shared image, be sure that every shared instances of the same image are + also destroyed to avoid further access to desallocated memory buffers. + \see CImg(), assign(), is_empty(). + **/ + ~CImg() { + if (data && !is_shared) delete[] data; + } + + //! In-place version of the default constructor. + /** + This function replaces the instance image by an empty image. + \remark + - Memory used by the previous content of the instance image is freed if necessary. + - If the instance image was initially shared, it is replaced by a (non-shared) empty image. + - This function is useful to free memory used by an image that is not of use, but which + has been created in the current code scope (i.e. not destroyed yet). + \see ~CImg(), assign(), is_empty(). + **/ + CImg& assign() { + if (data && !is_shared) delete[] data; + width = height = depth = dim = 0; is_shared = false; data = 0; + return *this; + } + + //! In-place version of the default constructor. + /** + This function is strictly equivalent to \ref assign() and has been + introduced for having a STL-compliant function name. + \see assign(). + **/ + CImg& clear() { + return assign(); + } + + //! Default copy constructor. + /** + The default copy constructor creates a new instance image having same dimensions + (\ref width, \ref height, \ref depth, \ref dim) and same pixel values as the input image \p img. + \param img The input image to copy. + \remark + - If the input image \p img is non-shared or have a different template type \p t != \p T, + the default copy constructor allocates a new pixel buffer and copy the pixel data + of \p img into it. In this case, the pointers \ref data to the pixel buffers of the two images are different + and the resulting instance image is non-shared. + - If the input image \p img is shared and has the same template type \p t == \p T, + the default copy constructor does not allocate a new pixel buffer and the resulting instance image + shares its pixel buffer with the input image \p img, which means that modifying pixels of \p img also modifies + the created instance image. + - Copying an image having a different template type \p t != \p T performs a crude static cast conversion of each pixel value from + type \p t to type \p T. + - Copying an image having the same template type \p t == \p T is significantly faster. + \see assign(const CImg< t >&), CImg(const CImg< t >&,const bool). + **/ + template CImg(const CImg& img):is_shared(false) { + const unsigned int siz = img.size(); + if (img.data && siz) { + width = img.width; height = img.height; depth = img.depth; dim = img.dim; data = new T[siz]; + const t *ptrs = img.data+siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + } else { width = height = depth = dim = 0; data = 0; } + } + + CImg(const CImg& img) { + const unsigned int siz = img.size(); + if (img.data && siz) { + width = img.width; height = img.height; depth = img.depth; dim = img.dim; is_shared = img.is_shared; + if (is_shared) data = const_cast(img.data); + else { data = new T[siz]; std::memcpy(data,img.data,siz*sizeof(T)); } + } else { width = height = depth = dim = 0; is_shared = false; data = 0; } + } + + //! In-place version of the default copy constructor. + /** + This function assigns a copy of the input image \p img to the current instance image. + \param img The input image to copy. + \remark + - If the instance image is not shared, the content of the input image \p img is copied into a new buffer + becoming the new pixel buffer of the instance image, while the old pixel buffer is freed if necessary. + - If the instance image is shared, the content of the input image \p img is copied into the current (shared) pixel buffer + of the instance image, modifying then the image referenced by the shared instance image. The instance image still remains shared. + \see CImg(const CImg< t >&), operator=(const CImg< t >&). + **/ + template CImg& assign(const CImg& img) { + return assign(img.data,img.width,img.height,img.depth,img.dim); + } + + //! Advanced copy constructor. + /** + The advanced copy constructor - as the default constructor CImg(const CImg< t >&) - creates a new instance image having same dimensions + \ref width, \ref height, \ref depth, \ref dim and same pixel values as the input image \p img. + But it also decides if the created instance image shares its memory with the input image \p img (if the input parameter + \p shared is set to \p true) or not (if the input parameter \p shared is set to \p false). + \param img The input image to copy. + \param shared Boolean flag that decides if the copy is shared on non-shared. + \remark + - It is not possible to create a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T. + - If a non-shared copy of the input image \p img is created, a new memory buffer is allocated for pixel data. + - If a shared copy of the input image \p img is created, no extra memory is allocated and the pixel buffer of the instance + image is the same as the one used by the input image \p img. + \see CImg(const CImg< t >&), assign(const CImg< t >&,const bool). + **/ + template CImg(const CImg& img, const bool shared):is_shared(false) { + if (shared) throw CImgArgumentException("CImg<%s>::CImg() : Cannot construct a shared copy from a CImg<%s> image " + "(different pixel types).",pixel_type(),CImg::pixel_type()); + const unsigned int siz = img.size(); + if (img.data && siz) { + width = img.width; height = img.height; depth = img.depth; dim = img.dim; data = new T[siz]; + const t *ptrs = img.data+siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + } else { width = height = depth = dim = 0; data = 0; } + } + + CImg(const CImg& img, const bool shared) { + const unsigned int siz = img.size(); + if (img.data && siz) { + width = img.width; height = img.height; depth = img.depth; dim = img.dim; is_shared = shared; + if (is_shared) data = const_cast(img.data); + else { data = new T[siz]; std::memcpy(data,img.data,siz*sizeof(T)); } + } else { width = height = depth = dim = 0; is_shared = false; data = 0; } + } + + //! In-place version of the advanced constructor. + /** + This function - as the simpler function assign(const CImg< t >&) - assigns a copy of the input image \p img to the + current instance image. But it also decides if the copy is shared (if the input parameter \p shared is set to \true) or non-shared + (if the input parameter \p shared is set to false). + \param img The input image to copy. + \param shared Boolean flag that decides if the copy is shared or non-shared. + \remark + - It is not possible to assign a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T. + - If a non-shared copy of the input image \p img is assigned, a new memory buffer is allocated for pixel data. + - If a shared copy of the input image \p img is assigned, no extra memory is allocated and the pixel buffer of the instance + image is the same as the one used by the input image \p img. + \see CImg(const CImg< t >&,const bool), assign(const CImg< t >&). + **/ + template CImg& assign(const CImg& img, const bool shared) { + return assign(img.data,img.width,img.height,img.depth,img.dim,shared); + } + + //! Constructs a new image with given size (\p dx,\p dy,\p dz,\p dv). + /** + This constructors create an instance image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T. + \param dx Desired size along the X-axis, i.e. the \ref width of the image. + \param dy Desired size along the Y-axis, i.e. the \ref height of the image. + \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. + \param dv Desired size along the V-axis, i.e. the number of image channels \ref dim. + \remark + - If one of the input dimension \p dx,\p dy,\p dz or \p dv is set to 0, the created image is empty + and all has its dimensions set to 0. No memory for pixel data is then allocated. + - This constructor creates only non-shared images. + - Image pixels allocated by this constructor are \b not \b initialized. + Use the constructor CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T&) + to get an image of desired size with pixels set to a particular value. + \see assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int), + CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T&). + **/ + explicit CImg(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1): + is_shared(false) { + const unsigned int siz = dx*dy*dz*dv; + if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; } + else { width = height = depth = dim = 0; data = 0; } + } + + //! In-place version of the previous constructor. + /** + This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T. + \param dx Desired size along the X-axis, i.e. the \ref width of the image. + \param dy Desired size along the Y-axis, i.e. the \ref height of the image. + \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. + \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. + - If one of the input dimension \p dx,\p dy,\p dz or \p dv is set to 0, the instance image becomes empty + and all has its dimensions set to 0. No memory for pixel data is then allocated. + - Memory buffer used to store previous pixel values is freed if necessary. + - If the instance image is shared, this constructor actually does nothing more than verifying + that new and old image dimensions fit. + - Image pixels allocated by this function are \b not \b initialized. + Use the function assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T&) + to assign an image of desired size with pixels set to a particular value. + \see CImg(), assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int). + **/ + CImg& assign(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { + const unsigned long siz = dx*dy*dz*dv, curr_siz = size(); + if (is_shared) { + if (siz!=curr_siz) + throw CImgArgumentException("CImg<%s>::assign() : Cannot assign image (%u,%u,%u,%u) to shared instance image (%u,%u,%u,%u,%p).", + pixel_type(),dx,dy,dz,dv,width,height,depth,dim,data); + } else { + if (siz) { + if (siz!=curr_siz) { if (data) delete[] data; data = new T[siz]; } + width = dx; height = dy; depth = dz; dim = dv; + } else { + if (data) delete[] data; + width = height = depth = dim = 0; data = 0; + } + } + return *this; + } + + //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with pixel having a default value \p val. + /** + This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T and sets all pixel + values of the created instance image to \p val. + \param dx Desired size along the X-axis, i.e. the \ref width of the image. + \param dy Desired size along the Y-axis, i.e. the \ref height of the image. + \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. + \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. + \param val Default value for image pixels. + \remark + - This constructor has the same properties as CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int). + \see CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int). + **/ + CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const T& val): + is_shared(false) { + const unsigned int siz = dx*dy*dz*dv; + if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; fill(val); } + else { width = height = depth = dim = 0; data = 0; } + } + + //! In-place version of the previous constructor. + /** + This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T + and sets all pixel values of the instance image to \p val. + \param dx Desired size along the X-axis, i.e. the \ref width of the image. + \param dy Desired size along the Y-axis, i.e. the \ref height of the image. + \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. + \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. + \param val Default value for image pixels. + \remark + - This function has the same properties as assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int). + \see assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int). + **/ + CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const T& val) { + return assign(dx,dy,dz,dv).fill(val); + } + + //! Construct an image from an image file. + /** + This constructor creates an instance image by reading it from a file. + \param filename Filename of the image file. + \remark + - The image format is deduced from the filename only by looking for the filename extension i.e. without + analyzing the file itself. + - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with. + More informations on this topic can be found in cimg_files_io. + - If the filename is not found, a CImgIOException is thrown by this constructor. + \see assign(const char *const), load(const char *const) + **/ + CImg(const char *const filename):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { + assign(filename); + } + + //! In-place version of the previous constructor. + /** + This function replaces the instance image by the one that have been read from the given file. + \param filename Filename of the image file. + - The image format is deduced from the filename only by looking for the filename extension i.e. without + analyzing the file itself. + - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with. + More informations on this topic can be found in cimg_files_io. + - If the filename is not found, a CImgIOException is thrown by this constructor. + **/ + CImg& assign(const char *const filename) { + return load(filename); + } + + //! Construct an image from raw memory buffer. + /** + This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dv) and fill its pixel buffer by + copying data values from the input raw pixel buffer \p data_buffer. + **/ + template CImg(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1, const bool shared=false):is_shared(false) { + if (shared) throw CImgArgumentException("CImg<%s>::CImg() : Cannot construct a shared copy from a (%s*) buffer " + "(different pixel types).",pixel_type(),CImg::pixel_type()); + const unsigned int siz = dx*dy*dz*dv; + if (data_buffer && siz) { + width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; + const t *ptrs = data_buffer+siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + } else { width = height = depth = dim = 0; data = 0; } + } + +#ifdef cimg_use_visualcpp6 + CImg(const T *const data_buffer, const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dv, const bool shared) { +#else + CImg(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1, const bool shared=false) { +#endif + const unsigned int siz = dx*dy*dz*dv; + if (data_buffer && siz) { + width = dx; height = dy; depth = dz; dim = dv; is_shared = shared; + if (is_shared) data = const_cast(data_buffer); + else { data = new T[siz]; std::memcpy(data,data_buffer,siz*sizeof(T)); } + } else { width = height = depth = dim = 0; is_shared = false; data = 0; } + } + + template CImg(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1) { + + } + + //! In-place version of the previous constructor. +#ifdef cimg_use_visualcpp6 + template CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dv) { +#else + template CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1) { +#endif + assign(dx,dy,dz,dv); + const unsigned int siz = dx*dy*dz*dv; + if (data_buffer && siz) { const t *ptrs = data_buffer+siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); } + else { width = height = depth = dim = 0; is_shared = false; data = 0; } + return *this; + } + + CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1) { + assign(dx,dy,dz,dv); + const unsigned int siz = dx*dy*dz*dv; + if (data_buffer && siz) std::memcpy(data,data_buffer,siz*sizeof(T)); + else { width = height = depth = dim = 0; is_shared = false; data = 0; } + return *this; + } + + //! In-place version of the previous constructor, allowing to force the shared state of the instance image. + template CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dv, const bool shared) { + if (shared) throw CImgArgumentException("CImg<%s>::assign() : Cannot define a shared copy from a CImg<%s> image " + "(different pixel types).",pixel_type(),CImg::pixel_type()); + if (data && !is_shared) delete[] data; + is_shared = false; + const unsigned int siz = dx*dy*dz*dv; + if (data_buffer && siz) { + width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; + const t *ptrs = data_buffer+siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + } else { width = height = depth = dim = 0; data = 0; } + return *this; + } + + CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dv, const bool shared) { + if (data && !is_shared) delete[] data; + const unsigned int siz = dx*dy*dz*dv; + if (data_buffer && siz) { + width = dx; height = dy; depth = dz; dim = dv; is_shared = shared; + if (is_shared) data = const_cast(data_buffer); + else { data = new T[siz]; std::memcpy(data,data_buffer,siz*sizeof(T)); } + } else { width = height = depth = dim = 0; is_shared = false; data = 0; } + return *this; + } + + // INNER ROUTINE : Swap fields of an image (use it carefully!) + // If one of the image is shared, its content is replaced by the non-shared image (which remains unchanged). + CImg& swap(CImg& img) { + if (img.is_shared==is_shared) { + cimg::swap(width,img.width); + cimg::swap(height,img.height); + cimg::swap(depth,img.depth); + cimg::swap(dim,img.dim); + cimg::swap(data,img.data); + } else { + if (img.is_shared) img.assign(*this); + else assign(img); + } + return img; + } + + //@} + //------------------------------------- + // + //! \name Image Informations + //@{ + //------------------------------------- + + //! Return the type of the pixel values. + /** + \return a string describing the type of the image pixels (template parameter \p T). + - The string returned may contains spaces ("unsigned char"). + - If the template parameter T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::id(); + } + + //! Return the total number of pixel values in an image. + /** + - Equivalent to : dimx() * dimy() * dimz() * dimv(). + + \par example: + \code + CImg<> img(100,100,1,3); + if (img.size()==100*100*3) std::fprintf(stderr,"This statement is true"); + \endcode + \sa dimx(), dimy(), dimz(), dimv() + **/ + unsigned long size() const { + return width*height*depth*dim; + } + + //! Return the number of columns of the instance image (size along the X-axis, i.e image width). + /** + \sa width, dimy(), dimz(), dimv(), size(). + **/ + int dimx() const { + return (int)width; + } + + //! Return the number of rows of the instance image (size along the Y-axis, i.e image height). + /** + \sa height, dimx(), dimz(), dimv(), size(). + **/ + int dimy() const { + return (int)height; + } + + //! Return the number of slices of the instance image (size along the Z-axis). + /** + \sa depth, dimx(), dimy(), dimv(), size(). + **/ + int dimz() const { + return (int)depth; + } + + //! Return the number of vector channels of the instance image (size along the V-axis). + /** + \sa dim, dimx(), dimy(), dimz(), size(). + **/ + int dimv() const { + return (int)dim; + } + + //! Return \c true if images \c (*this) and \c img have same width. + template bool is_sameX(const CImg& img) const { + return (width==img.width); + } + + //! Return \c true if images \c (*this) and the display \c disp have same width. + bool is_sameX(const CImgDisplay& disp) const { + return (width==disp.width); + } + + //! Return \c true if images \c (*this) and \c img have same height. + template bool is_sameY(const CImg& img) const { + return (height==img.height); + } + + //! Return \c true if images \c (*this) and the display \c disp have same height. + bool is_sameY(const CImgDisplay& disp) const { + return (height==disp.height); + } + + //! Return \c true if images \c (*this) and \c img have same depth. + template bool is_sameZ(const CImg& img) const { + return (depth==img.depth); + } + + //! Return \c true if images \c (*this) and \c img have same dim. + template bool is_sameV(const CImg& img) const { + return (dim==img.dim); + } + + //! Return \c true if images have same width and same height. + template bool is_sameXY(const CImg& img) const { + return (is_sameX(img) && is_sameY(img)); + } + + //! Return \c true if image \c (*this) and the display \c disp have same width and same height. + bool is_sameXY(const CImgDisplay& disp) const { + return (is_sameX(disp) && is_sameY(disp)); + } + + //! Return \c true if images have same width, same height and same depth. + template bool is_sameXYZ(const CImg& img) const { + return (is_sameXY(img) && is_sameZ(img)); + } + + //! Return \c true if images \c (*this) and \c img have same width, same height, same depth and same number of channels. + template bool is_sameXYZV(const CImg& img) const { + return (is_sameXYZ(img) && is_sameV(img)); + } + + //! Return \c true if pixel (x,y,z,v) is inside the image boundaries. + bool contains(const int x, const int y=0, const int z=0, const int v=0) const { + return data && x>=0 && x<(int)width && y>=0 && y<(int)height && z>=0 && z<(int)depth && v>=0 && v<(int)dim; + } + + //! Return \c true if current image is empty. + bool is_empty() const { + return !(data && width && height && depth && dim); + } + + //! Return the offset of the pixel coordinates (\p x,\p y,\p z,\p v) with respect to the data pointer \c data. + /** + \param x X-coordinate of the pixel. + \param y Y-coordinate of the pixel. + \param z Z-coordinate of the pixel. + \param v V-coordinate of the pixel. + + - No checking is done on the validity of the given coordinates. + + \par example: + \code + CImg img(100,100,1,3,0); // Define a 100x100 color image with float-valued black pixels. + long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10). + float val = img[off]; // Get the blue value of the pixel. + \endcode + \sa ptr(), operator()(), operator[](), cimg_storage. + **/ + long offset(const int x=0, const int y=0, const int z=0, const int v=0) const { + return x + y*width + z*width*height + v*width*height*depth; + } + + //! Return a pointer to the pixel value located at (\p x,\p y,\p z,\p v). + /** + \param x X-coordinate of the pixel. + \param y Y-coordinate of the pixel. + \param z Z-coordinate of the pixel. + \param v V-coordinate of the pixel. + + - When called without parameters, ptr() returns a pointer to the begining of the pixel buffer. + - If the macro \c cimg_debug == 3, boundary checking is performed and warning messages may appear if + given coordinates are outside the image range (but function performances decrease). + + \par example: + \code + CImg img(100,100,1,1,0); // Define a 100x100 greyscale image with float-valued pixels. + float *ptr = ptr(10,10); // Get a pointer to the pixel located at (10,10). + float val = *ptr; // Get the pixel value. + \endcode + \sa data, offset(), operator()(), operator[](), cimg_storage, cimg_environment. + **/ + T* ptr(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { + const long off = offset(x,y,z,v); +#if cimg_debug>=3 + if (off<0 || off>=(long)size()) { + cimg::warn(true,"CImg<%s>::ptr() : Asked for a pointer at coordinates (%u,%u,%u,%u) (offset=%u), " + "outside image range (%u,%u,%u,%u) (size=%u)", + pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); + return data; + } +#endif + return data+off; + } + + const T* ptr(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { + const long off = offset(x,y,z,v); +#if cimg_debug>=3 + if (off<0 || off>=(long)size()) { + cimg::warn(true,"CImg<%s>::ptr() : Trying to get a pointer at (%u,%u,%u,%u) (offset=%d) which is" + "outside the data of the image (%u,%u,%u,%u) (size=%u)", + pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); + return data; + } +#endif + return data+off; + } + + //! Return an iterator to the first image pixel + iterator begin() { + return data; + } + + const_iterator begin() const { + return data; + } + + //! Return an iterator to the last image pixel + iterator end() { + return data + size(); + } + + const_iterator end() const { + return data + size(); + } + + //! Fast access to pixel value for reading or writing. + /** + \param x X-coordinate of the pixel. + \param y Y-coordinate of the pixel. + \param z Z-coordinate of the pixel. + \param v V-coordinate of the pixel. + + - If one image dimension is equal to 1, it can be omitted in the coordinate list (see example below). + - If the macro \c cimg_debug == 3, boundary checking is performed and warning messages may appear + (but function performances decrease). + + \par example: + \code + CImg img(100,100,1,3,0); // Define a 100x100 color image with float-valued black pixels. + const float valR = img(10,10,0,0); // Read the red component at coordinates (10,10). + const float valG = img(10,10,0,1); // Read the green component at coordinates (10,10) + const float valB = img(10,10,2); // Read the blue component at coordinates (10,10) (Z-coordinate omitted here). + const float avg = (valR + valG + valB)/3; // Compute average pixel value. + img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the pixel (10,10) by the average grey value. + \endcode + + \sa operator[](), ptr(), offset(), cimg_storage, cimg_environment. + **/ + T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { + const long off = offset(x,y,z,v); +#if cimg_debug>=3 + if (!data || off>=(long)size()) { + cimg::warn(true,"CImg<%s>::operator() : Pixel access requested at (%u,%u,%u,%u) (offset=%d) " + "outside the image range (%u,%u,%u,%u) (size=%u)", + pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); + return *data; + } +#endif + return data[off]; + } + + const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { + const long off = offset(x,y,z,v); +#if cimg_debug>=3 + if (!data || off>=(long)size()) { + cimg::warn(true,"CImg<%s>::operator() : Pixel access requested at (%u,%u,%u,%u) (offset=%d) " + "outside the image range (%u,%u,%u,%u) (size=%u)", + pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); + return *data; + } +#endif + return data[off]; + } + + //! Return pixel value at a given position. Equivalent to operator(). + T& at(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { + const long off = offset(x,y,z,v); + if (!data || off>=(long)size()) + throw CImgArgumentException("CImg<%s>::at() : Pixel access requested at (%u,%u,%u,%u) (offset=%d) " + "outside the image range (%u,%u,%u,%u) (size=%u)", + pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); + return data[off]; + } + + const T& at(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { + const long off = offset(x,y,z,v); + if (!data || off>=(long)size()) + throw CImgArgumentException("CImg<%s>::at() : Pixel access requested at (%u,%u,%u,%u) (offset=%d) " + "outside the image range (%u,%u,%u,%u) (size=%u)", + pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); + return data[off]; + } + + //! Fast access to pixel value for reading or writing, using an offset to the image pixel. + /** + \param off Offset of the pixel according to the begining of the pixel buffer, given by ptr(). + + - If the macro \c cimg_debug==3, boundary checking is performed and warning messages may appear + (but function performances decrease). + - As pixel values are aligned in memory, this operator can sometime useful to access values easier than + with operator()() (see example below). + + \par example: + \code + CImg vec(1,10); // Define a vector of float values (10 lines, 1 row). + const float val1 = vec(0,4); // Get the fifth element using operator()(). + const float val2 = vec[4]; // Get the fifth element using operator[]. Here, val2==val1. + \endcode + + \sa operator()(), ptr(), offset(), cimg_storage, cimg_environment. + **/ + T& operator[](const unsigned long off) { + return operator()(off); + } + + const T& operator[](const unsigned long off) const { + return operator()(off); + } + + //! Return a reference to the last image value + T& back() { + return operator()(size()-1); + } + + const T& back() const { + return operator()(size()-1); + } + + //! Return a reference to the first image value + T& front() { + return *data; + } + + const T& front() const { + return *data; + } + + //! Read a pixel value with Dirichlet or Neumann boundary conditions. + /** + \param x X-coordinate of the pixel. + \param y Y-coordinate of the pixel. + \param z Z-coordinate of the pixel. + \param v V-coordinate of the pixel. + \param out_val Desired value if pixel coordinates are outside the image range (optional parameter). + + - This function allows to read pixel values with boundary checking on all coordinates. + - If given coordinates are outside the image range and the parameter out_val is specified, the value \c out_val is returned. + - If given coordinates are outside the image range and the parameter out_val is not specified, the closest pixel value + is returned. + + \par example: + \code + CImg img(100,100,1,1,128); // Define a 100x100 images with all pixel values equal to 128. + const float val1 = img.pix4d(10,10,0,0,0); // Equivalent to val1=img(10,10) (but slower). + const float val2 = img.pix4d(-4,5,0,0,0); // Return 0, since coordinates are outside the image range. + const float val3 = img.pix4d(10,10,5,0,64); // Return 64, since coordinates are outside the image range. + \endcode + + \sa operator()(), linear_pix4d(), cubic_pix2d(). + **/ + T pix4d(const int x, const int y, const int z, const int v, const T& out_val) const { + return (x<0 || y<0 || z<0 || v<0 || x>=dimx() || y>=dimy() || z>=dimz() || v>=dimv())?out_val:(*this)(x,y,z,v); + } + + T pix4d(const int x, const int y, const int z, const int v) const { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y), + z<0?0:(z>=dimz()?dimz()-1:z), v<0?0:(v>=dimv()?dimv()-1:v)); + } + + //! Read a pixel value with Dirichlet or Neumann boundary conditions for the three first coordinates (\c x,\c y,\c z). + T pix3d(const int x, const int y, const int z, const int v, const T& out_val) const { + return (x<0 || y<0 || z<0 || x>=dimx() || y>=dimy() || z>=dimz())?out_val:(*this)(x,y,z,v); + } + + const T& pix3d(const int x, const int y, const int z, const int v=0) const { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y), + z<0?0:(z>=dimz()?dimz()-1:z),v); + } + + //! Read a pixel value with Dirichlet or Neumann boundary conditions for the two first coordinates (\c x,\c y). + T pix2d(const int x, const int y, const int z, const int v, const T& out_val) const { + return (x<0 || y<0 || x>=dimx() || y>=dimy())?out_val:(*this)(x,y,z,v); + } + + const T& pix2d(const int x,const int y,const int z=0,const int v=0) const { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y),z,v); + } + + //! Read a pixel value with Dirichlet or Neumann boundary conditions for the first coordinate \c x. + T pix1d(const int x, const int y, const int z, const int v, const T& out_val) const { + return (x<0 || x>=dimx())?out_val:(*this)(x,y,z,v); + } + + const T& pix1d(const int x, const int y=0, const int z=0, const int v=0) const { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y,z,v); + } + + //! Read a pixel value using linear interpolation. + /** + \param ffx X-coordinate of the pixel (float-valued). + \param ffy Y-coordinate of the pixel (float-valued). + \param ffz Z-coordinate of the pixel (float-valued). + \param ffv V-coordinate of the pixel (float-valued). + \param out_val Out-of-border pixel value + + - This function allows to read pixel values with boundary checking on all coordinates. + - If given coordinates are outside the image range, the value of the nearest pixel inside the image is returned + (Neumann boundary conditions). + - If given coordinates are float-valued, a linear interpolation is performed in order to compute the returned value. + + \par example: + \code + CImg img(2,2); // Define a greyscale 2x2 image. + img(0,0) = 0; // Fill image with specified pixel values. + img(1,0) = 1; + img(0,1) = 2; + img(1,1) = 3; + const double val = img.linear_pix4d(0.5,0.5); // Return val=1.5, which is the average intensity of the four pixels values. + \endcode + + \sa operator()(), linear_pix3d(), linear_pix2d(), linear_pix1d(), cubic_pix2d(). + **/ + typename cimg::largest::type linear_pix4d(const float fx,const float fy,const float fz,const float fv, + const T& out_val) const { + const int x = (int)fx-(fx>=0?0:1), y = (int)fy-(fy>=0?0:1), z = (int)fz-(fz>=0?0:1), v = (int)fv-(fv>=0?0:1), + nx = x+1, ny = y+1, nz = z+1, nv = v+1; + const float dx = fx-x, dy = fy-y, dz = fz-z, dv = fv-v; + const T + Icccc = pix4d(x,y,z,v,out_val), Inccc = pix4d(nx,y,z,v,out_val), + Icncc = pix4d(x,ny,z,v,out_val), Inncc = pix4d(nx,ny,z,v,out_val), + Iccnc = pix4d(x,y,nz,v,out_val), Incnc = pix4d(nx,y,nz,v,out_val), + Icnnc = pix4d(x,ny,nz,v,out_val), Innnc = pix4d(nx,ny,nz,v,out_val), + Icccn = pix4d(x,y,z,nv,out_val), Inccn = pix4d(nx,y,z,nv,out_val), + Icncn = pix4d(x,ny,z,nv,out_val), Inncn = pix4d(nx,ny,z,nv,out_val), + Iccnn = pix4d(x,y,nz,nv,out_val), Incnn = pix4d(nx,y,nz,nv,out_val), + Icnnn = pix4d(x,ny,nz,nv,out_val), Innnn = pix4d(nx,ny,nz,nv,out_val); + return Icccc + + dx*(Inccc-Icccc + + dy*(Icccc+Inncc-Icncc-Inccc + + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + + dv*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + + dv*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + + dz*(Icccc+Incnc-Iccnc-Inccc + + dv*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + + dv*(Icccc+Inccn-Inccc-Icccn)) + + dy*(Icncc-Icccc + + dz*(Icccc+Icnnc-Iccnc-Icncc + + dv*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + + dv*(Icccc+Icncn-Icncc-Icccn)) + + dz*(Iccnc-Icccc + + dv*(Icccc+Iccnn-Iccnc-Icccn)) + + dv*(Icccn-Icccc); + } + + typename cimg::largest::type linear_pix4d(const float ffx,const float ffy=0,const float ffz=0,const float ffv=0) const { + const float + fx = ffx<0?0:(ffx>width-1?width-1:ffx), fy = ffy<0?0:(ffy>height-1?height-1:ffy), + fz = ffz<0?0:(ffz>depth-1?depth-1:ffz), fv = ffv<0?0:(ffv>dim-1?dim-1:ffv); + const unsigned int x = (unsigned int)fx, y = (unsigned int)fy, z = (unsigned int)fz, v = (unsigned int)fv; + const float dx = fx-x, dy = fy-y, dz = fz-z, dv = fv-v; + const unsigned int nx = dx>0?x+1:x, ny = dy>0?y+1:y, nz = dz>0?z+1:z, nv = dv>0?v+1:v; + const T + &Icccc = (*this)(x,y,z,v), &Inccc = (*this)(nx,y,z,v), &Icncc = (*this)(x,ny,z,v), &Inncc = (*this)(nx,ny,z,v), + &Iccnc = (*this)(x,y,nz,v), &Incnc = (*this)(nx,y,nz,v), &Icnnc = (*this)(x,ny,nz,v), &Innnc = (*this)(nx,ny,nz,v), + &Icccn = (*this)(x,y,z,nv), &Inccn = (*this)(nx,y,z,nv), &Icncn = (*this)(x,ny,z,nv), &Inncn = (*this)(nx,ny,z,nv), + &Iccnn = (*this)(x,y,nz,nv), &Incnn = (*this)(nx,y,nz,nv), &Icnnn = (*this)(x,ny,nz,nv), &Innnn = (*this)(nx,ny,nz,nv); + return Icccc + + dx*(Inccc-Icccc + + dy*(Icccc+Inncc-Icncc-Inccc + + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + + dv*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + + dv*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + + dz*(Icccc+Incnc-Iccnc-Inccc + + dv*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + + dv*(Icccc+Inccn-Inccc-Icccn)) + + dy*(Icncc-Icccc + + dz*(Icccc+Icnnc-Iccnc-Icncc + + dv*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + + dv*(Icccc+Icncn-Icncc-Icccn)) + + dz*(Iccnc-Icccc + + dv*(Icccc+Iccnn-Iccnc-Icccn)) + + dv*(Icccn-Icccc); + } + + //! Read a pixel value using linear interpolation for the three first coordinates (\c cx,\c cy,\c cz). + /** + - Same as linear_pix4d(), except that linear interpolation and boundary checking is performed only on the three first coordinates. + + \sa operator()(), linear_pix4d(), linear_pix2d(), linear_pix1d(), linear_pix3d(), cubic_pix2d(). + **/ + typename cimg::largest::type linear_pix3d(const float fx,const float fy,const float fz,const int v, + const T& out_val) const { + const int x = (int)fx-(fx>=0?0:1), y = (int)fy-(fy>=0?0:1), z = (int)fz-(fz>=0?0:1), nx = x+1, ny = y+1, nz = z+1; + const float dx = fx-x, dy = fy-y, dz = fz-z; + const T + Iccc = pix3d(x,y,z,v,out_val), Incc = pix3d(nx,y,z,v,out_val), Icnc = pix3d(x,ny,z,v,out_val), Innc = pix3d(nx,ny,z,v,out_val), + Iccn = pix3d(x,y,nz,v,out_val), Incn = pix3d(nx,y,nz,v,out_val), Icnn = pix3d(x,ny,nz,v,out_val), Innn = pix3d(nx,ny,nz,v,out_val); + return Iccc + + dx*(Incc-Iccc + + dy*(Iccc+Innc-Icnc-Incc + + dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) + + dz*(Iccc+Incn-Iccn-Incc)) + + dy*(Icnc-Iccc + + dz*(Iccc+Icnn-Iccn-Icnc)) + + dz*(Iccn-Iccc); + } + + typename cimg::largest::type linear_pix3d(const float ffx,const float ffy=0,const float ffz=0,const int v=0) const { + const float fx = ffx<0?0:(ffx>width-1?width-1:ffx), fy = ffy<0?0:(ffy>height-1?height-1:ffy), fz = ffz<0?0:(ffz>depth-1?depth-1:ffz); + const unsigned int x = (unsigned int)fx, y = (unsigned int)fy, z = (unsigned int)fz; + const float dx = fx-x, dy = fy-y, dz = fz-z; + const unsigned int nx = dx>0?x+1:x, ny = dy>0?y+1:y, nz = dz>0?z+1:z; + const T + &Iccc = (*this)(x,y,z,v), &Incc = (*this)(nx,y,z,v), &Icnc = (*this)(x,ny,z,v), &Innc = (*this)(nx,ny,z,v), + &Iccn = (*this)(x,y,nz,v), &Incn = (*this)(nx,y,nz,v), &Icnn = (*this)(x,ny,nz,v), &Innn = (*this)(nx,ny,nz,v); + return Iccc + + dx*(Incc-Iccc + + dy*(Iccc+Innc-Icnc-Incc + + dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) + + dz*(Iccc+Incn-Iccn-Incc)) + + dy*(Icnc-Iccc + + dz*(Iccc+Icnn-Iccn-Icnc)) + + dz*(Iccn-Iccc); + } + + //! Read a pixel value using linear interpolation for the two first coordinates (\c cx,\c cy). + /** + - Same as linear_pix4d(), except that linear interpolation and boundary checking is performed only on the two first coordinates. + + \sa operator()(), linear_pix4d(), linear_pix3d(), linear_pix1d(), linear_pix2d(), cubic_pix2d(). + **/ + typename cimg::largest::type linear_pix2d(const float fx, const float fy, const int z, const int v, + const T& out_val) const { + const int x = (int)fx-(fx>0?0:1), y = (int)fy-(fy>0?0:1), nx = x+1, ny = y+1; + const float dx = fx-x, dy = fy-y; + const T + Icc = pix2d(x,y,z,v,out_val), Inc = pix2d(nx,y,z,v,out_val), + Icn = pix2d(x,ny,z,v,out_val), Inn = pix2d(nx,ny,z,v,out_val); + return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); + } + + typename cimg::largest::type linear_pix2d(const float ffx, const float ffy=0, const int z=0, const int v=0) const { + const float fx = ffx<0?0:(ffx>width-1?width-1:ffx), fy = ffy<0?0:(ffy>height-1?height-1:ffy); + const unsigned int x = (unsigned int)fx, y = (unsigned int)fy; + const float dx = fx-x, dy = fy-y; + const unsigned int nx = dx>0?x+1:x, ny = dy>0?y+1:y; + const T &Icc = (*this)(x,y,z,v), &Inc = (*this)(nx,y,z,v), &Icn = (*this)(x,ny,z,v), &Inn = (*this)(nx,ny,z,v); + return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); + } + + //! Read a pixel value using linear interpolation for the first coordinate \c cx. + /** + - Same as linear_pix4d(), except that linear interpolation and boundary checking is performed only on the first coordinate. + + \sa operator()(), linear_pix4d(), linear_pix3d(), linear_pix2d(), linear_pix1d(), cubic_pix1d(). + **/ + typename cimg::largest::type linear_pix1d(const float fx,const int y,const int z,const int v, + const T& out_val) const { + const int x = (int)fx-(fx>0?0:1), nx = x+1; + const float dx = fx-x; + const T Ic = pix1d(x,y,z,v,out_val), In = pix2d(nx,y,z,v,out_val); + return Ic + dx*(In-Ic); + } + + typename cimg::largest::type linear_pix1d(const float ffx,const int y=0,const int z=0,const int v=0) const { + const float fx = ffx<0?0:(ffx>width-1?width-1:ffx); + const unsigned int x = (unsigned int)fx; + const float dx = fx-x; + const unsigned int nx = dx>0?x+1:x; + const T &Ic = (*this)(x,y,z,v), &In = (*this)(nx,y,z,v); + return Ic + dx*(In-Ic); + } + + // This function is used as a subroutine for cubic interpolation + static float _cubic_R(const float x) { + const float xp2 = x+2, xp1 = x+1, xm1 = x-1, + nxp2 = xp2>0?xp2:0, nxp1 = xp1>0?xp1:0, nx = x>0?x:0, nxm1 = xm1>0?xm1:0; + return (nxp2*nxp2*nxp2 - 4*nxp1*nxp1*nxp1 + 6*nx*nx*nx - 4*nxm1*nxm1*nxm1)/6.0f; + } + + //! Read a pixel value using cubic interpolation for the first coordinate \c cx. + /** + - Same as cubic_pix2d(), except that cubic interpolation and boundary checking is performed only on the first coordinate. + + \sa operator()(), cubic_pix2d(), linear_pix1d(). + **/ + typename cimg::largest::type cubic_pix1d(const float fx, const int y, const int z, const int v, + const T& out_val) const { + const int x = (int)fx-(fx>=0?0:1), px = x-1, nx = x+1, ax = nx+1; + const float dx = fx-x; + const T a = pix2d(px,y,z,v,out_val), b = pix2d(x,y,z,v,out_val), c = pix2d(nx,y,z,v,out_val), d = pix2d(ax,y,z,v,out_val); + const float Rxp = _cubic_R(-1-dx), Rxc = _cubic_R(dx), Rxn = _cubic_R(1-dx), Rxa = _cubic_R(2-dx); + return Rxp*a + Rxc*b + Rxn*c + Rxa*d; + } + + typename cimg::largest::type cubic_pix1d(const float pfx, const int y=0, const int z=0, const int v=0) const { + const float fx = pfx<0?0:(pfx>width-1?width-1:pfx); + const unsigned int x = (unsigned int)fx, px = (int)x-1>=0?x-1:0, nx = x+1::type cubic_pix2d(const float fx, const float fy, const int z, const int v, + const T& out_val) const { + const int + x = (int)fx-(fx>=0?0:1), y = (int)fy-(fy>=0?0:1), + px = x-1, nx = x+1, ax = nx+1, py = y-1, ny = y+1, ay = ny+1; + const float dx = fx-x, dy = fy-y; + const T + a = pix2d(px,py,z,v,out_val), b = pix2d(x,py,z,v,out_val), c = pix2d(nx,py,z,v,out_val), d = pix2d(ax,py,z,v,out_val), + e = pix2d(px, y,z,v,out_val), f = pix2d(x, y,z,v,out_val), g = pix2d(nx, y,z,v,out_val), h = pix2d(ax, y,z,v,out_val), + i = pix2d(px,ny,z,v,out_val), j = pix2d(x,ny,z,v,out_val), k = pix2d(nx,ny,z,v,out_val), l = pix2d(ax,ny,z,v,out_val), + m = pix2d(px,ay,z,v,out_val), n = pix2d(x,ay,z,v,out_val), o = pix2d(nx,ay,z,v,out_val), p = pix2d(ax,ay,z,v,out_val); + const float + Rxp = _cubic_R(-1-dx), Rxc = _cubic_R(dx), Rxn = _cubic_R(1-dx), Rxa = _cubic_R(2-dx), + Ryp = _cubic_R(dy+1), Ryc = _cubic_R(dy), Ryn = _cubic_R(dy-1), Rya = _cubic_R(dy-2); + return + Rxp*Ryp*a + Rxc*Ryp*b + Rxn*Ryp*c + Rxa*Ryp*d + + Rxp*Ryc*e + Rxc*Ryc*f + Rxn*Ryc*g + Rxa*Ryc*h + + Rxp*Ryn*i + Rxc*Ryn*j + Rxn*Ryn*k + Rxa*Ryn*l + + Rxp*Rya*m + Rxc*Rya*n + Rxn*Rya*o + Rxa*Rya*p; + } + + typename cimg::largest::type cubic_pix2d(const float pfx, const float pfy=0, const int z=0, const int v=0) const { + const float fx = pfx<0?0:(pfx>width-1?width-1:pfx), fy = pfy<0?0:(pfy>height-1?height-1:pfy); + const unsigned int + x = (unsigned int)fx, px = (int)x-1>=0?x-1:0, nx = x+1=0?y-1:0, ny = y+1 img("foo.jpg"); // Load image from a JPEG file. + img.print("Image : foo.jpg",1); // Print image informations and statistics. + \endcode + + \sa CImgStats + **/ + const CImg& print(const char *title=0, const unsigned int print_flag=1) const { + std::fprintf(stderr,"%-8s(this=%p): { size=(%u,%u,%u,%u), data=(%s*)%p (%s)", + title?title:"CImg",(void*)this, + width,height,depth,dim,pixel_type(),(void*)data, + is_shared?"shared":"not shared"); + if (is_empty()) { std::fprintf(stderr,", [Undefined pixel data] }\n"); return *this; } + if (print_flag>=1) { + const CImgStats st(*this); + std::fprintf(stderr,", min=%g, mean=%g [var=%g], max=%g, pmin=(%d,%d,%d,%d), pmax=(%d,%d,%d,%d)", + st.min,st.mean,st.variance,st.max,st.xmin,st.ymin,st.zmin,st.vmin,st.xmax,st.ymax,st.zmax,st.vmax); + } + if (print_flag>=2 || size()<=16) { + std::fprintf(stderr," }\n%s = [ ",title?title:"data"); + cimg_forXYZV(*this,x,y,z,k) + std::fprintf(stderr,"%g%s",(double)(*this)(x,y,z,k), + ((x+1)*(y+1)*(z+1)*(k+1)==(int)size()?" ]\n":(((x+1)%width==0)?" ; ":" "))); + } else std::fprintf(stderr," }\n"); + return *this; + } + + //! Display informations about the image on the standart output. + const CImg& print(const unsigned int print_flag) const { + return print(0,print_flag); + } + + //@} + //------------------------------------------ + // + //! \name Arithmetic and Boolean Operators + //@{ + //------------------------------------------ + + //! Assignement operator. + /** + This operator assigns a copy of the input image \p img to the current instance image. + \param img The input image to copy. + \remark + - This operator is strictly equivalent to the function assign(const CImg< t >&) and has exactly the same properties. + \see assign(const CImg< t >&). + **/ + template CImg& operator=(const CImg& img) { + return assign(img); + } + + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Assign values of a C-array to the instance image. + /** + \param buf Pointer to a C-style array having a size of (at least) this->size(). + + - Replace pixel values by the content of the array \c buf. + - Warning : the value types in the array and in the image must be the same. + + \par example: + \code + float tab[4*4] = { 1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16 }; // Define a 4x4 matrix in C-style. + CImg matrice(4,4); // Define a 4x4 greyscale image. + matrice = tab; // Fill the image by the values in tab. + \endcode + **/ + CImg& operator=(const T *buf) { + if (buf) std::memcpy(data,buf,size()*sizeof(T)); + else assign(); + return *this; + } + + //! Assign a value to each image pixel of the instance image. + CImg& operator=(const T& val) { + return fill(val); + } + + //! Operator+ + /** + \remark + - This operator can be used to get a non-shared copy of an image. + **/ + CImg operator+() const { + return CImg(*this,false); + } + + //! Operator+=; +#ifdef cimg_use_visualcpp6 + CImg& operator+=(const T& val) { +#else + template CImg& operator+=(const t& val) { +#endif + cimg_for(*this,ptr,T) (*ptr)=(T)((*ptr)+val); + return *this; + } + + //! Operator+= + template CImg& operator+=(const CImg& img) { + const unsigned int smin = cimg::min(size(),img.size()); + t *ptrs = img.data+smin; + for (T *ptrd = data+smin; ptrd>data; --ptrd, (*ptrd)=(T)((*ptrd)+(*(--ptrs)))); + return *this; + } + + //! Operator++; + CImg& operator++() { + cimg_for(*this,ptr,T) (*ptr)++; + return *this; + } + + //! Operator-. + CImg operator-() const { + return CImg(width,height,depth,dim,0)-=*this; + } + + //! Operator-=. +#ifdef cimg_use_visualcpp6 + CImg& operator-=(const T& val) { +#else + template CImg& operator-=(const t& val) { +#endif + cimg_for(*this,ptr,T) (*ptr)=(T)((*ptr)-val); + return *this; + } + + //! Operator-=. + template CImg& operator-=(const CImg& img) { + const unsigned int smin = cimg::min(size(),img.size()); + t *ptrs = img.data+smin; + for (T *ptrd = data+smin; ptrd>data; --ptrd, (*ptrd)=(T)((*ptrd)-(*(--ptrs)))); + return *this; + } + + //! Operator--. + CImg& operator--() { + cimg_for(*this,ptr,T) (*ptr)--; + return *this; + } + + //! Operator*=. +#ifdef cimg_use_visualcpp6 + CImg& operator*=(const double val) { +#else + template CImg& operator*=(const t& val) { +#endif + cimg_for(*this,ptr,T) (*ptr)=(T)((*ptr)*val); + return *this; + } + + //! Operator*=. + template CImg& operator*=(const CImg& img) { + return ((*this)*img).swap(*this); + } + + //! Operator/=. +#ifdef cimg_use_visualcpp6 + CImg& operator/=(const double val) { +#else + template CImg& operator/=(const t& val) { +#endif + cimg_for(*this,ptr,T) (*ptr)=(T)((*ptr)/val); + return *this; + } + + //! Operator/=. + template CImg& operator/=(const CImg& img) { + return assign(*this*img.get_inverse()); + } + + //! Modulo. + CImg operator%(const CImg& img) const { + return (+*this)%=img; + } + + //! Modulo. + CImg operator%(const T& val) const { + return (+*this)%=val; + } + + //! In-place modulo. + CImg& operator%=(const T& val) { + cimg_for(*this,ptr,T) (*ptr)%=val; + return *this; + } + + //! In-place modulo. + CImg& operator%=(const CImg& img) { + const unsigned int smin = cimg::min(size(),img.size()); + for (T *ptrs=img.data+smin, *ptrd=data+smin; ptrd>data; *(--ptrd)%=*(--ptrs)); + return *this; + } + + //! Bitwise AND. + CImg operator&(const CImg& img) const { + return (+*this)&=img; + } + + //! Bitwise AND. + CImg operator&(const T& val) const { + return (+*this)&=val; + } + + //! In-place bitwise AND. + CImg& operator&=(const CImg& img) { + const unsigned int smin = cimg::min(size(),img.size()); + for (T *ptrs=img.data+smin, *ptrd=data+smin; ptrd>data; *(--ptrd)&=*(--ptrs)); + return *this; + } + + //! In-place bitwise AND. + CImg& operator&=(const T& val) { + cimg_for(*this,ptr,T) (*ptr)&=val; + return *this; + } + + //! Bitwise OR. + CImg operator|(const CImg& img) const { + return (+*this)|=img; + } + + //! Bitwise OR. + CImg operator|(const T& val) const { + return (+*this)|=val; + } + + //! In-place bitwise OR. + CImg& operator|=(const CImg& img) { + const unsigned int smin = cimg::min(size(),img.size()); + for (T *ptrs=img.data+smin, *ptrd=data+smin; ptrd>data; *(--ptrd)|=*(--ptrs)); + return *this; + } + + //! In-place bitwise OR. + CImg& operator|=(const T& val) { + cimg_for(*this,ptr,T) (*ptr)|=val; + return *this; + } + + //! Bitwise XOR. + CImg operator^(const CImg& img) const { + return (+*this)^=img; + } + + //! Bitwise XOR. + CImg operator^(const T& val) const { + return (+*this)^=val; + } + + //! In-place bitwise XOR. + CImg& operator^=(const CImg& img) { + const unsigned int smin = cimg::min(size(),img.size()); + for (T *ptrs=img.data+smin, *ptrd=data+smin; ptrd>data; *(--ptrd)^=*(--ptrs)); + return *this; + } + + //! In-place bitwise XOR. + CImg& operator^=(const T& val) { + cimg_for(*this,ptr,T) (*ptr)^=val; + return *this; + } + + //! Boolean NOT. + CImg operator!() const { + CImg res(width,height,depth,dim); + const T *ptrs = end(); + cimg_for(res,ptrd,T) *ptrd=!(*(--ptrs)); + return res; + } + + //! Bitwise NOT. + CImg operator~() const { + CImg res(width,height,depth,dim); + const T *ptrs = end(); + cimg_for(res,ptrd,T) *ptrd=~(*(--ptrs)); + return res; + } + + //! Bitwise shift + CImg& operator<<=(const unsigned int n) { + cimg_for(*this,ptr,T) (*ptr)<<=n; + return *this; + } + + //! Bitwise shift + CImg operator<<(const unsigned int n) const { + return (+*this)<<=n; + } + + //! Bitwise shift + CImg& operator>>=(const unsigned int n) { + cimg_for(*this,ptr,T) (*ptr)>>=n; + return *this; + } + + //! Bitwise shift + CImg operator>>(const unsigned int n) const { + return (+*this)>>=n; + } + + //! Boolean equality. + template bool operator==(const CImg& img) const { + const unsigned int siz = size(); + bool vequal = true; + if (siz!=img.size()) return false; + t *ptrs=img.data+siz; + for (T *ptrd=data+siz; vequal && ptrd>data; vequal=vequal&&((*(--ptrd))==(*(--ptrs)))); + return vequal; + } + + //! Boolean difference. + template bool operator!=(const CImg& img) const { + return !((*this)==img); + } + + //! Get a new list + template CImgList::type> operator<<(const CImg& img) const { + typedef typename cimg::largest::type restype; + return CImgList(*this,img); + } + + //@} + //--------------------------------------- + // + //! \name Usual Mathematics + //@{ + //--------------------------------------- + + //! Apply a R->R function on all image value. + template CImg& apply(td (*func)(ts)) { + cimg_for(*this,ptr,T) *ptr = (T)func(*ptr); + return *this; + } + + //! Return an image where each pixel value is equal to func(x). + template CImg::type> get_apply(td (*func)(ts)) { + typedef typename cimg::largest::type restype; + return CImg(*this,false).apply(func); + } + + //! In-place pointwise multiplication between \c *this and \c img. + /** + This is the in-place version of get_mul(). + \sa get_mul(). + **/ + template CImg& mul(const CImg& img) { + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd CImg::type> get_mul(const CImg& img) const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).mul(img); + } + + //! Replace the image by the pointwise division between \p *this and \p img. + /** + This is the in-place version of get_div(). + \see get_div(). + **/ + template CImg& div(const CImg& img) { + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd CImg::type> get_div(const CImg& img) const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).div(img); + } + + //! Replace the image by the pointwise max operator between \p *this and \p img + /** + This is the in-place version of get_max(). + \see get_max(). + **/ + template CImg& max(const CImg& img) { + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd CImg::type> get_max(const CImg& img) const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).max(img); + } + + //! Replace the image by the pointwise max operator between \p *this and \p val + /** + This is the in-place version of get_max(). + \see get_max(). + **/ +#ifdef cimg_use_visualcpp6 + CImg& max(const T val) { +#else + CImg& max(const T& val) { +#endif + cimg_for(*this,ptr,T) (*ptr)=cimg::max(*ptr,val); + return *this; + } + + //! Return the image corresponding to the max value for each pixel. + /** + \param val = second argument of the max operator (the first one is *this). + \see max(), min(), get_min() + **/ +#ifdef cimg_use_visualcpp6 + CImg get_max(const T val) const { +#else + CImg get_max(const T& val) const { +#endif + return (+*this).max(val); + } + + //! Replace the image by the pointwise min operator between \p *this and \p img + /** + This is the in-place version of get_min(). + \see get_min(). + **/ + template CImg& min(const CImg& img) { + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd CImg::type> get_min(const CImg& img) const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).min(img); + } + + //! Replace the image by the pointwise min operator between \p *this and \p val + /** + This is the in-place version of get_min(). + \see get_min(). + **/ +#ifdef cimg_use_visualcpp6 + CImg& min(const T val) { +#else + CImg& min(const T& val) { +#endif + cimg_for(*this,ptr,T) (*ptr)=cimg::min(*ptr,val); + return *this; + } + + //! Return the image corresponding to the min value for each pixel. + /** + \param val = second argument of the min operator (the first one is *this). + \see min(), max(), get_max() + **/ +#ifdef cimg_use_visualcpp6 + CImg get_min(const T val) const { +#else + CImg get_min(const T& val) const { +#endif + return (+*this).min(val); + } + + //! Replace each image pixel by its square root. + /** + \see get_sqrt() + **/ + CImg& sqrt() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::sqrt((double)(*ptr)); + return *this; + } + + //! Return the image of the square root of the pixel values. + /** + \see sqrt() + **/ + CImg::type> get_sqrt() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).sqrt(); + } + + //! Replace each image pixel by its exponential. + CImg& exp() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::exp((double)(*ptr)); + return *this; + } + + //! Return the image of the exponential of the pixel values. + CImg::type> get_exp() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).exp(); + } + + //! Replace each image pixel by its log. + /** + \see get_log(), log10(), get_log10() + **/ + CImg& log() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::log((double)(*ptr)); + return *this; + } + + //! Return the image of the log of the pixel values. + /** + \see log(), log10(), get_log10() + **/ + CImg::type> get_log() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).log(); + } + + //! Replace each image pixel by its log10. + /** + \see get_log10(), log(), get_log() + **/ + CImg& log10() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::log10((double)(*ptr)); + return *this; + } + + //! Return the image of the log10 of the pixel values. + /** + \see log10(), log(), get_log() + **/ + CImg::type> get_log10() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).log10(); + } + + //! Replace each image pixel by its power by \p p. + /** + \param p = power + \see get_pow(), sqrt(), get_sqrt() + **/ + CImg& pow(const double p) { + if (p==0) return fill(1); + if (p==1) return *this; + if (p==2) { cimg_for(*this,ptr,T) { const T& val = *ptr; *ptr=val*val; } return *this; } + if (p==3) { cimg_for(*this,ptr,T) { const T& val = *ptr; *ptr=val*val*val; } return *this; } + if (p==4) { cimg_for(*this,ptr,T) { const T& val = *ptr; *ptr=val*val*val*val; } return *this; } + cimg_for(*this,ptr,T) (*ptr)=(T)std::pow((double)(*ptr),p); + return *this; + } + + //! Return the image of the square root of the pixel values. + /** + \param p = power + \see pow(), sqrt(), get_sqrt() + **/ + CImg::type> get_pow(const double p) const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).pow(p); + } + + //! Return each image pixel (*this)(x,y,z,k) by its power by \p img(x,y,z,k) + /** + In-place version + **/ + template CImg& pow(const CImg& img) { + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd CImg::type> get_pow(const CImg& img) const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).pow(img); + } + + //! Replace each pixel value by its absolute value. + /** + \see get_abs() + **/ + CImg& abs() { + cimg_for(*this,ptr,T) (*ptr)=cimg::abs(*ptr); + return *this; + } + + //! Return the image of the absolute value of the pixel values. + /** + \see abs() + **/ + CImg::type> get_abs() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).abs(); + } + + //! Replace each image pixel by its cosinus. + /** + \see get_cos(), sin(), get_sin(), tan(), get_tan() + **/ + CImg& cos() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::cos((double)(*ptr)); + return *this; + } + + //! Return the image of the cosinus of the pixel values. + /** + \see cos(), sin(), get_sin(), tan(), get_tan() + **/ + CImg::type> get_cos() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).cos(); + } + + //! Replace each image pixel by its sinus. + /** + \see get_sin(), cos(), get_cos(), tan(), get_tan() + **/ + CImg& sin() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::sin((double)(*ptr)); + return *this; + } + + //! Return the image of the sinus of the pixel values. + /** + \see sin(), cos(), get_cos(), tan(), get_tan() + **/ + CImg::type> get_sin() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).sin(); + } + + //! Replace each image pixel by its tangent. + /** + \see get_tan(), cos(), get_cos(), sin(), get_sin() + **/ + CImg& tan() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::tan((double)(*ptr)); + return *this; + } + + //! Return the image of the tangent of the pixel values. + /** + \see tan(), cos(), get_cos(), sin(), get_sin() + **/ + CImg::type> get_tan() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).tan(); + } + + //! Replace each image pixel by its arc-cosinus. + CImg& acos() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::acos((double)(*ptr)); + return *this; + } + + //! Return the image of the arc-cosinus of the pixel values. + CImg::type> get_acos() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).acos(); + } + + //! Replace each image pixel by its arc-sinus. + CImg& asin() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::asin((double)(*ptr)); + return *this; + } + + //! Return the image of the arc-sinus of the pixel values. + CImg::type> get_asin() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).asin(); + } + + //! Replace each image pixel by its arc-tangent. + CImg& atan() { + cimg_for(*this,ptr,T) (*ptr)=(T)std::atan((double)(*ptr)); + return *this; + } + + //! Return the image of the arc-tangent of the pixel values. + CImg::type> get_atan() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).atan(); + } + + //! Return the MSE (Mean-Squared Error) between two images. + template double MSE(const CImg& img) const { + if (img.size()!=size()) + throw CImgArgumentException("CImg<%s>::MSE() : Instance image (%u,%u,%u,%u) and given image (%u,%u,%u,%u) have different dimensions.", + pixel_type(),width,height,depth,dim,img.width,img.height,img.depth,img.dim); + + double vMSE = 0; + const t* ptr2 = img.end(); + cimg_for(*this,ptr1,T) { + const double diff = (double)*ptr1 - (double)*(--ptr2); + vMSE += diff*diff; + } + vMSE/=img.size(); + return vMSE; + } + + //! Return the PSNR between two images. + template double PSNR(const CImg& img, const double valmax=255.0) const { + const double vMSE = std::sqrt(MSE(img)); + return (vMSE!=0)?(20*std::log10(valmax/vMSE)):(cimg::type::max()); + } + + //@} + //----------------------------------- + // + //! \name Usual Image Transformations + //@{ + //----------------------------------- + + //! Fill an image by a value \p val. + /** + \param val = fill value + \note All pixel values of the instance image will be initialized by \p val. + \see operator=(). + **/ + CImg& fill(const T& val) { + if (!is_empty()) { + if (val!=0 && sizeof(T)!=1) cimg_for(*this,ptr,T) *ptr=val; + else std::memset(data,(int)val,size()*sizeof(T)); + } + return *this; + } + + CImg get_fill(const T& val) const { + return (+*this).fill(val); + } + + //! Fill sequentially all pixel values with values \a val0 and \a val1 respectively. + /** + \param val0 = fill value 1 + \param val1 = fill value 2 + **/ + CImg& fill(const T& val0,const T& val1) { + if (!is_empty()) { + T *ptr, *ptr_end = end()-1; + for (ptr=data; ptrb)?b:*ptr); + return *this; + } + + //! Return the image of cutted values. + /** + \param a = minimum pixel value after cut. + \param b = maximum pixel value after cut. + \see cut(), normalize(), get_normalize(). + **/ + CImg get_cut(const T& a, const T& b) const { + return (+*this).cut(a,b); + } + + //! Quantize pixel values into \n levels. + /** + \param n = number of quantification levels + \see get_quantize(). + **/ + CImg& quantize(const unsigned int n=256) { + if (!is_empty()) { + if (!n) throw CImgArgumentException("CImg<%s>::quantize() : Cannot quantize image to 0 values.", + pixel_type()); + const CImgStats st(*this,false); + const double range = st.max-st.min; + if (range>0) cimg_for(*this,ptr,T) { + const unsigned int val = (unsigned int)((*ptr-st.min)*n/range); + *ptr = (T)(st.min + cimg::min(val,n-1)*range); + } + } + return *this; + } + + //! Return a quantified image, with \n levels. + /** + \param n = number of quantification levels + \see quantize(). + **/ + CImg get_quantize(const unsigned int n=256) const { + return (+*this).quantize(n); + } + + //! Threshold the image. + /** + \param thres = threshold + \see get_threshold(). + **/ + CImg& threshold(const T& thres) { + if (!is_empty()) cimg_for(*this,ptr,T) *ptr = *ptr<=thres?(T)0:(T)1; + return *this; + } + + //! Return a thresholded image. + /** + \param thres = threshold. + \see threshold(). + **/ + CImg get_threshold(const T& thres) const { + return (+*this).threshold(thres); + } + + //! Return a rotated image. + /** + \param angle = rotation angle (in degrees). + \param cond = rotation type. can be : + - 0 = zero-value at borders + - 1 = repeat image at borders + - 2 = zero-value at borders and linear interpolation + \note Returned image will probably have a different size than the instance image *this. + \see rotate() + **/ + CImg get_rotate(const float angle, const unsigned int cond=3) const { + if (is_empty()) return CImg(); + CImg dest; + const float nangle = cimg::mod(angle,360.0f), rad = (float)((nangle*cimg::PI)/180.0), + ca=(float)std::cos(rad), sa=(float)std::sin(rad); + if (cond!=1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles + const int wm1 = dimx()-1, hm1 = dimy()-1; + const int iangle = (int)nangle/90; + switch (iangle) { + case 1: { + dest.assign(height,width,depth,dim); + cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(y,hm1-x,z,v); + } break; + case 2: { + dest.assign(width,height,depth,dim); + cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(wm1-x,hm1-y,z,v); + } break; + case 3: { + dest.assign(height,width,depth,dim); + cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(wm1-y,x,z,v); + } break; + default: + return *this; + } + } else { // generic version + const float + ux = (float)(cimg::abs(width*ca)), uy = (float)(cimg::abs(width*sa)), + vx = (float)(cimg::abs(height*sa)), vy = (float)(cimg::abs(height*ca)), + w2 = 0.5f*width, h2 = 0.5f*height, + dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy); + dest.assign((int)(ux+vx), (int)(uy+vy),depth,dim); + switch (cond) { + case 0: { + cimg_forXY(dest,x,y) + cimg_forZV(*this,z,v) + dest(x,y,z,v) = pix2d((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,v,0); + } break; + case 1: { + cimg_forXY(dest,x,y) + cimg_forZV(*this,z,v) + dest(x,y,z,v) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),width), + cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),height),z,v); + } break; + case 2: { + cimg_forXY(dest,x,y) { + const float X = w2 + (x-dw2)*ca + (y-dh2)*sa, Y = h2 - (x-dw2)*sa + (y-dh2)*ca; + cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)linear_pix2d(X,Y,z,v,0); + } + } break; + default: { + cimg_forXY(dest,x,y) { + const float X = w2 + (x-dw2)*ca + (y-dh2)*sa, Y = h2 - (x-dw2)*sa + (y-dh2)*ca; + cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)cubic_pix2d(X,Y,z,v,0); + } + } break; + } + } + return dest; + } + + //! Rotate the image + /** + \param angle = rotation angle (in degrees). + \param cond = rotation type. can be : + - 0 = zero-value at borders + - 1 = repeat image at borders + - 2 = zero-value at borders and linear interpolation + \see get_rotate() + **/ + CImg& rotate(const float angle,const unsigned int cond=3) { return get_rotate(angle,cond).swap(*this); } + + //! Return a rotated image around the point (\c cx,\c cy). + /** + \param angle = rotation angle (in degrees). + \param cx = X-coordinate of the rotation center. + \param cy = Y-coordinate of the rotation center. + \param zoom = zoom. + \param cond = rotation type. can be : + - 0 = zero-value at borders + - 1 = repeat image at borders + - 2 = zero-value at borders and linear interpolation + \see rotate() + **/ + CImg get_rotate(const float angle,const float cx,const float cy,const float zoom=1,const unsigned int cond=3) const { + if (is_empty()) return CImg(); + CImg dest(width,height,depth,dim); + const float nangle = cimg::mod(angle,360.0f), rad = (float)((nangle*cimg::PI)/180.0), + ca=(float)std::cos(rad)/zoom, sa=(float)std::sin(rad)/zoom; + if (cond!=1 && zoom==1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles + const int iangle = (int)nangle/90; + switch (iangle) { + case 1: { + dest.fill(0); + const unsigned int + xmin = cimg::max(0,(dimx()-dimy())/2), xmax = cimg::min(width,xmin+height), + ymin = cimg::max(0,(dimy()-dimx())/2), ymax = cimg::min(height,ymin+width), + xoff = xmin + cimg::min(0,(dimx()-dimy())/2), + yoff = ymin + cimg::min(0,(dimy()-dimx())/2); + cimg_forZV(dest,z,v) for (unsigned int y=ymin; y(); + const unsigned int + tdx = pdx<0?-pdx*width/100:pdx, + tdy = pdy<0?-pdy*height/100:pdy, + tdz = pdz<0?-pdz*depth/100:pdz, + tdv = pdv<0?-pdv*dim/100:pdv, + dx = tdx?tdx:1, + dy = tdy?tdy:1, + dz = tdz?tdz:1, + dv = tdv?tdv:1; + if (is_empty()) return CImg(dx,dy,dz,dv,0); + if (width==dx && height==dy && depth==dz && dim==dv) return *this; + CImg res; + + switch (interp) { + case 0: // Zero filling + res.assign(dx,dy,dz,dv,0).draw_image(*this,0,0,0,0); + break; + + case 1: { // Nearest-neighbor interpolation + res.assign(dx,dy,dz,dv); + unsigned int + *const offx = new unsigned int[dx], + *const offy = new unsigned int[dy+1], + *const offz = new unsigned int[dz+1], + *const offv = new unsigned int[dv+1], + *poffx, *poffy, *poffz, *poffv, + curr, old; + const unsigned int wh = width*height, whd = width*height*depth, rwh = dx*dy, rwhd = dx*dy*dz; + poffx = offx; curr=0; { cimg_forX(res,x) { old=curr; curr=(x+1)*width/dx; *(poffx++) = (unsigned int)curr-(unsigned int)old; }} + poffy = offy; curr=0; { cimg_forY(res,y) { old=curr; curr=(y+1)*height/dy; *(poffy++) = width*((unsigned int)curr-(unsigned int)old); }} *poffy=0; + poffz = offz; curr=0; { cimg_forZ(res,z) { old=curr; curr=(z+1)*depth/dz; *(poffz++) = wh*((unsigned int)curr-(unsigned int)old); }} *poffz=0; + poffv = offv; curr=0; { cimg_forV(res,k) { old=curr; curr=(k+1)*dim/dv; *(poffv++) = whd*((unsigned int)curr-(unsigned int)old); }} *poffv=0; + T *ptrd = res.ptr(); + const T* ptrv = ptr(); + poffv = offv; + for (unsigned int k=0; k1 && border_condition<0); + const float + sx = bborder?(dx>0?(width-1.0f)/(dx-1) :0):(float)width/dx, + sy = bborder?(dy>0?(height-1.0f)/(dy-1):0):(float)height/dy, + sz = bborder?(dz>0?(depth-1.0f)/(dz-1) :0):(float)depth/dz, + sv = bborder?(dv>0?(dim-1.0f)/(dv-1) :0):(float)dim/dv; + unsigned int *const off = new unsigned int[dimmax], *poff; + float *const foff = new float[dimmax], *pfoff, old, curr; + CImg resx, resy, resz, resv; + T *ptrd; + + if (dx!=width) { + if (width==1) resx = get_resize(dx,height,depth,dim,1,0); + else { + resx.assign(dx,height,depth,dim); + curr = old = 0; poff = off; pfoff = foff; + cimg_forX(resx,x) { *(pfoff++) = curr-(unsigned int)curr; old = curr; curr+=sx; *(poff++) = (unsigned int)curr-(unsigned int)old; } + ptrd = resx.ptr(); + const T *ptrs0 = ptr(); + cimg_forYZV(resx,y,z,k) { + poff = off; pfoff = foff; + const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (width-1); + cimg_forX(resx,x) { + const float alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrs1 && border_condition<0); + const float + sx = bborder?(dx>0?(width-1.0f)/(dx-1) :0):(float)width/dx, + sy = bborder?(dy>0?(height-1.0f)/(dy-1):0):(float)height/dy, + sz = bborder?(dz>0?(depth-1.0f)/(dz-1) :0):(float)depth/dz, + sv = bborder?(dv>0?(dim-1.0f)/(dv-1) :0):(float)dim/dv; + res.assign(dx,dy,dz,dv); + float cx, cy, cz, ck = 0; + cimg_forV(res,k) { cz = 0; + cimg_forZ(res,z) { cy = 0; + cimg_forY(res,y) { cx = 0; + cimg_forX(res,x) { res(x,y,z,k) = (T)(border_condition?cubic_pix2d(cx,cy,(int)cz,(int)ck):cubic_pix2d(cx,cy,(int)cz,(int)ck,0)); + cx+=sx; + } cy+=sy; + } cz+=sz; + } ck+=sv; + } + } break; + + } + + return res; + } + + //! Return a resized image. + /** + \param src = Image giving the geometry of the resize. + \param interp = Resizing type : + - 0 = no interpolation : additionnal space is filled with 0. + - 1 = bloc interpolation (nearest point). + - 2 = mosaic : image is repeated if necessary. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = bi-cubic interpolation. + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + template CImg get_resize(const CImg& src, const unsigned int interp=1, const int border_condition=-1) const { + return get_resize(src.width,src.height,src.depth,src.dim,interp,border_condition); + } + + //! Return a resized image. + /** + \param disp = Display giving the geometry of the resize. + \param interp = Resizing type : + - 0 = no interpolation : additionnal space is filled with 0. + - 1 = bloc interpolation (nearest point). + - 2 = mosaic : image is repeated if necessary. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = bi-cubic interpolation. + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + CImg get_resize(const CImgDisplay& disp,const unsigned int interp=1, const int border_condition=-1) const { + return get_resize(disp.width,disp.height,depth,dim,interp,border_condition); + } + + //! Resize the image. + /** + \param pdx = Number of columns (new size along the X-axis). + \param pdy = Number of rows (new size along the Y-axis). + \param pdz = Number of slices (new size along the Z-axis). + \param pdv = Number of vector-channels (new size along the V-axis). + \param interp = Resizing type : + - 0 = no interpolation : additionnal space is filled with 0. + - 1 = bloc interpolation (nearest point). + - 2 = mosaic : image is repeated if necessary. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = bi-cubic interpolation. + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + CImg& resize(const int pdx=-100, const int pdy=-100, const int pdz=-100, const int pdv=-100, + const unsigned int interp=1, const int border_condition=-1) { + if (!pdx || !pdy || !pdz || !pdv) return assign(); + const unsigned int + dx = pdx<0?-pdx*width/100:pdx, + dy = pdy<0?-pdy*height/100:pdy, + dz = pdz<0?-pdz*depth/100:pdz, + dv = pdv<0?-pdv*dim/100:pdv; + if (width==dx && height==dy && depth==dz && dim==dv) return *this; + return get_resize(dx,dy,dz,dv,interp,border_condition).swap(*this); + } + + //! Resize the image. + /** + \param src = Image giving the geometry of the resize. + \param interp = Resizing type : + - 0 = no interpolation : additionnal space is filled with 0. + - 1 = bloc interpolation (nearest point). + - 2 = mosaic : image is repeated if necessary. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = bi-cubic interpolation. + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + template CImg& resize(const CImg& src, const unsigned int interp=1, const int border_condition=-1) { + return resize(src.width,src.height,src.depth,src.dim,interp,border_condition); + } + + //! Resize the image + /** + \param disp = Display giving the geometry of the resize. + \param interp = Resizing type : + - 0 = no interpolation : additionnal space is filled with 0. + - 1 = bloc interpolation (nearest point). + - 2 = mosaic : image is repeated if necessary. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = bi-cubic interpolation. + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + CImg& resize(const CImgDisplay& disp, const unsigned int interp=1, const int border_condition=-1) { + return resize(disp.width,disp.height,depth,dim,interp,border_condition); + } + + //! Return an half-resized image, using a special filter. + /** + \see resize_halfXY(), resize(), get_resize(). + **/ + CImg get_resize_halfXY() const { + typedef typename cimg::largest::type ftype; + if (is_empty()) return CImg(); + CImg mask = CImg::matrix(0.07842776544f, 0.1231940459f, 0.07842776544f, + 0.1231940459f, 0.1935127547f, 0.1231940459f, + 0.07842776544f, 0.1231940459f, 0.07842776544f); + CImg_3x3(I,ftype); + CImg dest(width/2,height/2,depth,dim); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) + if (x%2 && y%2) dest(x/2,y/2,z,k) = (T)cimg_conv3x3(I,mask); + return dest; + } + + //! Half-resize the image, using a special filter + /** + \see get_resize_halfXY(), resize(), get_resize(). + **/ + CImg& resize_halfXY() { + return get_resize_halfXY().swap(*this); + } + + //! Return a square region of the image, as a new image + /** + \param x0 = X-coordinate of the upper-left crop rectangle corner. + \param y0 = Y-coordinate of the upper-left crop rectangle corner. + \param z0 = Z-coordinate of the upper-left crop rectangle corner. + \param v0 = V-coordinate of the upper-left crop rectangle corner. + \param x1 = X-coordinate of the lower-right crop rectangle corner. + \param y1 = Y-coordinate of the lower-right crop rectangle corner. + \param z1 = Z-coordinate of the lower-right crop rectangle corner. + \param v1 = V-coordinate of the lower-right crop rectangle corner. + \param border_condition = Dirichlet (false) or Neumann border conditions. + \see crop() + **/ + CImg get_crop(const unsigned int x0,const unsigned int y0,const unsigned int z0,const unsigned int v0, + const unsigned int x1,const unsigned int y1,const unsigned int z1,const unsigned int v1, + const bool border_condition = false) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::get_crop() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + const unsigned int dx=x1-x0+1, dy=y1-y0+1, dz=z1-z0+1, dv=v1-v0+1; + CImg dest(dx,dy,dz,dv); + if (x0>=width || x1>=width || y0>=height || y1>=height || z0>=depth || z1>=depth || + v0>=dim || v1>=dim || x1end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_points() : Cannot return a shared-memory subset (%u->%u,%u,%u,%u) from " + "a (%u,%u,%u,%u) image.",pixel_type(),x0,x1,y0,z0,v0,width,height,depth,dim); + return CImg(data+beg,x1-x0+1,1,1,1,true); + } + + //! Get a shared-memory image referencing a set of points of the instance image (const version). + const CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int v0=0) const { + const unsigned long beg = offset(x0,y0,z0,v0), end = offset(x1,y0,z0,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_points() : Cannot return a shared-memory subset (%u->%u,%u,%u,%u) from " + "a (%u,%u,%u,%u) image.",pixel_type(),x0,x1,y0,z0,v0,width,height,depth,dim); + return CImg(data+beg,x1-x0+1,1,1,1,true); + } + + //! Return a shared-memory image referencing a set of lines of the instance image. + CImg get_shared_lines(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int v0=0) { + const unsigned long beg = offset(0,y0,z0,v0), end = offset(0,y1,z0,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_lines() : Cannot return a shared-memory subset (0->%u,%u->%u,%u,%u) from " + "a (%u,%u,%u,%u) image.",pixel_type(),width-1,y0,y1,z0,v0,width,height,depth,dim); + return CImg(data+beg,width,y1-y0+1,1,1,true); + } + + //! Return a shared-memory image referencing a set of lines of the instance image (const version). + const CImg get_shared_lines(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int v0=0) const { + const unsigned long beg = offset(0,y0,z0,v0), end = offset(0,y1,z0,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_lines() : Cannot return a shared-memory subset (0->%u,%u->%u,%u,%u) from " + "a (%u,%u,%u,%u) image.",pixel_type(),width-1,y0,y1,z0,v0,width,height,depth,dim); + return CImg(data+beg,width,y1-y0+1,1,1,true); + } + + //! Return a shared-memory image referencing one particular line (y0,z0,v0) of the instance image. + CImg get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int v0=0) { + return get_shared_lines(y0,y0,z0,v0); + } + + //! Return a shared-memory image referencing one particular line (y0,z0,v0) of the instance image (const version). + const CImg get_shared_line(const unsigned int y0,const unsigned int z0=0,const unsigned int v0=0) const { + return get_shared_lines(y0,y0,z0,v0); + } + + //! Return a shared memory image referencing a set of planes (z0->z1,v0) of the instance image. + CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int v0=0) { + const unsigned long beg = offset(0,0,z0,v0), end = offset(0,0,z1,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_planes() : Cannot return a shared-memory subset (0->%u,0->%u,%u->%u,%u) from " + "a (%u,%u,%u,%u) image.",pixel_type(),width-1,height-1,z0,z1,v0,width,height,depth,dim); + return CImg(data+beg,width,height,z1-z0+1,1,true); + } + + //! Return a shared-memory image referencing a set of planes (z0->z1,v0) of the instance image (const version). + const CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int v0=0) const { + const unsigned long beg = offset(0,0,z0,v0), end = offset(0,0,z1,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_planes() : Cannot return a shared-memory subset (0->%u,0->%u,%u->%u,%u) from " + "a (%u,%u,%u,%u) image.",pixel_type(),width-1,height-1,z0,z1,v0,width,height,depth,dim); + return CImg(data+beg,width,height,z1-z0+1,1,true); + } + + //! Return a shared-memory image referencing one plane (z0,v0) of the instance image. + CImg get_shared_plane(const unsigned int z0, const unsigned int v0=0) { + return get_shared_planes(z0,z0,v0); + } + + //! Return a shared-memory image referencing one plane (z0,v0) of the instance image (const version). + const CImg get_shared_plane(const unsigned int z0, const unsigned int v0=0) const { + return get_shared_planes(z0,z0,v0); + } + + //! Return a shared-memory image referencing a set of channels (v0->v1) of the instance image. + CImg get_shared_channels(const unsigned int v0, const unsigned int v1) { + const unsigned long beg = offset(0,0,0,v0), end = offset(0,0,0,v1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_channels() : Cannot return a shared-memory subset (0->%u,0->%u,0->%u,%u->%u) from " + "a (%u,%u,%u,%u) image.",pixel_type(),width-1,height-1,depth-1,v0,v1,width,height,depth,dim); + return CImg(data+beg,width,height,depth,v1-v0+1,true); + } + + //! Return a shared-memory image referencing a set of channels (v0->v1) of the instance image (const version). + const CImg get_shared_channels(const unsigned int v0, const unsigned int v1) const { + const unsigned long beg = offset(0,0,0,v0), end = offset(0,0,0,v1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_channels() : Cannot return a shared-memory subset (0->%u,0->%u,0->%u,%u->%u) from " + "a (%u,%u,%u,%u) image.",pixel_type(),width-1,height-1,depth-1,v0,v1,width,height,depth,dim); + return CImg(data+beg,width,height,depth,v1-v0+1,true); + } + + //! Return a shared-memory image referencing one channel v0 of the instance image. + CImg get_shared_channel(const unsigned int v0) { + return get_shared_channels(v0,v0); + } + + //! Return a shared-memory image referencing one channel v0 of the instance image (const version). + const CImg get_shared_channel(const unsigned int v0) const { + return get_shared_channels(v0,v0); + } + + //! Return a shared version of the instance image. + CImg get_shared() { + return CImg(data,width,height,depth,dim,true); + } + + //! Return a shared version of the instance image (const version). + const CImg get_shared() const { + return CImg(data,width,height,depth,dim,true); + } + + //! Mirror an image along the specified axis. + /** + This is the in-place version of get_mirror(). + \sa get_mirror(). + **/ + CImg& mirror(const char axe='x') { + if (!is_empty()) { + T *pf,*pb,*buf=0; + switch (cimg::uncase(axe)) { + case 'x': { + pf = ptr(); pb = ptr(width-1); + for (unsigned int yzv=0; yzv::mirror() : unknow axe '%c', must be 'x','y','z' or 'v'",pixel_type(),axe); + } + if (buf) delete[] buf; + } + return *this; + } + + //! Get a mirrored version of the image, along the specified axis. + /** + \param axe Axe used to mirror the image. Can be \c 'x', \c 'y', \c 'z' or \c 'v'. + \sa mirror(). + **/ + CImg get_mirror(const char axe='x') { + return (+*this).mirror(axe); + } + + //! Translate the image + /** + This is the in-place version of get_translate(). + \sa get_translate(). + **/ + CImg& translate(const int deltax,const int deltay=0,const int deltaz=0,const int deltav=0,const int border_condition=0) { + if (!is_empty()) { + + if (deltax) // Translate along X-axis + switch (border_condition) { + case 0: + if (cimg::abs(deltax)>=(int)width) return fill(0); + if (deltax>0) cimg_forYZV(*this,y,z,k) { + std::memmove(ptr(0,y,z,k),ptr(deltax,y,z,k),(width-deltax)*sizeof(T)); + std::memset(ptr(width-deltax,y,z,k),0,deltax*sizeof(T)); + } else cimg_forYZV(*this,y,z,k) { + std::memmove(ptr(-deltax,y,z,k),ptr(0,y,z,k),(width+deltax)*sizeof(T)); + std::memset(ptr(0,y,z,k),0,-deltax*sizeof(T)); + } + break; + case 1: + if (deltax>0) { + const int ndeltax = (deltax>=(int)width)?width-1:deltax; + if (!ndeltax) return *this; + cimg_forYZV(*this,y,z,k) { + std::memmove(ptr(0,y,z,k),ptr(ndeltax,y,z,k),(width-ndeltax)*sizeof(T)); + T *ptrd = ptr(width-1,y,z,k); + const T &val = *ptrd; + for (int l=0; l=(int)width)?width-1:-deltax; + if (!ndeltax) return *this; + cimg_forYZV(*this,y,z,k) { + std::memmove(ptr(ndeltax,y,z,k),ptr(0,y,z,k),(width-ndeltax)*sizeof(T)); + T *ptrd = ptr(0,y,z,k); + const T &val = *ptrd; + for (int l=0; l0) cimg_forYZV(*this,y,z,k) { + std::memcpy(buf,ptr(0,y,z,k),ndeltax*sizeof(T)); + std::memmove(ptr(0,y,z,k),ptr(ndeltax,y,z,k),(width-ndeltax)*sizeof(T)); + std::memcpy(ptr(width-ndeltax,y,z,k),buf,ndeltax*sizeof(T)); + } else cimg_forYZV(*this,y,z,k) { + std::memcpy(buf,ptr(width+ndeltax,y,z,k),-ndeltax*sizeof(T)); + std::memmove(ptr(-ndeltax,y,z,k),ptr(0,y,z,k),(width+ndeltax)*sizeof(T)); + std::memcpy(ptr(0,y,z,k),buf,-ndeltax*sizeof(T)); + } + delete[] buf; + } break; + } + + if (deltay) // Translate along Y-axis + switch (border_condition) { + case 0: + if (cimg::abs(deltay)>=(int)height) return fill(0); + if (deltay>0) cimg_forZV(*this,z,k) { + std::memmove(ptr(0,0,z,k),ptr(0,deltay,z,k),width*(height-deltay)*sizeof(T)); + std::memset(ptr(0,height-deltay,z,k),0,width*deltay*sizeof(T)); + } else cimg_forZV(*this,z,k) { + std::memmove(ptr(0,-deltay,z,k),ptr(0,0,z,k),width*(height+deltay)*sizeof(T)); + std::memset(ptr(0,0,z,k),0,-deltay*width*sizeof(T)); + } + break; + case 1: + if (deltay>0) { + const int ndeltay = (deltay>=(int)height)?height-1:deltay; + if (!ndeltay) return *this; + cimg_forZV(*this,z,k) { + std::memmove(ptr(0,0,z,k),ptr(0,ndeltay,z,k),width*(height-ndeltay)*sizeof(T)); + T *ptrd = ptr(0,height-ndeltay,z,k), *ptrs = ptr(0,height-1,z,k); + for (int l=0; l=(int)height)?height-1:-deltay; + if (!ndeltay) return *this; + cimg_forZV(*this,z,k) { + std::memmove(ptr(0,ndeltay,z,k),ptr(0,0,z,k),width*(height-ndeltay)*sizeof(T)); + T *ptrd = ptr(0,1,z,k), *ptrs = ptr(0,0,z,k); + for (int l=0; l0) cimg_forZV(*this,z,k) { + std::memcpy(buf,ptr(0,0,z,k),width*ndeltay*sizeof(T)); + std::memmove(ptr(0,0,z,k),ptr(0,ndeltay,z,k),width*(height-ndeltay)*sizeof(T)); + std::memcpy(ptr(0,height-ndeltay,z,k),buf,width*ndeltay*sizeof(T)); + } else cimg_forZV(*this,z,k) { + std::memcpy(buf,ptr(0,height+ndeltay,z,k),-ndeltay*width*sizeof(T)); + std::memmove(ptr(0,-ndeltay,z,k),ptr(0,0,z,k),width*(height+ndeltay)*sizeof(T)); + std::memcpy(ptr(0,0,z,k),buf,-ndeltay*width*sizeof(T)); + } + delete[] buf; + } break; + } + + if (deltaz) // Translate along Z-axis + switch (border_condition) { + case 0: + if (cimg::abs(deltaz)>=(int)depth) return fill(0); + if (deltaz>0) cimg_forV(*this,k) { + std::memmove(ptr(0,0,0,k),ptr(0,0,deltaz,k),width*height*(depth-deltaz)*sizeof(T)); + std::memset(ptr(0,0,depth-deltaz,k),0,width*height*deltaz*sizeof(T)); + } else cimg_forV(*this,k) { + std::memmove(ptr(0,0,-deltaz,k),ptr(0,0,0,k),width*height*(depth+deltaz)*sizeof(T)); + std::memset(ptr(0,0,0,k),0,-deltaz*width*height*sizeof(T)); + } + break; + case 1: + if (deltaz>0) { + const int ndeltaz = (deltaz>=(int)depth)?depth-1:deltaz; + if (!ndeltaz) return *this; + cimg_forV(*this,k) { + std::memmove(ptr(0,0,0,k),ptr(0,0,ndeltaz,k),width*height*(depth-ndeltaz)*sizeof(T)); + T *ptrd = ptr(0,0,depth-ndeltaz,k), *ptrs = ptr(0,0,depth-1,k); + for (int l=0; l=(int)depth)?depth-1:-deltaz; + if (!ndeltaz) return *this; + cimg_forV(*this,k) { + std::memmove(ptr(0,0,ndeltaz,k),ptr(0,0,0,k),width*height*(depth-ndeltaz)*sizeof(T)); + T *ptrd = ptr(0,0,1,k), *ptrs = ptr(0,0,0,k); + for (int l=0; l0) cimg_forV(*this,k) { + std::memcpy(buf,ptr(0,0,0,k),width*height*ndeltaz*sizeof(T)); + std::memmove(ptr(0,0,0,k),ptr(0,0,ndeltaz,k),width*height*(depth-ndeltaz)*sizeof(T)); + std::memcpy(ptr(0,0,depth-ndeltaz,k),buf,width*height*ndeltaz*sizeof(T)); + } else cimg_forV(*this,k) { + std::memcpy(buf,ptr(0,0,depth+ndeltaz,k),-ndeltaz*width*height*sizeof(T)); + std::memmove(ptr(0,0,-ndeltaz,k),ptr(0,0,0,k),width*height*(depth+ndeltaz)*sizeof(T)); + std::memcpy(ptr(0,0,0,k),buf,-ndeltaz*width*height*sizeof(T)); + } + delete[] buf; + } break; + } + + if (deltav) // Translate along V-axis + switch (border_condition) { + case 0: + if (cimg::abs(deltav)>=(int)dim) return fill(0); + if (deltav>0) { + std::memmove(data,ptr(0,0,0,deltav),width*height*depth*(dim-deltav)*sizeof(T)); + std::memset(ptr(0,0,0,dim-deltav),0,width*height*depth*deltav*sizeof(T)); + } else cimg_forV(*this,k) { + std::memmove(ptr(0,0,0,-deltav),data,width*height*depth*(dim+deltav)*sizeof(T)); + std::memset(data,0,-deltav*width*height*depth*sizeof(T)); + } + break; + case 1: + if (deltav>0) { + const int ndeltav = (deltav>=(int)dim)?dim-1:deltav; + if (!ndeltav) return *this; + std::memmove(data,ptr(0,0,0,ndeltav),width*height*depth*(dim-ndeltav)*sizeof(T)); + T *ptrd = ptr(0,0,0,dim-ndeltav), *ptrs = ptr(0,0,0,dim-1); + for (int l=0; l=(int)dim)?dim-1:-deltav; + if (!ndeltav) return *this; + std::memmove(ptr(0,0,0,ndeltav),data,width*height*depth*(dim-ndeltav)*sizeof(T)); + T *ptrd = ptr(0,0,0,1); + for (int l=0; l0) { + std::memcpy(buf,data,width*height*depth*ndeltav*sizeof(T)); + std::memmove(data,ptr(0,0,0,ndeltav),width*height*depth*(dim-ndeltav)*sizeof(T)); + std::memcpy(ptr(0,0,0,dim-ndeltav),buf,width*height*depth*ndeltav*sizeof(T)); + } else { + std::memcpy(buf,ptr(0,0,0,dim+ndeltav),-ndeltav*width*height*depth*sizeof(T)); + std::memmove(ptr(0,0,0,-ndeltav),data,width*height*depth*(dim+ndeltav)*sizeof(T)); + std::memcpy(data,buf,-ndeltav*width*height*depth*sizeof(T)); + } + delete[] buf; + } break; + } + } + return *this; + } + + //! Return a translated image. + /** + \param deltax Amount of displacement along the X-axis. + \param deltay Amount of displacement along the Y-axis. + \param deltaz Amount of displacement along the Z-axis. + \param deltav Amount of displacement along the V-axis. + \param border_condition Border condition. + + - \c border_condition can be : + - 0 : Zero border condition (Dirichlet). + - 1 : Nearest neighbors (Neumann). + - 2 : Repeat Pattern (Fourier style). + **/ + CImg get_translate(const int deltax,const int deltay=0,const int deltaz=0,const int deltav=0, + const int border_condition=0) const { + return (+*this).translate(deltax,deltay,deltaz,deltav,border_condition); + } + + //! Return a 2D representation of a 3D image, with three slices. + CImg get_projections2d(const unsigned int px0,const unsigned int py0,const unsigned int pz0) const { + if (is_empty()) return CImg(); + const unsigned int + x0=(px0>=width)?width-1:px0, + y0=(py0>=height)?height-1:py0, + z0=(pz0>=depth)?depth-1:pz0; + CImg res(width+depth,height+depth,1,dim); + res.fill((*this)[0]); + { cimg_forXYV(*this,x,y,k) res(x,y,0,k) = (*this)(x,y,z0,k); } + { cimg_forYZV(*this,y,z,k) res(width+z,y,0,k) = (*this)(x0,y,z,k); } + { cimg_forXZV(*this,x,z,k) res(x,height+z,0,k) = (*this)(x,y0,z,k); } + return res; + } + + //! Return the image histogram. + /** + The histogram H of an image I is a 1D-function where H(x) is the number of + occurences of the value x in I. + \param nblevels = Number of different levels of the computed histogram. + For classical images, this value is 256 (default value). You should specify more levels + if you are working with CImg or images with high range of pixel values. + \param val_min = Minimum value considered for the histogram computation. All pixel values lower than val_min + won't be counted. + \param val_max = Maximum value considered for the histogram computation. All pixel values higher than val_max + won't be counted. + \note If val_min==val_max==0 (default values), the function first estimates the minimum and maximum + pixel values of the current image, then uses these values for the histogram computation. + \result The histogram is returned as a 1D CImg image H, having a size of (nblevels,1,1,1) such that + H(0) and H(nblevels-1) are respectively equal to the number of occurences of the values val_min and val_max in I. + \note Histogram computation always returns a 1D function. Histogram of multi-valued (such as color) images + are not multi-dimensional. + \see get_equalize_histogram(), equalize_histogram() + **/ + CImg get_histogram(const unsigned int nblevels=256,const T val_min=(T)0,const T val_max=(T)0) const { + if (is_empty()) return CImg(); + if (nblevels<1) + throw CImgArgumentException("CImg<%s>::get_histogram() : Can't compute an histogram with %u levels", + pixel_type(),nblevels); + T vmin=val_min, vmax=val_max; + CImg res(nblevels,1,1,1,0); + if (vmin==vmax && vmin==0) { const CImgStats st(*this,false); vmin = (T)st.min; vmax = (T)st.max; } + cimg_for(*this,ptr,T) { + const int pos = (int)((*ptr-vmin)*(nblevels-1)/(vmax-vmin)); + if (pos>=0 && pos<(int)nblevels) res[pos]++; + } + return res; + } + + //! Equalize the image histogram + /** This is the in-place version of \ref get_equalize_histogram() **/ + CImg& equalize_histogram(const unsigned int nblevels=256,const T val_min=(T)0,const T val_max=(T)0) { + if (!is_empty()) { + T vmin=val_min, vmax=val_max; + if (vmin==vmax && vmin==0) { const CImgStats st(*this,false); vmin = (T)st.min; vmax = (T)st.max; } + CImg hist = get_histogram(nblevels,vmin,vmax); + float cumul=0; + cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos]=cumul; } + cimg_for(*this,ptr,T) { + const int pos = (unsigned int)((*ptr-vmin)*(nblevels-1)/(vmax-vmin)); + if (pos>=0 && pos<(int)nblevels) *ptr = (T)(vmin + (vmax-vmin)*hist[pos]/size()); + } + } + return *this; + } + + //! Return the histogram-equalized version of the current image. + /** + The histogram equalization is a classical image processing algorithm that enhances the image contrast + by expanding its histogram. + \param nblevels = Number of different levels of the computed histogram. + For classical images, this value is 256 (default value). You should specify more levels + if you are working with CImg or images with high range of pixel values. + \param val_min = Minimum value considered for the histogram computation. All pixel values lower than val_min + won't be changed. + \param val_max = Maximum value considered for the histogram computation. All pixel values higher than val_max + won't be changed. + \note If val_min==val_max==0 (default values), the function acts on all pixel values of the image. + \return A new image with same size is returned, where pixels have been equalized. + \see get_histogram(), equalize_histogram() + **/ + CImg get_equalize_histogram(const unsigned int nblevels=256,const T val_min=(T)0,const T val_max=(T)0) const { + return (+*this).equalize_histogram(nblevels,val_min,val_max); + } + + //! Return the scalar image of vector norms. + /** + When dealing with vector-valued images (i.e images with dimv()>1), this function computes the L1,L2 or Linf norm of each + vector-valued pixel. + \param norm_type = Type of the norm being computed (1 = L1, 2 = L2, -1 = Linf). + \return A scalar-valued image CImg with size (dimx(),dimy(),dimz(),1), where each pixel is the norm + of the corresponding pixels in the original vector-valued image. + \see get_orientation_pointwise, orientation_pointwise, norm_pointwise. + **/ + CImg::type> get_norm_pointwise(int norm_type=2) const { + typedef typename cimg::largest::type restype; + if (is_empty()) return CImg(); + CImg res(width,height,depth); + switch(norm_type) { + case -1: { // Linf norm + cimg_forXYZ(*this,x,y,z) { + restype n=0; cimg_forV(*this,v) { + const restype tmp = (restype)cimg::abs((*this)(x,y,z,v)); + if (tmp>n) n=tmp; res(x,y,z) = n; + } + } + } break; + case 1: { // L1 norm + cimg_forXYZ(*this,x,y,z) { + restype n=0; cimg_forV(*this,v) n+=cimg::abs((*this)(x,y,z,v)); res(x,y,z) = n; + } + } break; + default: { // L2 norm + cimg_forXYZ(*this,x,y,z) { + restype n=0; cimg_forV(*this,v) n+=(*this)(x,y,z,v)*(*this)(x,y,z,v); res(x,y,z) = (restype)std::sqrt((double)n); + } + } break; + } + return res; + } + + //! Replace each pixel value with its vector norm. + /** + This is the in-place version of \ref get_norm_pointwise(). + \note Be careful when using this function on CImg with T=char, unsigned char,unsigned int or int. The vector norm + is usually a floating point value, and a rough cast will be done here. + **/ + CImg& norm_pointwise() { + return CImg(get_norm_pointwise()).swap(*this); + } + + //! Return the image of normalized vectors + /** + When dealing with vector-valued images (i.e images with dimv()>1), this function return the image of normalized vectors + (unit vectors). Null vectors are unchanged. The L2-norm is computed for the normalization. + \return A new vector-valued image with same size, where each vector-valued pixels have been normalized. + \see get_norm_pointwise, norm_pointwise, orientation_pointwise. + **/ + CImg::type> get_orientation_pointwise() const { + typedef typename cimg::largest::type restype; + if (is_empty()) return CImg(); + return CImg(*this,false).orientation_pointwise(); + } + + //! Replace each pixel value by its normalized vector + /** This is the in-place version of \ref get_orientation_pointwise() **/ + CImg& orientation_pointwise() { + cimg_forXYZ(*this,x,y,z) { + float n = 0.0f; + cimg_forV(*this,v) n+=(float)((*this)(x,y,z,v)*(*this)(x,y,z,v)); + n = (float)std::sqrt(n); + if (n>0) cimg_forV(*this,v) (*this)(x,y,z,v)=(T)((*this)(x,y,z,v)/n); + else cimg_forV(*this,v) (*this)(x,y,z,v)=0; + } + return *this; + } + + //! Split image into a list CImgList<>. + CImgList get_split(const char axe='x', const unsigned int nb=0) const { + if (is_empty()) return CImgList(); + CImgList res; + switch (cimg::uncase(axe)) { + case 'x': { + if (nb>width) + throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'x' into %u images.", + pixel_type(),width,height,depth,dim,data,nb); + res.assign(nb?nb:width); + const unsigned int delta = width/res.size + ((width%res.size)?1:0); + unsigned int l,x; + for (l=0, x=0; lheight) + throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'y' into %u images.", + pixel_type(),width,height,depth,dim,data,nb); + res.assign(nb?nb:height); + const unsigned int delta = height/res.size + ((height%res.size)?1:0); + unsigned int l,x; + for (l=0, x=0; ldepth) + throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'z' into %u images.", + pixel_type(),width,height,depth,dim,data,nb); + res.assign(nb?nb:depth); + const unsigned int delta = depth/res.size + ((depth%res.size)?1:0); + unsigned int l,x; + for (l=0, x=0; ldim) + throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'v' into %u images.", + pixel_type(),width,height,depth,dim,data,nb); + res.assign(nb?nb:dim); + const unsigned int delta = dim/res.size + ((dim%res.size)?1:0); + unsigned int l,x; + for (l=0, x=0; l::get_split() : Unknow axe '%c', must be 'x','y','z' or 'v'",pixel_type(),axe); + break; + } + return res; + } + + //! Append an image to another one + CImg get_append(const CImg& img, const char axis='x', const char align='c') const { + if (img.is_empty()) return *this; + if (is_empty()) return img; + CImgList temp(2); + temp[0].width = width; temp[0].height = height; temp[0].depth = depth; + temp[0].dim = dim; temp[0].data = data; + temp[1].width = img.width; temp[1].height = img.height; temp[1].depth = img.depth; + temp[1].dim = img.dim; temp[1].data = img.data; + const CImg res = temp.get_append(axis,align); + temp[0].width = temp[0].height = temp[0].depth = temp[0].dim = 0; temp[0].data = 0; + temp[1].width = temp[1].height = temp[1].depth = temp[1].dim = 0; temp[1].data = 0; + return res; + } + + //! Append an image to another one (in-place version) + CImg& append(const CImg& img, const char axis='x', const char align='c') { + if (img.is_empty()) return *this; + if (is_empty()) return (*this=img); + return get_append(img,axis,align).swap(*this); + } + + //! Return a list of images, corresponding to the XY-gradients of an image. + /** + \param scheme = Numerical scheme used for the gradient computation : + - -1 = Backward finite differences + - 0 = Centered finite differences + - 1 = Forward finite differences + - 2 = Using Sobel masks + - 3 = Using rotation invariant masks + - 4 = Using Deriche recusrsive filter. + **/ + CImgList::type> get_gradientXY(const int scheme=0) const { + typedef typename cimg::largest::type restype; + if (is_empty()) return CImgList(2); + CImgList res(2,width,height,depth,dim); + switch(scheme) { + case -1: { // backward finite differences + CImg_3x3(I,restype); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = Icc-Ipc; res[1](x,y,z,k) = Icc-Icp; } + } break; + case 1: { // forward finite differences + CImg_2x2(I,restype); + cimg_forZV(*this,z,k) cimg_for2x2(*this,x,y,z,k,I) { res[0](x,y,0,k) = Inc-Icc; res[1](x,y,z,k) = Icn-Icc; } + } break; + case 2: { // using Sobel mask + CImg_3x3(I,restype); + const float a = 1, b = 2; + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { + res[0](x,y,z,k) = -a*Ipp-b*Ipc-a*Ipn+a*Inp+b*Inc+a*Inn; + res[1](x,y,z,k) = -a*Ipp-b*Icp-a*Inp+a*Ipn+b*Icn+a*Inn; + } + } break; + case 3: { // using rotation invariant mask + CImg_3x3(I,restype); + const float a = (float)(0.25*(2-std::sqrt(2.0))), b = (float)(0.5f*(std::sqrt(2.0)-1)); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { + res[0](x,y,z,k) = -a*Ipp-b*Ipc-a*Ipn+a*Inp+b*Inc+a*Inn; + res[1](x,y,z,k) = -a*Ipp-b*Icp-a*Inp+a*Ipn+b*Icn+a*Inn; + } + } break; + case 4: { // using Deriche filter with low standard variation + res[0] = get_deriche(0,1,'x'); + res[1] = get_deriche(0,1,'y'); + } break; + default: { // central finite differences + CImg_3x3(I,restype); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { + res[0](x,y,z,k) = 0.5f*(Inc-Ipc); + res[1](x,y,z,k) = 0.5f*(Icn-Icp); + } + } break; + } + return res; + } + + //! Return a list of images, corresponding to the XYZ-gradients of an image. + /** + \see get_gradientXY(). + **/ + CImgList::type> get_gradientXYZ(const int scheme=0) const { + typedef typename cimg::largest::type restype; + if (is_empty()) return CImgList(3); + CImgList res(3,width,height,depth,dim); + CImg_3x3x3(I,restype); + switch(scheme) { + case -1: { // backward finite differences + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + res[0](x,y,z,k) = Iccc-Ipcc; + res[1](x,y,z,k) = Iccc-Icpc; + res[2](x,y,z,k) = Iccc-Iccp; + } + } break; + case 1: { // forward finite differences + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + res[0](x,y,z,k) = Incc-Iccc; + res[1](x,y,z,k) = Icnc-Iccc; + res[2](x,y,z,k) = Iccn-Iccc; + } + } break; + case 4: { // using Deriche filter with low standard variation + res[0] = get_deriche(0,1,'x'); + res[1] = get_deriche(0,1,'y'); + res[2] = get_deriche(0,1,'z'); + } break; + default: { // central finite differences + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + res[0](x,y,z,k) = 0.5f*(Incc-Ipcc); + res[1](x,y,z,k) = 0.5f*(Icnc-Icpc); + res[2](x,y,z,k) = 0.5f*(Iccn-Iccp); + } + } break; + } + return res; + } + + //! Return the 2D structure tensor field of an image + CImg::type> get_structure_tensorXY(const int scheme=1) const { + typedef typename cimg::largest::type restype; + if (is_empty()) return CImg(); + CImg res(width,height,depth,3,0); + CImg_3x3(I,restype); + switch (scheme) { + case 0: { // classical central finite differences + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,0,k,I) { + const restype + ix = 0.5f*(Inc-Ipc), + iy = 0.5f*(Icn-Icp); + res(x,y,z,0)+=ix*ix; + res(x,y,z,1)+=ix*iy; + res(x,y,z,2)+=iy*iy; + } + } break; + default: { // Precise forward/backward finite differences + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,0,k,I) { + const restype + ixf = Inc-Icc, ixb = Icc-Ipc, + iyf = Icn-Icc, iyb = Icc-Icp; + res(x,y,z,0) += 0.5f*(ixf*ixf+ixb*ixb); + res(x,y,z,1) += 0.25f*(ixf*iyf+ixf*iyb+ixb*iyf+ixb*iyb); + res(x,y,z,2) += 0.5f*(iyf*iyf+iyb*iyb); + } + } break; + } + return res; + } + + //! In-place version of the previous function + CImg& structure_tensorXY(const int scheme=1) { + return get_structure_tensorXY(scheme).swap(*this); + } + + //! Return the 3D structure tensor field of an image + CImg::type> get_structure_tensorXYZ(const int scheme=1) const { + typedef typename cimg::largest::type restype; + if (is_empty()) return CImg(); + CImg res(width,height,depth,6,0); + CImg_3x3x3(I,restype); + switch (scheme) { + case 0: { // classical central finite differences + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + const restype + ix = 0.5f*(Incc-Ipcc), + iy = 0.5f*(Icnc-Icpc), + iz = 0.5f*(Iccn-Iccp); + res(x,y,z,0)+=ix*ix; + res(x,y,z,1)+=ix*iy; + res(x,y,z,2)+=ix*iz; + res(x,y,z,3)+=iy*iy; + res(x,y,z,4)+=iy*iz; + res(x,y,z,5)+=iz*iz; + } + } break; + default: { // Precise forward/backward finite differences + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + const restype + ixf = Incc-Iccc, ixb = Iccc-Ipcc, + iyf = Icnc-Iccc, iyb = Iccc-Icpc, + izf = Iccn-Iccc, izb = Iccc-Iccp; + res(x,y,z,0) += 0.5f*(ixf*ixf + ixb*ixb); + res(x,y,z,1) += 0.25f*(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb); + res(x,y,z,2) += 0.25f*(ixf*izf + ixf*izb + ixb*izf + ixb*izb); + res(x,y,z,3) += 0.5f*(iyf*iyf + iyb*iyb); + res(x,y,z,4) += 0.25f*(iyf*izf + iyf*izb + iyb*izf + iyb*izb); + res(x,y,z,5) += 0.5f*(izf*izf + izb*izb); + } + } break; + } + return res; + } + + //! In-place version of the previous function + CImg& structure_tensorXYZ(const int scheme=1) { + return get_structure_tensorXYZ(scheme).swap(*this); + } + + //@} + //------------------------------------- + // + //! \name Meshes and Triangulations + //@{ + //------------------------------------- + + struct _marching_squares_func { + const CImg& ref; + _marching_squares_func(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref((int)x,(int)y); + } + }; + + struct _marching_cubes_func { + const CImg& ref; + _marching_cubes_func(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref((int)x,(int)y,(int)z); + } + }; + + struct _marching_squares_func_float { + const CImg& ref; + _marching_squares_func_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref.linear_pix2d(x,y); + } + }; + + struct _marching_cubes_func_float { + const CImg& ref; + _marching_cubes_func_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref.linear_pix3d(x,y,z); + } + }; + + //! Get a vectorization of an implicit function defined by the instance image. + template + const CImg& marching_squares(const float isovalue,CImgList& points, CImgList& primitives) const { + if (height<=1 || depth>1 || dim>1) + throw CImgInstanceException("CImg<%s>::marching_squares() : Instance image (%u,%u,%u,%u,%p) is not a 2D scalar image.", + pixel_type(),width,height,depth,dim,data); + const _marching_squares_func func(*this); + cimg::marching_squares(func,isovalue,0.0f,0.0f,dimx()-1.0f,dimy()-1.0f,1.0f,1.0f,points,primitives); + return *this; + } + + //! Get a vectorization of an implicit function defined by the instance image. + /** + This version allows to specify the marching squares resolution along x,y, and z. + **/ + template + const CImg& marching_squares(const float isovalue, + const float resx, const float resy, + CImgList& points, CImgList& primitives) const { + if (height<=1 || depth>1 || dim>1) + throw CImgInstanceException("CImg<%s>::marching_squares() : Instance image (%u,%u,%u,%u,%p) is not a 2D scalar image.", + pixel_type(),width,height,depth,dim,data); + const _marching_squares_func_float func(*this); + cimg::marching_squares(func,isovalue,0.0f,0.0f,dimx()-1.0f,dimy()-1.0f,resx,resy,points,primitives); + return *this; + } + + //! Get a triangulation of an implicit function defined by the instance image + template + const CImg& marching_cubes(const float isovalue,CImgList& points, CImgList& primitives, + const bool invert_faces = false) const { + if (depth<=1 || dim>1) + throw CImgInstanceException("CImg<%s>::marching_cubes() : Instance image (%u,%u,%u,%u,%p) is not a 3D scalar image.", + pixel_type(),width,height,depth,dim,data); + const _marching_cubes_func func(*this); + cimg::marching_cubes(func,isovalue,0.0f,0.0f,0.0f,dimx()-1.0f,dimy()-1.0f,dimz()-1.0f, + 1.0f,1.0f,1.0f,points,primitives,invert_faces); + return *this; + } + + //! Get a triangulation of an implicit function defined by the instance image + /** + This version allows to specify the marching cube resolution along x,y and z. + **/ + template + const CImg& marching_cubes(const float isovalue, + const float resx, const float resy, const float resz, + CImgList& points, CImgList& primitives, + const bool invert_faces = false) const { + if (depth<=1 || dim>1) + throw CImgInstanceException("CImg<%s>::marching_cubes() : Instance image (%u,%u,%u,%u,%p) is not a 3D scalar image.", + pixel_type(),width,height,depth,dim,data); + const _marching_cubes_func_float func(*this); + cimg::marching_cubes(func,isovalue,0.0f,0.0f,0.0f,dimx()-1.0f,dimy()-1.0f,dimz()-1.0f, + resx,resy,resz,points,primitives,invert_faces); + return *this; + } + + //@} + //---------------------------- + // + //! \name Color conversions + //@{ + //---------------------------- + + //! Return the default 256 colors palette. + /** + The default color palette is used by %CImg when displaying images on 256 colors displays. + It consists in the quantification of the (R,G,B) color space using 3:3:2 bits for color coding + (i.e 8 levels for the Red and Green and 4 levels for the Blue). + \return A 256x1x1x3 color image defining the palette entries. + **/ + static CImg get_default_LUT8() { + static CImg palette; + if (!palette.data) { + palette.assign(256,1,1,3); + for (unsigned int index=0, r=16; r<256; r+=32) + for (unsigned int g=16; g<256; g+=32) + for (unsigned int b=32; b<256; b+=64) { + palette(index,0) = r; + palette(index,1) = g; + palette(index++,2) = b; + } + } + return palette; + } + + //! Convert color pixels from (R,G,B) to match a specified palette. + /** + This function return a (R,G,B) image where colored pixels are constrained to match entries + of the specified color \c palette. + \param palette User-defined palette that will constraint the color conversion. + \param dithering Enable/Disable Floyd-Steinberg dithering. + \param indexing If \c true, each resulting image pixel is an index to the given color palette. + Otherwise, (R,G,B) values of the palette are copied instead. + **/ + template CImg get_RGBtoLUT(const CImg& palette, const bool dithering=true,const bool indexing=false) const { + if (is_empty()) return CImg(); + if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoLUT() : Input image dimension is dim=%u, " + "should be a (R,G,B) image.",pixel_type(),dim); + if (palette.data && palette.dim!=3) + throw CImgArgumentException("CImg<%s>::RGBtoLUT() : Given palette dimension is dim=%u, " + "should be a (R,G,B) palette",pixel_type(),palette.dim); + CImg res(width,height,depth,indexing?1:3), pal = palette.data?palette:CImg::get_default_LUT8(); + float *line1 = new float[3*width], *line2 = new float[3*width], *pline1 = line1, *pline2 = line2; + cimg_forZ(*this,z) { + float *ptr=pline2; cimg_forX(*this,x) { *(ptr++)=(*this)(x,0,z,0); *(ptr++)=(*this)(x,0,z,1); *(ptr++)=(*this)(x,0,z,2); } + cimg_forY(*this,y) { + cimg::swap(pline1,pline2); + if (y255?255:R); G = G<0?0:(G>255?255:G); B = B<0?0:(B>255?255:B); + int best_index = 0; + t Rbest=0,Gbest=0,Bbest=0; + if (palette.data) { // find best match in given color palette + float min = cimg::type::max(); + cimg_forX(palette,off) { + const t Rp = palette(off,0), Gp = palette(off,1), Bp = palette(off,2); + const float error = (float)((Rp-R)*(Rp-R) + (Gp-G)*(Gp-G) + (Bp-B)*(Bp-B)); + if (error>3) | ((unsigned char)Bbest>>6); + } + if (indexing) res(x,y,z) = best_index; + else { res(x,y,z,0) = Rbest; res(x,y,z,1) = Gbest; res(x,y,z,2) = Bbest; } + if (dithering) { // apply dithering to neighborhood pixels if needed + const float dR = (float)(R-Rbest), dG = (float)(G-Gbest), dB = (float)(B-Bbest); + if (x0) { *(--ptr2)+= dB*3/16; *(--ptr2)+= dG*3/16; *(--ptr2)+= dR*3/16; ptr2+=3; } + if (x get_RGBtoLUT(const bool dithering=true, const bool indexing=false) const { + CImg foo; + return get_RGBtoLUT(foo,dithering,indexing); + } + + //! Convert color pixels from (R,G,B) to match the specified color palette. + /** This is the in-place version of get_RGBtoLUT(). **/ + CImg& RGBtoLUT(const CImg& palette,const bool dithering=true,const bool indexing=false) { + return get_RGBtoLUT(palette,dithering,indexing).swap(*this); + } + + //! Convert color pixels from (R,G,B) to match the specified color palette. + /** This is the in-place version of get_RGBtoLUT(). **/ + CImg& RGBtoLUT(const bool dithering=true,const bool indexing=false) { + CImg foo; + return get_RGBtoLUT(foo,dithering,indexing).swap(*this); + } + + //! Convert an indexed image to a (R,G,B) image using the specified color palette. + template CImg get_LUTtoRGB(const CImg& palette) const { + if (is_empty()) return CImg(); + if (dim!=1) throw CImgInstanceException("CImg<%s>::LUTtoRGB() : Input image dimension is dim=%u, " + "should be a LUT image",pixel_type(),dim); + if (palette.data && palette.dim!=3) + throw CImgArgumentException("CImg<%s>::LUTtoRGB() : Given palette dimension is dim=%u, " + "should be a (R,G,B) palette",pixel_type(),palette.dim); + CImg res(width,height,depth,3); + CImg pal = palette.data?palette:get_default_LUT8(); + cimg_forXYZ(*this,x,y,z) { + const unsigned int index = (unsigned int)(*this)(x,y,z); + res(x,y,z,0) = pal(index,0); + res(x,y,z,1) = pal(index,1); + res(x,y,z,2) = pal(index,2); + } + return res; + } + + //! Convert an indexed image (with the default palette) to a (R,G,B) image. + CImg get_LUTtoRGB() const { + CImg foo; + return get_LUTtoRGB(foo); + } + + //! In-place version of get_LUTtoRGB(). + CImg& LUTtoRGB(const CImg& palette) { + return get_LUTtoRGB(palette).swap(*this); + } + + //! In-place version of get_LUTroRGB(). + CImg& LUTtoRGB() { + CImg foo; + return get_LUTtoRGB(foo).swap(*this); + } + + //! Convert color pixels from (R,G,B) to (H,S,V). + CImg& RGBtoHSV() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoHSV() : Input image dimension is dim=%u, " + "should be a (R,G,B) image.",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + const float + R = (float)((*this)(x,y,z,0)/255.0f), + G = (float)((*this)(x,y,z,1)/255.0f), + B = (float)((*this)(x,y,z,2)/255.0f); + const float m = cimg::min(R,G,B), v = cimg::max(R,G,B); + float h,s; + if (v==m) { h=-1; s=0; } else { + const float + f = (R==m)?(G-B):((G==m)?(B-R):(R-G)), + i = (R==m)?3.0f:((G==m)?5.0f:1.0f); + h = (i-f/(v-m)); + s = (v-m)/v; + if (h>=6.0f) h-=6.0f; + h*=(float)cimg::PI/3.0f; + } + (*this)(x,y,z,0) = (T)h; + (*this)(x,y,z,1) = (T)s; + (*this)(x,y,z,2) = (T)v; + } + } + return *this; + } + + //! Convert color pixels from (H,S,V) to (R,G,B). + CImg& HSVtoRGB() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::HSVtoRGB() : Input image dimension is dim=%u, " + "should be a (H,S,V) image",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + float + H = (float)((*this)(x,y,z,0)), + S = (float)((*this)(x,y,z,1)), + V = (float)((*this)(x,y,z,2)); + float R=0,G=0,B=0; + if (H<0) R=G=B=V; + else { + H/=(float)cimg::PI/3.0f; + const int i = (int)std::floor(H); + const float + f = (i&1)?(H-i):(1.0f-H+i), + m = V*(1.0f-S), + n = V*(1.0f-S*f); + switch(i) { + case 6: + case 0: R=V; G=n; B=m; break; + case 1: R=n; G=V; B=m; break; + case 2: R=m; G=V; B=n; break; + case 3: R=m; G=n; B=V; break; + case 4: R=n; G=m; B=V; break; + case 5: R=V; G=m; B=n; break; + } + } + (*this)(x,y,z,0) = (T)(R*255.0f); + (*this)(x,y,z,1) = (T)(G*255.0f); + (*this)(x,y,z,2) = (T)(B*255.0f); + } + } + return *this; + } + + //! Convert color pixels from (R,G,B) to (Y,Cb,Cr)_8 (Thanks to Chen Wang). + CImg& RGBtoYCbCr() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoYCbCr() : Input image dimension is dim=%u, " + "should be a (R,G,B) image (dim=3)",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + const int + R = (int)((*this)(x,y,z,0)), + G = (int)((*this)(x,y,z,1)), + B = (int)((*this)(x,y,z,2)); + const int + Y = ((66*R+129*G+25*B+128)>>8) + 16, + Cb = ((-38*R-74*G+112*B+128)>>8) + 128, + Cr = ((112*R-94*G-18*B+128)>>8) + 128; + (*this)(x,y,z,0) = (T)(Y<0?0:(Y>255?255:Y)); + (*this)(x,y,z,1) = (T)(Cb<0?0:(Cb>255?255:Cb)); + (*this)(x,y,z,2) = (T)(Cr<0?0:(Cr>255?255:Cr)); + } + } + return *this; + } + + //! Convert color pixels from (Y,Cb,Cr)_8 to (R,G,B). + CImg& YCbCrtoRGB() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::YCbCrtoRGB() : Input image dimension is dim=%u, " + "should be a (Y,Cb,Cr)_8 image (dim=3)",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + const int + Y = (int)((*this)(x, y, z, 0)-16), + Cb = (int)((*this)(x, y, z, 1)-128), + Cr = (int)((*this)(x, y, z, 2)-128); + const int + R = ((298*Y + 409*Cr + 128) >> 8 ), + G = ((298*Y - 100*Cb - 208*Cr + 128) >> 8 ), + B = ((298*Y + 516*Cb + 128) >> 8 ); + (*this)(x,y,z,0) = (T)(R<0?0:(R>255?255:R)); + (*this)(x,y,z,1) = (T)(G<0?0:(G>255?255:G)); + (*this)(x,y,z,2) = (T)(B<0?0:(B>255?255:B)); + } + } + return *this; + } + + //! Convert color pixels from (R,G,B) to (Y,U,V). + CImg& RGBtoYUV() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoYUV() : Input image dimension is dim=%u, " + "should be a (R,G,B) image (dim=3)",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + const float + R = (*this)(x,y,z,0)/255.0f, + G = (*this)(x,y,z,1)/255.0f, + B = (*this)(x,y,z,2)/255.0f, + Y = (T)(0.299*R + 0.587*G + 0.114*B); + (*this)(x,y,z,0) = Y; + (*this)(x,y,z,1) = (T)(0.492*(B-Y)); + (*this)(x,y,z,2) = (T)(0.877*(R-Y)); + } + } + return *this; + } + + //! Convert color pixels from (Y,U,V) to (R,G,B). + CImg& YUVtoRGB() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::YUVtoRGB() : Input image dimension is dim=%u, " + "should be a (Y,U,V) image (dim=3)",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + const T Y = (*this)(x,y,z,0), U = (*this)(x,y,z,1), V = (*this)(x,y,z,2); + (*this)(x,y,z,0) = (T)((Y + 1.140*V)*255.0f); + (*this)(x,y,z,1) = (T)((Y - 0.395*U - 0.581*V)*255.0f); + (*this)(x,y,z,2) = (T)((Y + 2.032*U)*255.0f); + } + } + return *this; + } + + //! Convert color pixels from (R,G,B) to (X,Y,Z)_709. + CImg& RGBtoXYZ() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoXYZ() : Input image dimension is dim=%u, " + "should be a (R,G,B) image (dim=3)",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + const float + R = (float)((*this)(x,y,z,0)/255.0f), + G = (float)((*this)(x,y,z,1)/255.0f), + B = (float)((*this)(x,y,z,2)/255.0f); + (*this)(x,y,z,0) = (T)(0.412453*R + 0.357580*G + 0.180423*B); + (*this)(x,y,z,1) = (T)(0.212671*R + 0.715160*G + 0.072169*B); + (*this)(x,y,z,2) = (T)(0.019334*R + 0.119193*G + 0.950227*B); + } + } + return *this; + } + + //! Convert (X,Y,Z)_709 pixels of a color image into the (R,G,B) color space. + CImg& XYZtoRGB() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::XYZtoRGB() : Input image dimension is dim=%u, " + "should be a (X,Y,Z) image (dim=3)",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + const float + X = (float)(255.0f*(*this)(x,y,z,0)), + Y = (float)(255.0f*(*this)(x,y,z,1)), + Z = (float)(255.0f*(*this)(x,y,z,2)); + (*this)(x,y,z,0) = (T)(3.240479*X - 1.537150*Y - 0.498535*Z); + (*this)(x,y,z,1) = (T)(-0.969256*X + 1.875992*Y + 0.041556*Z); + (*this)(x,y,z,2) = (T)(0.055648*X - 0.204043*Y + 1.057311*Z); + } + } + return *this; + } + + //! Convert (X,Y,Z)_709 pixels of a color image into the (L*,a*,b*) color space. +#define cimg_Labf(x) ((x)>=0.008856?(std::pow(x,1/3.0)):(7.787*(x)+16.0/116.0)) +#define cimg_Labfi(x) ((x)>=0.206893?((x)*(x)*(x)):(((x)-16.0/116.0)/7.787)) + + CImg& XYZtoLab() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::XYZtoLab() : Input image dimension is dim=%u, " + "should be a (X,Y,Z) image (dim=3)",pixel_type(),dim); + const double + Xn = 0.412453 + 0.357580 + 0.180423, + Yn = 0.212671 + 0.715160 + 0.072169, + Zn = 0.019334 + 0.119193 + 0.950227; + cimg_forXYZ(*this,x,y,z) { + const T X = (*this)(x,y,z,0), Y = (*this)(x,y,z,1), Z = (*this)(x,y,z,2); + const double + XXn = X/Xn, YYn = Y/Yn, ZZn = Z/Zn, + fX = cimg_Labf(XXn), fY = cimg_Labf(YYn), fZ = cimg_Labf(ZZn); + (*this)(x,y,z,0) = (T)(116*fY-16); + (*this)(x,y,z,1) = (T)(500*(fX-fY)); + (*this)(x,y,z,2) = (T)(200*(fY-fZ)); + } + } + return *this; + } + + //! Convert (L,a,b) pixels of a color image into the (X,Y,Z) color space. + CImg& LabtoXYZ() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::LabtoXYZ() : Input image dimension is dim=%u, " + "should be a (X,Y,Z) image (dim=3)",pixel_type(),dim); + const double + Xn = 0.412453 + 0.357580 + 0.180423, + Yn = 0.212671 + 0.715160 + 0.072169, + Zn = 0.019334 + 0.119193 + 0.950227; + cimg_forXYZ(*this,x,y,z) { + const T L = (*this)(x,y,z,0), a = (*this)(x,y,z,1), b = (*this)(x,y,z,2); + const double + cY = (L+16)/116.0, + Y = Yn*cimg_Labfi(cY), + pY = std::pow(Y/Yn,1.0/3), + cX = a/500+pY, + X = Xn*cX*cX*cX, + cZ = pY-b/200, + Z = Zn*cZ*cZ*cZ; + (*this)(x,y,z,0) = (T)(X); + (*this)(x,y,z,1) = (T)(Y); + (*this)(x,y,z,2) = (T)(Z); + } + } + return *this; + } + + //! Convert (X,Y,Z)_709 pixels of a color image into the (x,y,Y) color space. + CImg& XYZtoxyY() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::XYZtoxyY() : Input image dimension is dim=%u, " + "should be a (X,Y,Z) image (dim=3)",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + const T X = (*this)(x,y,z,0), Y = (*this)(x,y,z,1), Z = (*this)(x,y,z,2), sum = (X+Y+Z), nsum = sum>0?sum:1; + (*this)(x,y,z,0) = X/nsum; + (*this)(x,y,z,1) = Y/nsum; + (*this)(x,y,z,2) = Y; + } + } + return *this; + } + + //! Convert (x,y,Y) pixels of a color image into the (X,Y,Z)_709 color space. + CImg& xyYtoXYZ() { + if (!is_empty()) { + if (dim!=3) throw CImgInstanceException("CImg<%s>::xyYtoXYZ() : Input image dimension is dim=%u, " + "should be a (x,y,Y) image (dim=3)",pixel_type(),dim); + cimg_forXYZ(*this,x,y,z) { + const T px = (*this)(x,y,z,0), py = (*this)(x,y,z,1), Y = (*this)(x,y,z,2), ny = py>0?py:1; + (*this)(x,y,z,0) = (T)(px*Y/ny); + (*this)(x,y,z,1) = Y; + (*this)(x,y,z,2) = (T)((1-px-py)*Y/ny); + } + } + return *this; + } + + //! In-place version of get_RGBtoLab(). + CImg& RGBtoLab() { + return RGBtoXYZ().XYZtoLab(); + } + + //! In-place version of get_LabtoRGb(). + CImg& LabtoRGB() { + return LabtoXYZ().XYZtoRGB(); + } + + //! In-place version of get_RGBtoxyY(). + CImg& RGBtoxyY() { + return RGBtoXYZ().XYZtoxyY(); + } + + //! In-place version of get_xyYtoRGB(). + CImg& xyYtoRGB() { + return xyYtoXYZ().XYZtoRGB(); + } + + //! Convert a (R,G,B) image to a (H,S,V) one. + CImg get_RGBtoHSV() const { + return (+*this).RGBtoHSV(); + } + + //! Convert a (H,S,V) image to a (R,G,B) one. + CImg get_HSVtoRGB() const { + return (+*this).HSVtoRGB(); + } + + //! Convert a (R,G,B) image to a (Y,Cb,Cr) one. + CImg get_RGBtoYCbCr() const { + return (+*this).RGBtoYCbCr(); + } + + //! Convert a (Y,Cb,Cr) image to a (R,G,B) one. + CImg get_YCbCrtoRGB() const { + return (+*this).YCbCrtoRGB(); + } + + //! Convert a (R,G,B) image into a (Y,U,V) one. + CImg::type> get_RGBtoYUV() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).RGBtoYUV(); + } + + //! Convert a (Y,U,V) image into a (R,G,B) one. + CImg get_YUVtoRGB() const { + return (+*this).YUVtoRGB(); + } + + //! Convert a (R,G,B) image to a (X,Y,Z) one. + CImg::type> get_RGBtoXYZ() const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).RGBtoXYZ(); + } + + //! Convert a (X,Y,Z) image to a (R,G,B) one. + CImg get_XYZtoRGB() const { + return (+*this).XYZtoRGB(); + } + + //! Convert a (X,Y,Z) image to a (L,a,b) one. + CImg get_XYZtoLab() const { + return (+*this).XYZtoLab(); + } + + //! Convert a (L,a,b) image to a (X,Y,Z) one. + CImg get_LabtoXYZ() const { + return (+*this).LabtoXYZ(); + } + + //! Convert a (X,Y,Z) image to a (x,y,Y) one. + CImg get_XYZtoxyY() const { + return (+*this).XYZtoxyY(); + } + + //! Convert a (x,y,Y) image to a (X,Y,Z) one. + CImg get_xyYtoXYZ() const { + return (+*this).xyYtoXYZ(); + } + + //! Convert a (R,G,B) image to a (L,a,b) one. + CImg get_RGBtoLab() const { + return (+*this).RGBtoLab(); + } + + //! Convert a (L,a,b) image to a (R,G,B) one. + CImg get_LabtoRGB() const { + return (+*this).LabtoRGB(); + } + + //! Convert a (R,G,B) image to a (x,y,Y) one. + CImg get_RGBtoxyY() const { + return (+*this).RGBtoxyY(); + } + + //! Convert a (x,y,Y) image to a (R,G,B) one. + CImg get_xyYtoRGB() const { + return (+*this).xyYtoRGB(); + } + + //@} + //------------------- + // + //! \name Drawing + //@{ + //------------------- + + // Should be used only by member functions. Not an user-friendly function. + // Pre-requisites : x0=0) { + if (opacity>=1) { + int off = whz-dx-1; + if (sizeof(T)!=1) cimg_forV(*this,k) { + const T val = (T)(*(col++)*brightness); + for (int x=dx; x>=0; x--) *(ptrd++)=val; + ptrd+=off; + } else cimg_forV(*this,k) { std::memset(ptrd,(int)(*(col++)*brightness),dx+1); ptrd+=whz; } + col-=dim; + } else { + int off = whz-dx-1; + cimg_forV(*this,k) { + const T val = (T)(*(col++)*brightness); + for (int x=dx; x>=0; x--) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ptrd++; } + ptrd+=off; + } + col-=dim; + } + } + } + return *this; + } + + CImg& draw_scanline(const T *const color,const float opacity=1) { return draw_scanline(0,0,0,color,opacity,1.0f,true); } + + //! Draw a colored point in the instance image, at coordinates (\c x0,\c y0,\c z0). + /** + \param x0 = X-coordinate of the vector-valued pixel to plot. + \param y0 = Y-coordinate of the vector-valued pixel to plot. + \param z0 = Z-coordinate of the vector-valued pixel to plot. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + CImg& draw_point(const int x0,const int y0,const int z0, + const T *const color,const float opacity=1) { + if (!is_empty()) { + if (!color) throw CImgArgumentException("CImg<%s>::draw_point() : Specified color is (null)",pixel_type()); + if (x0>=0 && y0>=0 && z0>=0 && x0=1) cimg_forV(*this,k) { *ptrd = *(col++); ptrd+=whz; } + else cimg_forV(*this,k) { *ptrd=(T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } + } + } + return *this; + } + + //! Draw a colored point in the instance image, at coordinates (\c x0,\c y0). + /** + \param x0 = X-coordinate of the vector-valued pixel to plot. + \param y0 = Y-coordinate of the vector-valued pixel to plot. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + CImg& draw_point(const int x0,const int y0,const T *const color,const float opacity=1) { + return draw_point(x0,y0,0,color,opacity); + } + + //! Draw a 2D colored line in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1). + /** + \param x0 = X-coordinate of the starting point of the line. + \param y0 = Y-coordinate of the starting point of the line. + \param x1 = X-coordinate of the ending point of the line. + \param y1 = Y-coordinate of the ending point of the line. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param pattern = An integer whose bits describes the line pattern. + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + CImg& draw_line(const int x0,const int y0,const int x1,const int y1, + const T *const color,const unsigned int pattern=~0L,const float opacity=1) { + if (!is_empty()) { + if (!color) throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null)",pixel_type()); + const T* col=color; + int nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1; + + if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1); + if (nx1<0 || nx0>=dimx()) return *this; + if (nx0<0) { ny0-=nx0*(ny1-ny0)/(nx1-nx0); nx0=0; } + if (nx1>=dimx()) { ny1+=(nx1-dimx())*(ny0-ny1)/(nx1-nx0); nx1=dimx()-1;} + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1); + if (ny1<0 || ny0>=dimy()) return *this; + if (ny0<0) { nx0-=ny0*(nx1-nx0)/(ny1-ny0); ny0=0; } + if (ny1>=dimy()) { nx1+=(ny1-dimy())*(nx0-nx1)/(ny1-ny0); ny1=dimy()-1;} + + const bool steep = (ny1-ny0)>cimg::abs(nx1-nx0); + if (steep) cimg::swap(nx0,ny0,nx1,ny1); + if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1); + const int + dx = nx1-nx0, dy = cimg::abs(ny1-ny0), + offx = steep?width:1, + offy = (ny0=1) cimg_forV(*this,k) { + unsigned int hatch=1; + T *ptrd = steep?ptr(ny0,nx0,0,k):ptr(nx0,ny0,0,k); + const T c = *(col++); + for (int error=0, x=nx0; x<=nx1; x++) { + if (!(~pattern) || (~pattern && pattern&hatch)) *ptrd=c; + ptrd+=offx; + if (((error+=dy)<<1)>=dx) { ptrd+=offy; error-=dx; } + if (pattern) hatch=(hatch<<1)+(hatch>>(sizeof(unsigned int)*8-1)); + } + } else { + const float nopacity = cimg::abs(opacity), copacity=1-cimg::max(opacity,0.0f); + cimg_forV(*this,k) { + unsigned int hatch=1; + T *ptrd = steep?ptr(ny0,nx0,0,k):ptr(nx0,ny0,0,k); + const T c = *(col++); + for (int error=0, x=nx0; x<=nx1; x++) { + if (!(~pattern) || (~pattern && pattern&hatch)) *ptrd = (T)(c*nopacity + copacity*(*ptrd)); + ptrd+=offx; + if (((error+=dy)<<1)>=dx) { ptrd+=offy; error-=dx; } + if (pattern) hatch=(hatch<<1)+(hatch>>(sizeof(unsigned int)*8-1)); + } + } + } + } + return *this; + } + + //! Draw a 3D colored line in the instance image, at coordinates (\c x0,\c y0,\c z0)-(\c x1,\c y1,\c z1). + /** + \param x0 = X-coordinate of the starting point of the line. + \param y0 = Y-coordinate of the starting point of the line. + \param z0 = Z-coordinate of the starting point of the line. + \param x1 = X-coordinate of the ending point of the line. + \param y1 = Y-coordinate of the ending point of the line. + \param z1 = Z-coordinate of the ending point of the line. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param pattern = An integer whose bits describes the line pattern. + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + CImg& draw_line(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, + const T *const color, const unsigned int pattern=~0L, const float opacity=1) { + if (!is_empty()) { + if (!color) throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null)",pixel_type()); + const T* col=color; + unsigned int hatch=1; + int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1; + if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); + if (nx1<0 || nx0>=dimx()) return *this; + if (nx0<0) { const int D=1+nx1-nx0; ny0-=nx0*(1+ny1-ny0)/D; nz0-=nx0*(1+nz1-nz0)/D; nx0=0; } + if (nx1>=dimx()) { const int d=nx1-dimx(), D=1+nx1-nx0; ny1+=d*(1+ny0-ny1)/D; nz1+=d*(1+nz0-nz1)/D; nx1=dimx()-1;} + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); + if (ny1<0 || ny0>=dimy()) return *this; + if (ny0<0) { const int D=1+ny1-ny0; nx0-=ny0*(1+nx1-nx0)/D; nz0-=ny0*(1+nz1-nz0)/D; ny0=0; } + if (ny1>=dimy()) { const int d=ny1-dimy(), D=1+ny1-ny0; nx1+=d*(1+nx0-nx1)/D; nz1+=d*(1+nz0-nz1)/D; ny1=dimy()-1;} + if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); + if (nz1<0 || nz0>=dimz()) return *this; + if (nz0<0) { const int D=1+nz1-nz0; nx0-=nz0*(1+nx1-nx0)/D; ny0-=nz0*(1+ny1-ny0)/D; nz0=0; } + if (nz1>=dimz()) { const int d=nz1-dimz(), D=1+nz1-nz0; nx1+=d*(1+nx0-nx1)/D; ny1+=d*(1+ny0-ny1)/D; nz1=dimz()-1;} + const unsigned int dmax = cimg::max(cimg::abs(nx1-nx0),cimg::abs(ny1-ny0),nz1-nz0), whz = width*height*depth; + const float px = (nx1-nx0)/(float)dmax, py = (ny1-ny0)/(float)dmax, pz = (nz1-nz0)/(float)dmax; + float x = (float)nx0, y = (float)ny0, z = (float)nz0; + if (opacity>=1) for (unsigned int t=0; t<=dmax; t++) { + if (!(~pattern) || (~pattern && pattern&hatch)) { + T* ptrd = ptr((unsigned int)x,(unsigned int)y,(unsigned int)z,0); + cimg_forV(*this,k) { *ptrd=*(col++); ptrd+=whz; } + col-=dim; + } + x+=px; y+=py; z+=pz; if (pattern) hatch=(hatch<<1)+(hatch>>(sizeof(unsigned int)*8-1)); + } else { + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + for (unsigned int t=0; t<=dmax; t++) { + if (!(~pattern) || (~pattern && pattern&hatch)) { + T* ptrd = ptr((unsigned int)x,(unsigned int)y,(unsigned int)z,0); + cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + copacity*(*ptrd)); ptrd+=whz; } + col-=dim; + } + x+=px; y+=py; z+=pz; if (pattern) hatch=(hatch<<1)+(hatch>>(sizeof(unsigned int)*8-1)); + } + } + } + return *this; + } + + //! Draw a 2D textured line in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1). + /** + \param x0 = X-coordinate of the starting point of the line. + \param y0 = Y-coordinate of the starting point of the line. + \param x1 = X-coordinate of the ending point of the line. + \param y1 = Y-coordinate of the ending point of the line. + \param texture = a colored texture image used to draw the line color. + \param tx0 = X-coordinate of the starting point of the texture. + \param ty0 = Y-coordinate of the starting point of the texture. + \param tx1 = X-coordinate of the ending point of the texture. + \param ty1 = Y-coordinate of the ending point of the texture. + \param opacity = opacity of the drawing. + \note Clipping is supported, but texture coordinates do not support clipping. + **/ + template CImg& draw_line(const int x0,const int y0,const int x1,const int y1, + const CImg& texture, + const int tx0,const int ty0,const int tx1,const int ty1, + const float opacity=1) { + if (!is_empty()) { + if (texture.is_empty() || texture.dim::draw_line() : specified texture (%u,%u,%u,%u,%p) has wrong dimensions.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + int nx0=x0, ny0=y0, nx1=x1, ny1=y1, ntx0=tx0, nty0=ty0, ntx1=tx1, nty1=ty1; + if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1); + if (nx1<0 || nx0>=dimx()) return *this; + if (nx0<0) { const int D=nx1-nx0; ny0-=nx0*(ny1-ny0)/D; ntx0-=nx0*(ntx1-ntx0)/D; nty0-=nx0*(nty1-nty0)/D; nx0=0; } + if (nx1>=dimx()) { const int d=nx1-dimx(),D=nx1-nx0; ny1+=d*(ny0-ny1)/D; ntx1+=d*(ntx0-ntx1)/D; nty1+=d*(nty0-nty1)/D; nx1=dimx()-1; } + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1); + if (ny1<0 || ny0>=dimy()) return *this; + if (ny0<0) { const int D=ny1-ny0; nx0-=ny0*(nx1-nx0)/D; ntx0-=ny0*(ntx1-ntx0)/D; nty0-=ny0*(nty1-nty0)/D; ny0=0; } + if (ny1>=dimy()) { const int d=ny1-dimy(),D=ny1-ny0; nx1+=d*(nx0-nx1)/D; ntx1+=d*(ntx0-ntx1)/D; nty1+=d*(nty0-nty1)/D; ny1=dimy()-1; } + const unsigned int dmax = (unsigned int)cimg::max(cimg::abs(nx1-nx0),ny1-ny0), + whz = width*height*depth, twhz = texture.width*texture.height*texture.depth; + const float px = dmax?(nx1-nx0)/(float)dmax:0, py = dmax?(ny1-ny0)/(float)dmax:0, + tpx = dmax?(ntx1-ntx0)/(float)dmax:0, tpy = dmax?(nty1-nty0)/(float)dmax:0; + float x = (float)nx0, y = (float)ny0, tx = (float)ntx0, ty = (float)nty0; + if (opacity>=1) for (unsigned int tt=0; tt<=dmax; tt++) { + T *ptrd = ptr((unsigned int)x,(unsigned int)y,0,0); + const t *ptrs = texture.ptr((unsigned int)tx,(unsigned int)ty,0,0); + cimg_forV(*this,k) { *ptrd = (T)*ptrs; ptrd+=whz; ptrs+=twhz; } + x+=px; y+=py; tx+=tpx; ty+=tpy; + } else { + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + for (unsigned int tt=0; tt<=dmax; tt++) { + T *ptrd = ptr((unsigned int)x,(unsigned int)y,0,0); + const t *ptrs = texture.ptr((unsigned int)tx,(unsigned int)ty,0,0); + cimg_forV(*this,k) { *ptrd = (T)(nopacity*(*ptrs) + copacity*(*ptrd)); ptrd+=whz; ptrs+=twhz; } + x+=px; y+=py; tx+=tpx; ty+=tpy; + } + } + } + return *this; + } + + //! Draw a 2D colored arrow in the instance image, at coordinates (\c x0,\c y0)->(\c x1,\c y1). + /** + \param x0 = X-coordinate of the starting point of the arrow (tail). + \param y0 = Y-coordinate of the starting point of the arrow (tail). + \param x1 = X-coordinate of the ending point of the arrow (head). + \param y1 = Y-coordinate of the ending point of the arrow (head). + \param color = array of dimv() values of type \c T, defining the drawing color. + \param angle = aperture angle of the arrow head + \param length = length of the arrow head. If <0, described as a percentage of the arrow length. + \param pattern = An integer whose bits describes the line pattern. + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + CImg& draw_arrow(const int x0,const int y0,const int x1,const int y1, + const T *const color, + const float angle=30,const float length=-10,const unsigned int pattern=~0L,const float opacity=1) { + if (!is_empty()) { + const float u = (float)(x0-x1), v = (float)(y0-y1), sq = u*u+v*v, + deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f, + l = (length>=0)?length:-length*(float)std::sqrt(sq)/100; + if (sq>0) { + const double cl = std::cos(ang-deg), sl = std::sin(ang-deg), cr = std::cos(ang+deg), sr = std::sin(ang+deg); + const int + xl = x1+(int)(l*cl), yl = y1+(int)(l*sl), + xr = x1+(int)(l*cr), yr = y1+(int)(l*sr), + xc = x1+(int)((l+1)*(cl+cr))/2, yc = y1+(int)((l+1)*(sl+sr))/2; + draw_line(x0,y0,xc,yc,color,pattern,opacity).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity); + } else draw_point(x0,y0,color,opacity); + } + return *this; + } + + //! Draw a sprite image in the instance image, at coordinates (\c x0,\c y0,\c z0,\c v0). + /** + \param sprite = sprite image. + \param x0 = X-coordinate of the sprite position in the instance image. + \param y0 = Y-coordinate of the sprite position in the instance image. + \param z0 = Z-coordinate of the sprite position in the instance image. + \param v0 = V-coordinate of the sprite position in the instance image. + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + template CImg& draw_image(const CImg& sprite, + const int x0=0,const int y0=0,const int z0=0,const int v0=0,const float opacity=1) { + if (!is_empty()) { + if (sprite.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); + const bool bx=(x0<0), by=(y0<0), bz=(z0<0), bv=(v0<0); + const int + lX = sprite.dimx() - (x0+sprite.dimx()>dimx()?x0+sprite.dimx()-dimx():0) + (bx?x0:0), + lY = sprite.dimy() - (y0+sprite.dimy()>dimy()?y0+sprite.dimy()-dimy():0) + (by?y0:0), + lZ = sprite.dimz() - (z0+sprite.dimz()>dimz()?z0+sprite.dimz()-dimz():0) + (bz?z0:0), + lV = sprite.dimv() - (v0+sprite.dimv()>dimv()?v0+sprite.dimv()-dimv():0) + (bv?v0:0); + const t *ptrs = sprite.ptr()-(bx?x0:0)-(by?y0*sprite.dimx():0)+(bz?z0*sprite.dimx()*sprite.dimy():0)+ + (bv?v0*sprite.dimx()*sprite.dimy()*sprite.dimz():0); + const unsigned int + offX = width-lX, soffX = sprite.width-lX, + offY = width*(height-lY), soffY = sprite.width*(sprite.height-lY), + offZ = width*height*(depth-lZ), soffZ = sprite.width*sprite.height*(sprite.depth-lZ); + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); + if (lX>0 && lY>0 && lZ>0 && lV>0) + for (int v=0; v=1) for (int x=0; x::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); + if (this==&sprite) return draw_image(CImg(sprite),x0,y0,z0,v0,opacity); + const bool bx=(x0<0), by=(y0<0), bz=(z0<0), bv=(v0<0); + const int + lX = sprite.dimx() - (x0+sprite.dimx()>dimx()?x0+sprite.dimx()-dimx():0) + (bx?x0:0), + lY = sprite.dimy() - (y0+sprite.dimy()>dimy()?y0+sprite.dimy()-dimy():0) + (by?y0:0), + lZ = sprite.dimz() - (z0+sprite.dimz()>dimz()?z0+sprite.dimz()-dimz():0) + (bz?z0:0), + lV = sprite.dimv() - (v0+sprite.dimv()>dimv()?v0+sprite.dimv()-dimv():0) + (bv?v0:0); + const T *ptrs = sprite.ptr()-(bx?x0:0)-(by?y0*sprite.dimx():0)+(bz?z0*sprite.dimx()*sprite.dimy():0)+ + (bv?v0*sprite.dimx()*sprite.dimy()*sprite.dimz():0); + const unsigned int + offX = width-lX, soffX = sprite.width-lX, + offY = width*(height-lY), soffY = sprite.width*(sprite.height-lY), + offZ = width*height*(depth-lZ), soffZ = sprite.width*sprite.height*(sprite.depth-lZ), + slX = lX*sizeof(T); + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); + if (lX>0 && lY>0 && lZ>0 && lV>0) + for (int v=0; v=1) for (int y=0; y CImg& draw_image(const CImg& sprite, const CImg& mask, + const int x0=0, const int y0=0, const int z0=0, const int v0=0, + const tm mask_valmax='\1', const float opacity=1) { + if (!is_empty()) { + if (sprite.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); + if (mask.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_image() : Specified mask image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + if ((void*)this==(void*)&sprite) return draw_image(CImg(sprite),mask,x0,y0,z0,v0); + if(mask.width!=sprite.width || mask.height!=sprite.height || mask.depth!=sprite.depth) + throw CImgArgumentException("CImg<%s>::draw_image() : Mask dimension is (%u,%u,%u,%u), while sprite is (%u,%u,%u,%u)", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,sprite.width,sprite.height,sprite.depth,sprite.dim); + const bool bx=(x0<0), by=(y0<0), bz=(z0<0), bv=(v0<0); + const int + lX = sprite.dimx() - (x0+sprite.dimx()>dimx()?x0+sprite.dimx()-dimx():0) + (bx?x0:0), + lY = sprite.dimy() - (y0+sprite.dimy()>dimy()?y0+sprite.dimy()-dimy():0) + (by?y0:0), + lZ = sprite.dimz() - (z0+sprite.dimz()>dimz()?z0+sprite.dimz()-dimz():0) + (bz?z0:0), + lV = sprite.dimv() - (v0+sprite.dimv()>dimv()?v0+sprite.dimv()-dimv():0) + (bv?v0:0); + const int coff = -(bx?x0:0)-(by?y0*mask.dimx():0)-(bz?z0*mask.dimx()*mask.dimy():0)- + (bv?v0*mask.dimx()*mask.dimy()*mask.dimz():0), + ssize = mask.dimx()*mask.dimy()*mask.dimz(); + const ti *ptrs = sprite.ptr() + coff; + const tm *ptrm = mask.ptr() + coff; + const unsigned int + offX = width-lX, soffX = sprite.width-lX, + offY = width*(height-lY), soffY = sprite.width*(sprite.height-lY), + offZ = width*height*(depth-lZ), soffZ = sprite.width*sprite.height*(sprite.depth-lZ); + T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); + if (lX>0 && lY>0 && lZ>0 && lV>0) + for (int v=0; v=dimx()?dimx()-1-nx1:0) + (nx0<0?nx0:0), + lY = (1+ny1-ny0) + (ny1>=dimy()?dimy()-1-ny1:0) + (ny0<0?ny0:0), + lZ = (1+nz1-nz0) + (nz1>=dimz()?dimz()-1-nz1:0) + (nz0<0?nz0:0), + lV = (1+nv1-nv0) + (nv1>=dimv()?dimv()-1-nv1:0) + (nv0<0?nv0:0); + const unsigned int offX = width-lX, offY = width*(height-lY), offZ = width*height*(depth-lZ); + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + T *ptrd = ptr(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nv0<0?0:nv0); + if (lX>0 && lY>0 && lZ>0 && lV>0) + for (int v=0; v=1) { + if (sizeof(T)!=1) { for (int x=0; x::draw_rectangle : specified color is (null)",pixel_type()); + cimg_forV(*this,k) draw_rectangle(x0,y0,z0,k,x1,y1,z1,k,color[k],opacity); + return *this; + } + + //! Draw a 2D filled colored rectangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1). + /** + \param x0 = X-coordinate of the upper-left rectangle corner in the instance image. + \param y0 = Y-coordinate of the upper-left rectangle corner in the instance image. + \param x1 = X-coordinate of the lower-right rectangle corner in the instance image. + \param y1 = Y-coordinate of the lower-right rectangle corner in the instance image. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + CImg& draw_rectangle(const int x0,const int y0,const int x1,const int y1, + const T *const color,const float opacity=1) { + draw_rectangle(x0,y0,0,x1,y1,depth-1,color,opacity); + return *this; + } + + //! Draw a 2D filled colored triangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing (<1) + \param brightness = brightness of the drawing (in [0,1]) + \note Clipping is supported. + **/ + CImg& draw_triangle(const int x0,const int y0, + const int x1,const int y1, + const int x2,const int y2, + const T *const color, + const float opacity=1, + const float brightness=1) { + draw_scanline(color,opacity); + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2); + if (ny0>=dimy() || ny2<0) return *this; + const float + p1 = (ny1-ny0)?(nx1-nx0)/(float)(ny1-ny0):(nx1-nx0), + p2 = (ny2-ny0)?(nx2-nx0)/(float)(ny2-ny0):(nx2-nx0), + p3 = (ny2-ny1)?(nx2-nx1)/(float)(ny2-ny1):(nx2-nx1); + float xleft = (float)nx0, xright = xleft, pleft = (p1dimy()?height:ny1; + for (int y=ny0<0?0:ny0; y=dimy()?height-1:ny2; + for (int yy=ny1<0?0:ny1; yy<=yb; yy++) { + draw_scanline((int)xleft,(int)xright,yy,color,opacity,brightness); + xleft+=pleft; xright+=pright; + } + return *this; + } + + //! Draw a 2D Gouraud-filled triangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param color = array of dimv() values of type \c T, defining the global drawing color. + \param c0 = brightness of the first corner. + \param c1 = brightness of the second corner. + \param c2 = brightness of the third corner. + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const T *const color, + const float c0, const float c1, const float c2, + const float opacity=1) { + if (!is_empty()) { + int nx0=x0,ny0=y0,nx1=x1,ny1=y1,nx2=x2,ny2=y2,whz=width*height*depth; + float nc0=c0,nc1=c1,nc2=c2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2); + if (ny0>=dimy() || ny2<0) return *this; + const float + p1 = (ny1-ny0)?(nx1-nx0)/(float)(ny1-ny0):(nx1-nx0), + p2 = (ny2-ny0)?(nx2-nx0)/(float)(ny2-ny0):(nx2-nx0), + p3 = (ny2-ny1)?(nx2-nx1)/(float)(ny2-ny1):(nx2-nx1), + cp1 = (ny1-ny0)?(nc1-nc0)/(float)(ny1-ny0):0, + cp2 = (ny2-ny0)?(nc2-nc0)/(float)(ny2-ny0):0, + cp3 = (ny2-ny1)?(nc2-nc1)/(float)(ny2-ny1):0; + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + float pleft,pright,cpleft,cpright,xleft=(float)nx0,xright=xleft,cleft=nc0,cright=cleft; + if (p1=0)?cleft:(cleft-xleft*cp); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + const T col = color[k]; + float c=ci; + for (int x=xmin; x<=xmax; x++) { *(ptrd++)=(T)(c*col); c+=cp; } + ptrd+=offx; + } else cimg_forV(*this,k) { + const T col = color[k]; + float c=ci; + for (int x=xmin; x<=xmax; x++) { *ptrd=(T)(nopacity*c*col+copacity*(*ptrd)); ptrd++; c+=cp; } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; cleft+=cpleft; cright+=cpright; + } + + if (p1=dimy()?(height-1):ny2; + for (int yy=(ny1<0?0:ny1); yy<=yb; yy++) { + const int dx = (int)xright-(int)xleft; + const float + cp = dx?(cright-cleft)/dx:0, + ci = (xleft>=0)?cleft:(cleft-xleft*cp); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + const T col = color[k]; + float c=ci; + for (int x=xmin; x<=xmax; x++) { *(ptrd++)=(T)(c*col); c+=cp; } + ptrd+=offx; + } else cimg_forV(*this,k) { + const T col = color[k]; + float c=ci; + for (int x=xmin; x<=xmax; x++) { *ptrd=(T)(nopacity*c*col+copacity*(*ptrd)); ptrd++; c+=cp; } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; cleft+=cpleft; cright+=cpright; + } + } + return *this; + } + + //! Draw a 2D phong-shaded triangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param color = array of dimv() values of type \c T, defining the global drawing color. + \param light = light image. + \param lx0 = X-coordinate of the first corner in the light image. + \param ly0 = Y-coordinate of the first corner in the light image. + \param lx1 = X-coordinate of the second corner in the light image. + \param ly1 = Y-coordinate of the second corner in the light image. + \param lx2 = X-coordinate of the third corner in the light image. + \param ly2 = Y-coordinate of the third corner in the light image. + \param opacity = opacity of the drawing. + \note Clipping is supported, but texture coordinates do not support clipping. + **/ + template CImg& draw_triangle(const int x0,const int y0, + const int x1,const int y1, + const int x2,const int y2, + const T *const color, + const CImg& light, + const int lx0,const int ly0, + const int lx1,const int ly1, + const int lx2,const int ly2, + const float opacity=1.0f) { + if (!is_empty()) { + if (light.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", + pixel_type(),light.width,light.height,light.depth,light.dim,light.data); + int nx0=x0,ny0=y0,nx1=x1,ny1=y1,nx2=x2,ny2=y2,nlx0=lx0,nly0=ly0,nlx1=lx1,nly1=ly1,nlx2=lx2,nly2=ly2,whz=width*height*depth; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2); + if (ny0>=dimy() || ny2<0) return *this; + const float + p1 = (ny1-ny0)?(nx1-nx0)/(float)(ny1-ny0):(nx1-nx0), + p2 = (ny2-ny0)?(nx2-nx0)/(float)(ny2-ny0):(nx2-nx0), + p3 = (ny2-ny1)?(nx2-nx1)/(float)(ny2-ny1):(nx2-nx1), + lpx1 = (ny1-ny0)?(nlx1-nlx0)/(float)(ny1-ny0):0, + lpy1 = (ny1-ny0)?(nly1-nly0)/(float)(ny1-ny0):0, + lpx2 = (ny2-ny0)?(nlx2-nlx0)/(float)(ny2-ny0):0, + lpy2 = (ny2-ny0)?(nly2-nly0)/(float)(ny2-ny0):0, + lpx3 = (ny2-ny1)?(nlx2-nlx1)/(float)(ny2-ny1):0, + lpy3 = (ny2-ny1)?(nly2-nly1)/(float)(ny2-ny1):0; + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + float pleft,pright,lpxleft,lpyleft,lpxright,lpyright, + xleft=(float)nx0,xright=xleft,lxleft=(float)nlx0,lyleft=(float)nly0,lxright=lxleft,lyright=lyleft; + if (p1=0)?(int)lxleft:(int)(lxleft-(int)xleft*lpx)), + lyi = (float)((xleft>=0)?(int)lyleft:(int)(lyleft-(int)xleft*lpy)); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + float lx=lxi, ly=lyi; + for (int x=xmin; x<=xmax; x++) { *(ptrd++)=(T)(light((unsigned int)lx,(unsigned int)ly)*color[k]); lx+=lpx; ly+=lpy; } + ptrd+=offx; + } else cimg_forV(*this,k) { + float lx=lxi, ly=lyi; + for (int x=xmin; x<=xmax; x++) { *ptrd=(T)(nopacity*light((unsigned int)lx,(unsigned int)ly)*color[k]+copacity*(*ptrd)); ptrd++; lx+=lpx; ly+=lpy; } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; lxleft+=lpxleft; lyleft+=lpyleft; lxright+=lpxright; lyright+=lpyright; + } + + if (p1=dimy()?(height-1):ny2; + for (int yy=(ny1<0?0:ny1); yy<=yb; yy++) { + const int dx = (int)xright-(int)xleft; + const float + lpx = dx?((int)lxright-(int)lxleft)/(float)dx:0, + lpy = dx?((int)lyright-(int)lyleft)/(float)dx:0, + lxi = (float)((xleft>=0)?(int)lxleft:(int)(lxleft-(int)xleft*lpx)), + lyi = (float)((xleft>=0)?(int)lyleft:(int)(lyleft-(int)xleft*lpy)); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + float lx=lxi, ly=lyi; + for (int x=xmin; x<=xmax; x++) { *(ptrd++)=(T)(light((unsigned int)lx,(unsigned int)ly)*color[k]); lx+=lpx; ly+=lpy; } + ptrd+=offx; + } else cimg_forV(*this,k) { + float lx=lxi, ly=lyi; + for (int x=xmin; x<=xmax; x++) { *ptrd=(T)(nopacity*light((unsigned int)lx,(unsigned int)ly)*color[k]+copacity*(*ptrd)); ptrd++; lx+=lpx; ly+=lpy; } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; lxleft+=lpxleft; lyleft+=lpyleft; lxright+=lpxright; lyright+=lpyright; + } + } + return *this; + } + + //! Draw a 2D textured triangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param texture = texture image used to fill the triangle. + \param tx0 = X-coordinate of the first corner in the texture image. + \param ty0 = Y-coordinate of the first corner in the texture image. + \param tx1 = X-coordinate of the second corner in the texture image. + \param ty1 = Y-coordinate of the second corner in the texture image. + \param tx2 = X-coordinate of the third corner in the texture image. + \param ty2 = Y-coordinate of the third corner in the texture image. + \param opacity = opacity of the drawing. + \param brightness = brightness of the drawing. + \note Clipping is supported, but texture coordinates do not support clipping. + **/ + template CImg& draw_triangle(const int x0,const int y0, + const int x1,const int y1, + const int x2,const int y2, + const CImg& texture, + const int tx0,const int ty0, + const int tx1,const int ty1, + const int tx2,const int ty2, + const float opacity=1.0f, const float brightness=1.0f) { + if (!is_empty()) { + if (texture.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is empty.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + int nx0=x0,ny0=y0,nx1=x1,ny1=y1,nx2=x2,ny2=y2,ntx0=tx0,nty0=ty0,ntx1=tx1,nty1=ty1,ntx2=tx2,nty2=ty2,whz=width*height*depth; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2); + if (ny0>=dimy() || ny2<0) return *this; + const float + p1 = (ny1-ny0)?(nx1-nx0)/(float)(ny1-ny0):(nx1-nx0), + p2 = (ny2-ny0)?(nx2-nx0)/(float)(ny2-ny0):(nx2-nx0), + p3 = (ny2-ny1)?(nx2-nx1)/(float)(ny2-ny1):(nx2-nx1), + tpx1 = (ny1-ny0)?(ntx1-ntx0)/(float)(ny1-ny0):0, + tpy1 = (ny1-ny0)?(nty1-nty0)/(float)(ny1-ny0):0, + tpx2 = (ny2-ny0)?(ntx2-ntx0)/(float)(ny2-ny0):0, + tpy2 = (ny2-ny0)?(nty2-nty0)/(float)(ny2-ny0):0, + tpx3 = (ny2-ny1)?(ntx2-ntx1)/(float)(ny2-ny1):0, + tpy3 = (ny2-ny1)?(nty2-nty1)/(float)(ny2-ny1):0; + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + float pleft,pright,tpxleft,tpyleft,tpxright,tpyright, + xleft=(float)nx0,xright=xleft,txleft=(float)ntx0,tyleft=(float)nty0,txright=txleft,tyright=tyleft; + if (p1=0)?(int)txleft:(int)(txleft-(int)xleft*tpx)), + tyi = (float)((xleft>=0)?(int)tyleft:(int)(tyleft-(int)xleft*tpy)); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + float tx=txi, ty=tyi; + for (int x=xmin; x<=xmax; x++) { *(ptrd++)=(T)(brightness*texture((unsigned int)tx,(unsigned int)ty,0,k)); tx+=tpx; ty+=tpy; } + ptrd+=offx; + } else cimg_forV(*this,k) { + float tx=txi, ty=tyi; + for (int x=xmin; x<=xmax; x++) { *ptrd=(T)(nopacity*brightness*texture((unsigned int)tx,(unsigned int)ty,0,k)+copacity*(*ptrd)); ptrd++; tx+=tpx; ty+=tpy; } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; txleft+=tpxleft; tyleft+=tpyleft; txright+=tpxright; tyright+=tpyright; + } + + if (p1=dimy()?(height-1):ny2; + for (int yy=(ny1<0?0:ny1); yy<=yb; yy++) { + const int dx = (int)xright-(int)xleft; + const float + tpx = dx?((int)txright-(int)txleft)/(float)dx:0, + tpy = dx?((int)tyright-(int)tyleft)/(float)dx:0, + txi = (float)((xleft>=0)?(int)txleft:(int)(txleft-(int)xleft*tpx)), + tyi = (float)((xleft>=0)?(int)tyleft:(int)(tyleft-(int)xleft*tpy)); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + float tx=txi, ty=tyi; + for (int x=xmin; x<=xmax; x++) { *(ptrd++)=(T)(brightness*texture((unsigned int)tx,(unsigned int)ty,0,k)); tx+=tpx; ty+=tpy; } + ptrd+=offx; + } else cimg_forV(*this,k) { + float tx=txi, ty=tyi; + for (int x=xmin; x<=xmax; x++) { *ptrd=(T)(nopacity*brightness*texture((unsigned int)tx,(unsigned int)ty,0,k)+copacity*(*ptrd)); ptrd++; tx+=tpx; ty+=tpy; } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; txleft+=tpxleft; tyleft+=tpyleft; txright+=tpxright; tyright+=tpyright; + } + } + return *this; + } + + //! Draw a 2D textured triangle with Gouraud-Shading in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param texture = texture image used to fill the triangle. + \param tx0 = X-coordinate of the first corner in the texture image. + \param ty0 = Y-coordinate of the first corner in the texture image. + \param tx1 = X-coordinate of the second corner in the texture image. + \param ty1 = Y-coordinate of the second corner in the texture image. + \param tx2 = X-coordinate of the third corner in the texture image. + \param ty2 = Y-coordinate of the third corner in the texture image. + \param c0 = brightness value of the first corner. + \param c1 = brightness value of the second corner. + \param c2 = brightness value of the third corner. + \param opacity = opacity of the drawing. + \note Clipping is supported, but texture coordinates do not support clipping. + **/ + template CImg& draw_triangle(const int x0,const int y0, + const int x1,const int y1, + const int x2,const int y2, + const CImg& texture, + const int tx0,const int ty0, + const int tx1,const int ty1, + const int tx2,const int ty2, + const float c0,const float c1,const float c2, + const float opacity=1) { + if (!is_empty()) { + if (texture.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is empty.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + int nx0=x0,ny0=y0,nx1=x1,ny1=y1,nx2=x2,ny2=y2,ntx0=tx0,nty0=ty0,ntx1=tx1,nty1=ty1,ntx2=tx2,nty2=ty2,whz=width*height*depth; + float nc0=c0,nc1=c1,nc2=c2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2); + if (ny0>=dimy() || ny2<0) return *this; + const float + p1 = (ny1-ny0)?(nx1-nx0)/(float)(ny1-ny0):(nx1-nx0), + p2 = (ny2-ny0)?(nx2-nx0)/(float)(ny2-ny0):(nx2-nx0), + p3 = (ny2-ny1)?(nx2-nx1)/(float)(ny2-ny1):(nx2-nx1), + tpx1 = (ny1-ny0)?(ntx1-ntx0)/(float)(ny1-ny0):0, + tpy1 = (ny1-ny0)?(nty1-nty0)/(float)(ny1-ny0):0, + tpx2 = (ny2-ny0)?(ntx2-ntx0)/(float)(ny2-ny0):0, + tpy2 = (ny2-ny0)?(nty2-nty0)/(float)(ny2-ny0):0, + tpx3 = (ny2-ny1)?(ntx2-ntx1)/(float)(ny2-ny1):0, + tpy3 = (ny2-ny1)?(nty2-nty1)/(float)(ny2-ny1):0, + cp1 = (ny1-ny0)?(nc1-nc0)/(float)(ny1-ny0):0, + cp2 = (ny2-ny0)?(nc2-nc0)/(float)(ny2-ny0):0, + cp3 = (ny2-ny1)?(nc2-nc1)/(float)(ny2-ny1):0; + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + float pleft,pright,tpxleft,tpyleft,tpxright,tpyright,cpleft,cpright, + xleft=(float)nx0,xright=xleft,txleft=(float)ntx0,tyleft=(float)nty0,txright=txleft,tyright=tyleft,cleft=nc0,cright=cleft; + if (p1=0)?(int)txleft:(int)(txleft-(int)xleft*tpx)), + tyi = (float)((xleft>=0)?(int)tyleft:(int)(tyleft-(int)xleft*tpy)), + ci = (xleft>=0)?cleft:(cleft-xleft*cp); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + float tx=txi, ty=tyi, c=ci; + for (int x=xmin; x<=xmax; x++) { *(ptrd++)=(T)(c*texture((unsigned int)tx,(unsigned int)ty,0,k)); tx+=tpx; ty+=tpy; c+=cp; } + ptrd+=offx; + } else cimg_forV(*this,k) { + float tx=txi, ty=tyi, c=ci; + for (int x=xmin; x<=xmax; x++) { *ptrd=(T)(nopacity*c*texture((unsigned int)tx,(unsigned int)ty,0,k)+copacity*(*ptrd)); ptrd++; tx+=tpx; ty+=tpy; c+=cp; } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; txleft+=tpxleft; tyleft+=tpyleft; txright+=tpxright; tyright+=tpyright; cleft+=cpleft; cright+=cpright; + } + + if (p1=dimy()?(height-1):ny2; + for (int yy=(ny1<0?0:ny1); yy<=yb; yy++) { + const int dx = (int)xright-(int)xleft; + const float + tpx = dx?((int)txright-(int)txleft)/(float)dx:0, + tpy = dx?((int)tyright-(int)tyleft)/(float)dx:0, + cp = dx?(cright-cleft)/dx:0, + txi = (float)((xleft>=0)?(int)txleft:(int)(txleft-(int)xleft*tpx)), + tyi = (float)((xleft>=0)?(int)tyleft:(int)(tyleft-(int)xleft*tpy)), + ci = (xleft>=0)?cleft:(cleft-xleft*cp); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + float tx=txi, ty=tyi, c=ci; + for (int x=xmin; x<=xmax; x++) { *(ptrd++)=(T)(c*texture((unsigned int)tx,(unsigned int)ty,0,k)); tx+=tpx; ty+=tpy; c+=cp; } + ptrd+=offx; + } else cimg_forV(*this,k) { + float tx=txi, ty=tyi, c=ci; + for (int x=xmin; x<=xmax; x++) { *ptrd=(T)(nopacity*c*texture((unsigned int)tx,(unsigned int)ty,0,k)+copacity*(*ptrd)); ptrd++; tx+=tpx; ty+=tpy; c+=ci; } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; txleft+=tpxleft; tyleft+=tpyleft; txright+=tpxright; tyright+=tpyright; cleft+=cpleft; cright+=cpright; + } + } + return *this; + } + + //! Draw a phong-shaded 2D textured triangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param texture = texture image used to fill the triangle. + \param tx0 = X-coordinate of the first corner in the texture image. + \param ty0 = Y-coordinate of the first corner in the texture image. + \param tx1 = X-coordinate of the second corner in the texture image. + \param ty1 = Y-coordinate of the second corner in the texture image. + \param tx2 = X-coordinate of the third corner in the texture image. + \param ty2 = Y-coordinate of the third corner in the texture image. + \param light = light image. + \param lx0 = X-coordinate of the first corner in the light image. + \param ly0 = Y-coordinate of the first corner in the light image. + \param lx1 = X-coordinate of the second corner in the light image. + \param ly1 = Y-coordinate of the second corner in the light image. + \param lx2 = X-coordinate of the third corner in the light image. + \param ly2 = Y-coordinate of the third corner in the light image. + \param opacity = opacity of the drawing. + \note Clipping is supported, but texture coordinates do not support clipping. + **/ + template CImg& draw_triangle(const int x0,const int y0, + const int x1,const int y1, + const int x2,const int y2, + const CImg& texture, + const int tx0,const int ty0, + const int tx1,const int ty1, + const int tx2,const int ty2, + const CImg& light, + const int lx0,const int ly0, + const int lx1,const int ly1, + const int lx2,const int ly2, + const float opacity=1.0f) { + if (!is_empty()) { + if (texture.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is empty.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (light.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light (%u,%u,%u,%u,%p) is empty.", + pixel_type(),light.width,light.height,light.depth,light.dim,light.data); + int + nx0=x0,ny0=y0,nx1=x1,ny1=y1,nx2=x2,ny2=y2, + ntx0=tx0,nty0=ty0,ntx1=tx1,nty1=ty1,ntx2=tx2,nty2=ty2, + nlx0=lx0,nly0=ly0,nlx1=lx1,nly1=ly1,nlx2=lx2,nly2=ly2, + whz=width*height*depth; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2); + if (ny0>=dimy() || ny2<0) return *this; + const float + p1 = (ny1-ny0)?(nx1-nx0)/(float)(ny1-ny0):(nx1-nx0), + p2 = (ny2-ny0)?(nx2-nx0)/(float)(ny2-ny0):(nx2-nx0), + p3 = (ny2-ny1)?(nx2-nx1)/(float)(ny2-ny1):(nx2-nx1), + tpx1 = (ny1-ny0)?(ntx1-ntx0)/(float)(ny1-ny0):0, + tpy1 = (ny1-ny0)?(nty1-nty0)/(float)(ny1-ny0):0, + tpx2 = (ny2-ny0)?(ntx2-ntx0)/(float)(ny2-ny0):0, + tpy2 = (ny2-ny0)?(nty2-nty0)/(float)(ny2-ny0):0, + tpx3 = (ny2-ny1)?(ntx2-ntx1)/(float)(ny2-ny1):0, + tpy3 = (ny2-ny1)?(nty2-nty1)/(float)(ny2-ny1):0, + lpx1 = (ny1-ny0)?(nlx1-nlx0)/(float)(ny1-ny0):0, + lpy1 = (ny1-ny0)?(nly1-nly0)/(float)(ny1-ny0):0, + lpx2 = (ny2-ny0)?(nlx2-nlx0)/(float)(ny2-ny0):0, + lpy2 = (ny2-ny0)?(nly2-nly0)/(float)(ny2-ny0):0, + lpx3 = (ny2-ny1)?(nlx2-nlx1)/(float)(ny2-ny1):0, + lpy3 = (ny2-ny1)?(nly2-nly1)/(float)(ny2-ny1):0; + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + float pleft,pright,tpxleft,tpyleft,tpxright,tpyright,lpxleft,lpyleft,lpxright,lpyright, + xleft=(float)nx0,xright=xleft, + txleft=(float)ntx0,tyleft=(float)nty0,txright=txleft,tyright=tyleft, + lxleft=(float)nlx0,lyleft=(float)nly0,lxright=lxleft,lyright=lyleft; + if (p1=0)?(int)txleft:(int)(txleft-(int)xleft*tpx)), + tyi = (float)((xleft>=0)?(int)tyleft:(int)(tyleft-(int)xleft*tpy)), + lpx = dx?((int)lxright-(int)lxleft)/(float)dx:0, + lpy = dx?((int)lyright-(int)lyleft)/(float)dx:0, + lxi = (float)((xleft>=0)?(int)lxleft:(int)(lxleft-(int)xleft*lpx)), + lyi = (float)((xleft>=0)?(int)lyleft:(int)(lyleft-(int)xleft*lpy)); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + float tx=txi, ty=tyi, lx=lxi, ly=lyi; + for (int x=xmin; x<=xmax; x++) { + *(ptrd++)=(T)(light((unsigned int)lx,(unsigned int)ly)*texture((unsigned int)tx,(unsigned int)ty,0,k)); + tx+=tpx; ty+=tpy; lx+=lpx; ly+=lpy; + } + ptrd+=offx; + } else cimg_forV(*this,k) { + float tx=txi, ty=tyi, lx=lxi, ly=lyi; + for (int x=xmin; x<=xmax; x++) { + *ptrd=(T)(nopacity*light((unsigned int)lx,(unsigned int)ly)*texture((unsigned int)tx,(unsigned int)ty,0,k)+copacity*(*ptrd)); ptrd++; + tx+=tpx; ty+=tpy; lx+=lpx; ly+=lpy; + } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; + txleft+=tpxleft; tyleft+=tpyleft; txright+=tpxright; tyright+=tpyright; + lxleft+=lpxleft; lyleft+=lpyleft; lxright+=lpxright; lyright+=lpyright; + } + + if (p1=dimy()?(height-1):ny2; + for (int yy=(ny1<0?0:ny1); yy<=yb; yy++) { + const int dx = (int)xright-(int)xleft; + const float + tpx = dx?((int)txright-(int)txleft)/(float)dx:0, + tpy = dx?((int)tyright-(int)tyleft)/(float)dx:0, + txi = (float)((xleft>=0)?(int)txleft:(int)(txleft-(int)xleft*tpx)), + tyi = (float)((xleft>=0)?(int)tyleft:(int)(tyleft-(int)xleft*tpy)), + lpx = dx?((int)lxright-(int)lxleft)/(float)dx:0, + lpy = dx?((int)lyright-(int)lyleft)/(float)dx:0, + lxi = (float)((xleft>=0)?(int)lxleft:(int)(lxleft-(int)xleft*lpx)), + lyi = (float)((xleft>=0)?(int)lyleft:(int)(lyleft-(int)xleft*lpy)); + const int xmin=(xleft>=0)?(int)xleft:0, xmax=(xright=1) cimg_forV(*this,k) { + float tx=txi, ty=tyi, lx=lxi, ly=lyi; + for (int x=xmin; x<=xmax; x++) { + *(ptrd++)=(T)(light((unsigned int)lx,(unsigned int)ly)*texture((unsigned int)tx,(unsigned int)ty,0,k)); + tx+=tpx; ty+=tpy; lx+=lpx; ly+=lpy; + } + ptrd+=offx; + } else cimg_forV(*this,k) { + float tx=txi, ty=tyi, lx=lxi, ly=lyi; + for (int x=xmin; x<=xmax; x++) { + *ptrd=(T)(nopacity*light((unsigned int)lx,(unsigned int)ly)*texture((unsigned int)tx,(unsigned int)ty,0,k)+copacity*(*ptrd)); ptrd++; + tx+=tpx; ty+=tpy; lx+=lpx; ly+=lpy; + } + ptrd+=offx; + } + } + xleft+=pleft; xright+=pright; + txleft+=tpxleft; tyleft+=tpyleft; txright+=tpxright; tyright+=tpyright; + lxleft+=lpxleft; lyleft+=lpyleft; lxright+=lpxright; lyright+=lpyright; + } + } + return *this; + } + + + //! Draw an ellipse on the instance image + /** + \param x0 = X-coordinate of the ellipse center. + \param y0 = Y-coordinate of the ellipse center. + \param r1 = First radius of the ellipse. + \param r2 = Second radius of the ellipse. + \param ru = X-coordinate of the orientation vector related to the first radius. + \param rv = Y-coordinate of the orientation vector related to the first radius. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern. + \param opacity = opacity of the drawing. + **/ + CImg& draw_ellipse(const int x0,const int y0,const float r1,const float r2,const float ru,const float rv, + const T *const color,const unsigned int pattern=0L, const float opacity=1) { + if (!is_empty()) { + draw_scanline(color,opacity); + if (!color) throw CImgArgumentException("CImg<%s>::draw_ellipse : Specified color is (null).",pixel_type()); + unsigned int hatch=1; + const float + nr1 = cimg::abs(r1), nr2 = cimg::abs(r2), + norm = (float)std::sqrt(ru*ru+rv*rv), + u = norm>0?ru/norm:1, + v = norm>0?rv/norm:0, + rmax = cimg::max(nr1,nr2), + l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2), + l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2), + a = l1*u*u + l2*v*v, + b = u*v*(l1-l2), + c = l1*v*v + l2*u*u; + const int + yb = (int)std::sqrt(a*rmax*rmax/(a*c-b*b)), + ymin = (y0-yb<0)?0:(y0-yb), + ymax = (1+y0+yb>=dimy())?height-1:(1+y0+yb); + int oxmin=0, oxmax=0; + bool first_line = true; + for (int y=ymin; y0?std::sqrt(delta):0)), + fxmin = x0-(b*Y+sdelta)/a, + fxmax = x0-(b*Y-sdelta)/a; + const int xmin = (int)fxmin, xmax = (int)fxmax; + if (!pattern) draw_scanline(xmin,xmax,y,color,opacity); + else { + if (!(~pattern) || (~pattern && pattern&hatch)) { + if (first_line) { draw_scanline(xmin,xmax,y,color,opacity); first_line = false; } + else { + if (xmin>(sizeof(unsigned int)*8-1)); + } + } + return *this; + } + + //! Draw an ellipse on the instance image + /** + \param x0 = X-coordinate of the ellipse center. + \param y0 = Y-coordinate of the ellipse center. + \param tensor = Diffusion tensor describing the ellipse. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern. + \param opacity = opacity of the drawing. + **/ + template CImg& draw_ellipse(const int x0,const int y0,const CImg &tensor, + const T *color,const unsigned int pattern=0L,const float opacity=1) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,val(0),val(1),vec(0,0),vec(0,1),color,pattern,opacity); + } + + //! Draw a circle on the instance image + /** + \param x0 = X-coordinate of the circle center. + \param y0 = Y-coordinate of the circle center. + \param r = radius of the circle. + \param color = an array of dimv() values of type \c T, defining the drawing color. + \param pattern = If zero, the circle is filled, else pattern is an integer whose bits describe the outline pattern. + \param opacity = opacity of the drawing. + **/ + CImg& draw_circle(const int x0,const int y0,float r,const T *const color,const unsigned int pattern=0L,const float opacity=1) { + return draw_ellipse(x0,y0,r,r,1,0,color,pattern,opacity); + } + + //! Draw a text into the instance image. + /** + \param text = a C-string containing the text to display. + \param x0 = X-coordinate of the text in the instance image. + \param y0 = Y-coordinate of the text in the instance image. + \param fgcolor = an array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). + \param bgcolor = an array of dimv() values of type \c T, defining the background color (0 means 'transparent'). + \param font = List of font characters used for the drawing. + \param opacity = opacity of the drawing. + \note Clipping is supported. + \see get_font(). + **/ + template CImg& draw_text(const char *const text, + const int x0,const int y0, + const T *const fgcolor,const T *const bgcolor, + const CImgList& font,const float opacity=1) { + if (!text) + throw CImgArgumentException("CImg<%s>::draw_text() : Specified input string is (null).",pixel_type()); + if (font.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_text() : Specified font (%u,%p) is empty.", + pixel_type(),font.size,font.data); + + if (is_empty()) { + // If needed, pre-compute needed size of the image + int x=0, y=0, w=0; + for (int i=0; iw) w=x; x=0; break; + case '\t': x+=4*font[' '].width; break; + default: if (cw) w=x; + y+=font[' '].height; + } + assign(x0+w,y0+y,1,font[' '].dim,0); + if (bgcolor) cimg_forV(*this,k) get_shared_channel(k).fill(bgcolor[k]); + } + + int x = x0, y = y0; + CImg letter; + for (int i=0; i=512) draw_image(letter,mask,x,y,0,0,(T)1,opacity); + else draw_image(letter,x,y,0,0,opacity); + x+=letter.width; + } + break; + } + } + return *this; + } + + //! Draw a text into the instance image. + /** + \param text = a C-string containing the text to display. + \param x0 = X-coordinate of the text in the instance image. + \param y0 = Y-coordinate of the text in the instance image. + \param fgcolor = an array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). + \param bgcolor = an array of dimv() values of type \c T, defining the background color (0 means 'transparent'). + \param font_size = Height of the desired font (11,13,24,38 or 57) + \param opacity = opacity of the drawing. + \note Clipping is supported. + \see get_font(). + **/ + CImg& draw_text(const char *const text, + const int x0,const int y0, + const T *const fgcolor,const T *const bgcolor=0, + const unsigned int font_size=11,const float opacity=1.0f) { + return draw_text(text,x0,y0,fgcolor,bgcolor,CImgList::get_font(font_size),opacity); + } + + + //! Draw a text into the instance image. + /** + \param x0 = X-coordinate of the text in the instance image. + \param y0 = Y-coordinate of the text in the instance image. + \param fgcolor = an array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). + \param bgcolor = an array of dimv() values of type \c T, defining the background color (0 means 'transparent'). + \param opacity = opacity of the drawing. + \param format = a 'printf'-style format, followed by arguments. + \note Clipping is supported. + **/ + CImg& draw_text(const int x0,const int y0, + const T *const fgcolor,const T *const bgcolor, const unsigned int font_size, + const float opacity,const char *format,...) { + char tmp[2048]={0}; + std::va_list ap; + va_start(ap,format); + std::vsprintf(tmp,format,ap); + va_end(ap); + return draw_text(tmp,x0,y0,fgcolor,bgcolor,font_size,opacity); + } + + template CImg& draw_text(const int x0,const int y0, + const T *const fgcolor,const T *const bgcolor, + const CImgList& font, const float opacity, const char *format,...) { + char tmp[2048]={0}; + std::va_list ap; + va_start(ap,format); + std::vsprintf(tmp,format,ap); + va_end(ap); + return draw_text(tmp,x0,y0,fgcolor,bgcolor,font,opacity); + } + + + //! Draw a vector field in the instance image. + /** + \param flow = a 2d image of 2d vectors used as input data. + \param color = an array of dimv() values of type \c T, defining the drawing color. + \param sampling = length (in pixels) between each arrow. + \param factor = length factor of each arrow (if <0, computed as a percentage of the maximum length). + \param quiver_type = type of plot. Can be 0 (arrows) or 1 (segments). + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, const T *const color, + const unsigned int sampling=25, const float factor=-20, + const int quiver_type=0, const float opacity=1) { + if (!is_empty()) { + if (flow.is_empty() || flow.dim!=2) + throw CImgArgumentException("CImg<%s>::draw_quiver() : Specified flow (%u,%u,%u,%u,%p) has wrong dimensions.", + pixel_type(),flow.width,flow.height,flow.depth,flow.dim,flow.data); + if (!color) + throw CImgArgumentException("CImg<%s>::draw_quiver() : Specified color is (null)", + pixel_type()); + if (sampling<=0) + throw CImgArgumentException("CImg<%s>::draw_quiver() : Incorrect sampling value = %g", + pixel_type(),sampling); + + float vmax,fact; + if (factor<=0) { + const CImgStats st(flow.get_norm_pointwise(2),false); + vmax = (float)cimg::max(cimg::abs(st.min),cimg::abs(st.max)); + fact = -factor; + } else { fact = factor; vmax = 1; } + + for (unsigned int y=sampling/2; y + CImg& draw_quiver(const CImg& flow, const CImg& color, + const unsigned int sampling=25, const float factor=-20, + const int quiver_type=0, const float opacity=1) { + if (!is_empty()) { + if (flow.is_empty() || flow.dim!=2) + throw CImgArgumentException("CImg<%s>::draw_quiver() : Specified flow (%u,%u,%u,%u,%p) has wrong dimensions.", + pixel_type(),flow.width,flow.height,flow.depth,flow.dim,flow.data); + if (color.is_empty() || color.width!=flow.width || color.height!=flow.height) + throw CImgArgumentException("CImg<%s>::draw_quiver() : Specified color (%u,%u,%u,%u,%p) has wrong dimensions.", + pixel_type(),color.width,color.height,color.depth,color.dim,color.data); + if (sampling<=0) + throw CImgArgumentException("CImg<%s>::draw_quiver() : Incorrect sampling value = %g",pixel_type(),sampling); + + float vmax,fact; + if (factor<=0) { + const CImgStats st(flow.get_norm_pointwise(2),false); + vmax = (float)cimg::max(cimg::abs(st.min),cimg::abs(st.max)); + fact = -factor; + } else { fact = factor; vmax = 1; } + + for (unsigned int y=sampling/2; y + CImg& draw_graph(const CImg& data, const T *const color, const unsigned int gtype=0, + const double ymin=0, const double ymax=0, const float opacity=1) { + if (!is_empty()) { + if (!color) throw CImgArgumentException("CImg<%s>::draw_graph() : Specified color is (null)",pixel_type()); + T *color1 = new T[dim], *color2 = new T[dim]; + cimg_forV(*this,k) { color1[k]=(T)(color[k]*0.6f); color2[k]=(T)(color[k]*0.3f); } + CImgStats st; + if (ymin==ymax) { st = CImgStats(data,false); cimg::swap(st.min,st.max); } else { st.min = ymin; st.max = ymax; } + if (st.min==st.max) { st.min--; st.max++; } + const float ca = height>1?(float)(st.max-st.min)/(height-1):0, cb = (float)st.min; + const int Y0 = (int)(-cb/ca); + int pY=0; + cimg_foroff(data,off) { + const int Y = (int)((data[off]-cb)/ca); + switch (gtype) { + case 0: // plot with segments + if (off>0) draw_line((int)((off-1)*width/data.size()),pY,(int)(off*width/data.size()),Y,color,~0L,opacity); + break; + case 1: { // plot with bars + const unsigned int X = off*width/data.size(), nX = (off+1)*width/data.size()-1; + draw_rectangle(X,(int)Y0,nX,Y,color1,opacity); + draw_line(X,Y,X,(int)Y0,color2,~0L,opacity); + draw_line(X,(int)Y0,nX,(int)Y0,Y<=Y0?color2:color,~0L,opacity); + draw_line(nX,Y,nX,(int)Y0,color,~0L,opacity); + draw_line(X,Y,nX,Y,Y<=Y0?color:color2,~0L,opacity); + } break; + } + pY=Y; + } + if (gtype==2) { // plot with cubic interpolation + const CImg ndata = data.get_shared_points(0,data.size()-1); + cimg_forX(*this,x) { + const int Y = (int)((ndata.cubic_pix1d((float)x*ndata.width/width)-cb)/ca); + if (x>0) draw_line(x,pY,x+1,Y,color,~0L,opacity); + pY=Y; + } + } + delete[] color1; delete[] color2; + } + return *this; + } + + //! Draw a labelled horizontal axis on the instance image. + /** + \param x0 = lower bound of the x-range. + \param x1 = upper bound of the x-range. + \param y = Y-coordinate of the horizontal axis in the instance image. + \param color = an array of dimv() values of type \c T, defining the drawing color. + \param precision = precision of the labels. + \param grid_pattern = pattern of the grid + \param opacity = opacity of the drawing. + \note if \c precision==0, precision of the labels is automatically computed. + \see draw_graph(). + **/ + template CImg& draw_axis(const CImg& xvalues, const int y, const T *const color, + const int precision=-1, const float opacity=1.0f) { + if (!is_empty()) { + int siz = (int)xvalues.size()-1; + if (siz<=0) draw_line(0,y,width-1,y,color,~0L,opacity); + else { + if (xvalues[0]=0) std::sprintf(txt,"%.*g",precision,(double)xvalues(x)); + else std::sprintf(txt,"%g",(double)xvalues(x)); + const int xi=(int)(x*(width-1)/siz), xt = xi-(int)std::strlen(txt)*3; + draw_point(xi,y-1,color,opacity).draw_point(xi,y+1,color,opacity). + draw_text(txt,xt<0?0:xt,yt,color,0,11,opacity); + } + } + } + return *this; + } + + //! Draw a labelled vertical axis on the instance image. + template CImg& draw_axis(const int x, const CImg& yvalues, const T *const color, + const int precision=-1, const float opacity=1.0f) { + if (!is_empty()) { + int siz = (int)yvalues.size()-1; + if (siz<=0) draw_line(x,0,x,height-1,color,~0L,opacity); + else { + if (yvalues[0]=0) std::sprintf(txt,"%.*g",precision,(double)yvalues(y)); + else std::sprintf(txt,"%g",(double)yvalues(y)); + const int + yi = (int)(y*(height-1)/siz), + tmp = yi-5, + nyi = tmp<0?0:(tmp>=(int)height-11?(int)height-11:tmp), + xt = x-(int)std::strlen(txt)*7; + draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity); + if (xt>0) draw_text(txt,xt,nyi,color,0,11,opacity); + else draw_text(txt,x+3,nyi,color,0,11,opacity); + } + } + } + return *this; + } + + //! Draw a labelled horizontal+vertical axis on the instance image. + template CImg& draw_axis(const CImg& xvalues, const CImg& yvalues, const T *const color, + const int precisionx=-1, const int precisiony=-1, + const float opacity=1.0f) { + if (!is_empty()) { + const CImg nxvalues(xvalues.data,xvalues.size(),1,1,1,true); + const int sizx = (int)xvalues.size()-1, wm1 = (int)(width)-1; + if (sizx>0) { + float ox = (float)nxvalues[0]; + for (unsigned int x=1; x nyvalues(yvalues.data,yvalues.size(),1,1,1,true); + const int sizy = (int)yvalues.size()-1, hm1 = (int)(height)-1; + if (sizy>0) { + float oy = (float)nyvalues[0]; + for (unsigned int y=1; y CImg& draw_axis(const tx& x0, const tx& x1, const ty& y0, const ty& y1, + const T *const color, + const int subdivisionx=-60, const int subdivisiony=-60, + const int precisionx=-1, const int precisiony=-1, + const float opacity=1.0f) { + return draw_axis(CImg::sequence(subdivisionx>0?subdivisionx:1-(int)width/subdivisionx,x0,x1), + CImg::sequence(subdivisiony>0?subdivisiony:1-(int)height/subdivisiony,y0,y1), + color,precisionx,precisiony,opacity); + } + + //! Draw grid on the instance image + template + CImg& draw_grid(const CImg& xvalues, const CImg& yvalues, const T *const color, + const unsigned int patternx=~0U, const unsigned int patterny=~0U, + const float opacity=1.0f) { + if (!is_empty()) { + if (!xvalues.is_empty()) cimg_foroff(xvalues,x) { + const int xi = (int)xvalues[x]; + if (xi>=0 && xi<(int)width) draw_line(xi,0,xi,height-1,color,patternx,opacity); + } + if (!yvalues.is_empty()) cimg_foroff(yvalues,y) { + const int yi = (int)yvalues[y]; + if (yi>=0 && yi<(int)height) draw_line(0,yi,width-1,yi,color,patterny,opacity); + } + } + return *this; + } + + //! Draw grid on the instance image + CImg& draw_grid(const float deltax, const float deltay, + const float offsetx, const float offsety, + const T *const color, + const unsigned int patternx=~0U, const unsigned int patterny=~0U, + const bool invertx=false, const bool inverty=false, + const float opacity=1.0f) { + + CImg seqx, seqy; + + if (deltax!=0) { + const float dx = deltax>0?deltax:width*-deltax/100; + const unsigned int nx = (unsigned int)(width/dx); + seqx = CImg::sequence(1+nx,0,(unsigned int)(dx*nx)); + if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)width); + if (invertx) cimg_foroff(seqx,x) seqx(x) = width-1-seqx(x); + } + + if (deltay!=0) { + const float dy = deltay>0?deltay:height*-deltay/100; + const unsigned int ny = (unsigned int)(height/dy); + seqy = CImg::sequence(1+ny,0,(unsigned int)(dy*ny)); + if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)height); + if (inverty) cimg_foroff(seqy,y) seqy(y) = height-1-seqy(y); + } + + return draw_grid(seqx,seqy,color,patternx,patterny,opacity); + } + + // INNER CLASS used by function CImg<>::draw_fill() + template struct _draw_fill { + const T1 *const color; + const float sigma,opacity; + const CImg value; + CImg region; + + _draw_fill(const CImg& img,const int x,const int y,const int z, + const T *const pcolor,const float psigma,const float popacity): + color(pcolor),sigma(psigma),opacity(popacity), + value(img.get_vector_at(x,y,z)), region(CImg(img.width,img.height,img.depth,1,(T2)false)) { + } + + _draw_fill& operator=(const _draw_fill& d) { + color = d.color; + sigma = d.sigma; + opacity = d.opacity; + value = d.value; + region = d.region; + return *this; + } + + bool comp(const CImg& A,const CImg& B) const { + bool res=true; + const T *pA=A.data+A.size(); + for (const T *pB=B.data+B.size(); res && pA>A.data; res=(cimg::abs(*(--pA)-(*(--pB)))<=sigma) ); + return res; + } + + void fill(CImg& img,const int x,const int y,const int z) { + if (x<0 || x>=img.dimx() || y<0 || y>=img.dimy() || z<0 || z>=img.dimz()) return; + if (!region(x,y,z) && comp(value,img.get_vector_at(x,y,z))) { + const T *col=color; + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + int xmin,xmax; + if (opacity>=1) cimg_forV(img,k) img(x,y,z,k)=*(col++); + else cimg_forV(img,k) img(x,y,z,k)=(T1)(*(col++)*opacity+copacity*img(x,y,z,k)); + col-=img.dim; + region(x,y,z) = (T2)true; + for (xmin=x-1; xmin>=0 && comp(value,img.get_vector_at(xmin,y,z)); xmin--) { + if (opacity>=1) cimg_forV(img,k) img(xmin,y,z,k) = *(col++); + else cimg_forV(img,k) img(xmin,y,z,k)=(T1)(*(col++)*nopacity+copacity*img(xmin,y,z,k)); + col-=img.dim; + region(xmin,y,z)=(T2)true; + } + for (xmax=x+1; xmax=1) cimg_forV(img,k) img(xmax,y,z,k) = *(col++); + else cimg_forV(img,k) img(xmax,y,z,k)=(T1)(*(col++)*nopacity+copacity*img(xmax,y,z,k)); + col-=img.dim; + region(xmax,y,z)=(T2)true; + } + xmin++; xmax--; + for (; xmin<=xmax; xmin++) { + fill(img,xmin,y-1,z); + fill(img,xmin,y+1,z); + fill(img,xmin,y,z-1); + fill(img,xmin,y,z+1); + } + } + } + }; + + //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. + /** + \param x = X-coordinate of the starting point of the region to fill. + \param y = Y-coordinate of the starting point of the region to fill. + \param z = Z-coordinate of the starting point of the region to fill. + \param color = an array of dimv() values of type \c T, defining the drawing color. + \param region = image that will contain the mask of the filled region mask, as an output. + \param sigma = tolerance concerning neighborhood values. + \param opacity = opacity of the drawing. + + \return \p region is initialized with the binary mask of the filled region. + **/ + template CImg& draw_fill(const int x,const int y,const int z, + const T *const color, CImg& region,const float sigma=0, + const float opacity=1) { + _draw_fill F(*this,x,y,z,color,sigma,opacity); + F.fill(*this,x,y,z); + region = F.region; + return *this; + } + + //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. + /** + \param x = X-coordinate of the starting point of the region to fill. + \param y = Y-coordinate of the starting point of the region to fill. + \param z = Z-coordinate of the starting point of the region to fill. + \param color = an array of dimv() values of type \c T, defining the drawing color. + \param sigma = tolerance concerning neighborhood values. + \param opacity = opacity of the drawing. + **/ + CImg& draw_fill(const int x,const int y,const int z,const T *const color,const float sigma=0,const float opacity=1) { + CImg tmp; + return draw_fill(x,y,z,color,tmp,sigma,opacity); + } + + //! Draw a 2D filled region starting from a point (\c x,\c y) in the instance image. + /** + \param x = X-coordinate of the starting point of the region to fill. + \param y = Y-coordinate of the starting point of the region to fill. + \param color = an array of dimv() values of type \c T, defining the drawing color. + \param sigma = tolerance concerning neighborhood values. + \param opacity = opacity of the drawing. + **/ + CImg& draw_fill(const int x,const int y,const T *const color,const float sigma=0,const float opacity=1) { + CImg tmp; + return draw_fill(x,y,0,color,tmp,sigma,opacity); + } + + //! Draw a plasma square in the instance image. + /** + \param x0 = X-coordinate of the upper-left corner of the plasma. + \param y0 = Y-coordinate of the upper-left corner of the plasma. + \param x1 = X-coordinate of the lower-right corner of the plasma. + \param y1 = Y-coordinate of the lower-right corner of the plasma. + \param alpha = Alpha-parameter of the plasma. + \param beta = Beta-parameter of the plasma. + \param opacity = opacity of the drawing. + **/ + CImg& draw_plasma(const int x0, const int y0, const int x1, const int y1, + const double alpha=1.0, const double beta=1.0, const float opacity=1) { + if (!is_empty()) { + int nx0=x0,nx1=x1,ny0=y0,ny1=y1; + if (nx1=dimx()) nx1=width-1; + if (ny0<0) ny0=0; + if (ny1>=dimy()) ny1=height-1; + const int xc = (nx0+nx1)/2, yc = (ny0+ny1)/2, dx=(xc-nx0), dy=(yc-ny0); + const double dc = std::sqrt((double)(dx*dx+dy*dy))*alpha + beta; + float val = 0; + cimg_forV(*this,k) { + if (opacity>=1) { + (*this)(xc,ny0,0,k) = (T)(0.5f*((*this)(nx0,ny0,0,k)+(*this)(nx1,ny0,0,k))); + (*this)(xc,ny1,0,k) = (T)(0.5f*((*this)(nx0,ny1,0,k)+(*this)(nx1,ny1,0,k))); + (*this)(nx0,yc,0,k) = (T)(0.5f*((*this)(nx0,ny0,0,k)+(*this)(nx0,ny1,0,k))); + (*this)(nx1,yc,0,k) = (T)(0.5f*((*this)(nx1,ny0,0,k)+(*this)(nx1,ny1,0,k))); + do { + val = (float)(0.25f*((*this)(nx0,ny0,0,k)+(*this)(nx1,ny0,0,k) + + (*this)(nx1,ny1,0,k)+(*this)(nx0,ny1,0,k)) + dc*cimg::grand()); + } while (val<(float)cimg::type::min() || val>(float)cimg::type::max()); + (*this)(xc,yc,0,k) = (T)val; + } else { + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + (*this)(xc,ny0,0,k) = (T)(0.5f*((*this)(nx0,ny0,0,k)+(*this)(nx1,ny0,0,k))*nopacity + copacity*(*this)(xc,ny0,0,k)); + (*this)(xc,ny1,0,k) = (T)(0.5f*((*this)(nx0,ny1,0,k)+(*this)(nx1,ny1,0,k))*nopacity + copacity*(*this)(xc,ny1,0,k)); + (*this)(nx0,yc,0,k) = (T)(0.5f*((*this)(nx0,ny0,0,k)+(*this)(nx0,ny1,0,k))*nopacity + copacity*(*this)(nx0,yc,0,k)); + (*this)(nx1,yc,0,k) = (T)(0.5f*((*this)(nx1,ny0,0,k)+(*this)(nx1,ny1,0,k))*nopacity + copacity*(*this)(nx1,yc,0,k)); + do { + val = (float)(0.25f*(((*this)(nx0,ny0,0,k)+(*this)(nx1,ny0,0,k) + + (*this)(nx1,ny1,0,k)+(*this)(nx0,ny1,0,k)) + dc*cimg::grand())*nopacity + + copacity*(*this)(xc,yc,0,k)); + } while (val<(float)cimg::type::min() || val>(float)cimg::type::max()); + (*this)(xc,yc,0,k) = (T)val; + } + } + if (xc!=nx0 || yc!=ny0) { + draw_plasma(nx0,ny0,xc,yc,alpha,beta,opacity); + draw_plasma(xc,ny0,nx1,yc,alpha,beta,opacity); + draw_plasma(nx0,yc,xc,ny1,alpha,beta,opacity); + draw_plasma(xc,yc,nx1,ny1,alpha,beta,opacity); + } + } + return *this; + } + + //! Draw a plasma in the instance image. + /** + \param alpha = Alpha-parameter of the plasma. + \param beta = Beta-parameter of the plasma. + \param opacity = opacity of the drawing. + **/ + CImg& draw_plasma(const double alpha=1.0,const double beta=1.0,const float opacity=1) { + return draw_plasma(0,0,width-1,height-1,alpha,beta,opacity); + } + + //! Draw a 1D gaussian function in the instance image. + /** + \param xc = X-coordinate of the gaussian center. + \param sigma = Standard variation of the gaussian distribution. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + CImg& draw_gaussian(const float xc,const double sigma,const T *const color,const float opacity=1) { + if (!is_empty()) { + if (!color) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Specified color is (null)",pixel_type()); + const double sigma2 = 2*sigma*sigma; + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + const unsigned int whz = width*height*depth; + const T *col = color; + cimg_forX(*this,x) { + const float dx = (x-xc); + const double val = std::exp( -dx*dx/sigma2 ); + T *ptrd = ptr(x,0,0,0); + if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } + else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + copacity*(*ptrd)); ptrd+=whz; } + col-=dim; + } + } + return *this; + } + + //! Draw an anisotropic 2D gaussian function in the instance image. + /** + \param xc = X-coordinate of the gaussian center. + \param yc = Y-coordinate of the gaussian center. + \param tensor = 2x2 covariance matrix. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + template CImg& draw_gaussian(const float xc,const float yc,const CImg& tensor, + const T *const color,const float opacity=1) { + if (!is_empty()) { + if (tensor.width!=2 || tensor.height!=2 || tensor.depth!=1 || tensor.dim!=1) + throw CImgArgumentException("CImg<%s>::draw_gaussian() : Tensor parameter (%u,%u,%u,%u,%p) is not a 2x2 matrix.", + pixel_type(),tensor.width,tensor.height,tensor.depth,tensor.dim,tensor.data); + if (!color) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Specified color is (null)",pixel_type()); + const CImg invT = tensor.get_inverse(), invT2 = (invT*invT)/(-2.0); + const t &a=invT2(0,0), &b=2*invT2(1,0), &c=invT2(1,1); + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + const unsigned int whz = width*height*depth; + const T *col = color; + float dy = -yc; + cimg_forY(*this,y) { + float dx = -xc; + cimg_forX(*this,x) { + const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy); + T *ptrd = ptr(x,y,0,0); + if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } + else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + copacity*(*ptrd)); ptrd+=whz; } + col-=dim; + dx++; + } + dy++; + } + } + return *this; + } + + //! Draw an isotropic 2D gaussian function in the instance image + /** + \param xc = X-coordinate of the gaussian center. + \param yc = Y-coordinate of the gaussian center. + \param sigma = standard variation of the gaussian distribution. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + CImg& draw_gaussian(const float xc,const float yc,const float sigma,const T *const color,const float opacity=1) { + return draw_gaussian(xc,yc,CImg::diagonal(sigma,sigma),color,opacity); + } + + //! Draw an anisotropic 3D gaussian function in the instance image. + /** + \param xc = X-coordinate of the gaussian center. + \param yc = Y-coordinate of the gaussian center. + \param zc = Z-coordinate of the gaussian center. + \param tensor = 3x3 covariance matrix. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + template CImg& draw_gaussian(const float xc,const float yc,const float zc,const CImg& tensor, + const T *const color,const float opacity=1) { + if (!is_empty()) { + if (tensor.width!=3 || tensor.height!=3 || tensor.depth!=1 || tensor.dim!=1) + throw CImgArgumentException("CImg<%s>::draw_gaussian() : Tensor parameter (%u,%u,%u,%u,%p) is not a 3x3 matrix.", + pixel_type(),tensor.width,tensor.height,tensor.depth,tensor.dim,tensor.data); + const CImg invT = tensor.get_inverse(), invT2 = (invT*invT)/(-2.0); + const t a=invT(0,0), b=2*invT(1,0), c=2*invT(2,0), d=invT(1,1), e=2*invT(2,1), f=invT(2,2); + const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); + const unsigned int whz = width*height*depth; + const T *col = color; + cimg_forXYZ(*this,x,y,z) { + const float dx = (x-xc), dy = (y-yc), dz = (z-zc); + const double val = std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz); + T *ptrd = ptr(x,y,z,0); + if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } + else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + copacity*(*ptrd)); ptrd+=whz; } + col-=dim; + } + } + return *this; + } + + //! Draw an isotropic 3D gaussian function in the instance image + /** + \param xc = X-coordinate of the gaussian center. + \param yc = Y-coordinate of the gaussian center. + \param zc = Z-coordinate of the gaussian center. + \param sigma = standard variation of the gaussian distribution. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + CImg& draw_gaussian(const float xc,const float yc,const float zc, + const double sigma,const T *const color,const float opacity=1) { + return draw_gaussian(xc,yc,zc,CImg::diagonal(sigma,sigma,sigma),color,opacity); + } + + //! Draw a 3D object in the instance image + /** + \param X = X-coordinate of the 3d object position + \param Y = Y-coordinate of the 3d object position + \param Z = Z-coordinate of the 3d object position + \param points = Image N*3 describing 3D point coordinates + \param primitives = List of P primitives + \param colors = List of P color (or textures) + \param opacities = Image of P opacities + \param render_type = Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) + \param double_sided = Tell if object faces have two sides or are oriented. + \param focale = length of the focale + \param lightx = X-coordinate of the light + \param lighty = Y-coordinate of the light + \param lightz = Z-coordinate of the light + \param ambiant_light = Brightness (between 0..1) of the ambiant light + **/ + template + CImg& draw_object3d(const float X, const float Y, const float Z, + const CImg& points, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float ambiant_light = 0.05f) { + + static CImg light_texture; + if (is_empty() || points.is_empty() || primitives.is_empty()) return *this; + if (colors.is_empty() || opacities.is_empty()) + throw CImgArgumentException("CImg<%s>::draw_object3d() : Undefined colors or opacities",pixel_type()); + + if (points.height<3) + return draw_object3d(X,Y,Z,points.get_resize(-100,3,1,1,0),primitives,colors,opacities, + render_type,double_sided,focale,lightx,lighty,lightz,ambiant_light); + + // Create light texture for phong-like rendering + if (render_type==5) { + if (colors.size>primitives.size) light_texture = colors[primitives.size]; + else { + static float olightx=0, olighty=0, olightz=0, oambiant_light=0; + if (light_texture.is_empty() || lightx!=olightx || lighty!=olighty || lightz!=olightz || ambiant_light!=oambiant_light) { + light_texture.assign(512,512); + const float white[1]={ 1.0f }, + dlx = lightx-X, dly = lighty-Y, dlz = lightz-Z, + nl = (float)std::sqrt(dlx*dlx+dly*dly+dlz*dlz), + nlx = light_texture.width/2*(1+dlx/nl), + nly = light_texture.height/2*(1+dly/nl); + (light_texture.draw_gaussian(nlx,nly,light_texture.width/3.0f,white)+=ambiant_light).cut(0.0f,1.0f); + olightx = lightx; olighty = lighty; olightz = lightz; oambiant_light = ambiant_light; + } + } + } + + // Compute 3D to 2D projection + CImg projections(points.width,2); + cimg_forX(points,l) { + const float + x = (float)points(l,0), + y = (float)points(l,1), + z = (float)points(l,2); + const float projectedz = z + Z + focale; + projections(l,1) = Y + focale*y/projectedz; + projections(l,0) = X + focale*x/projectedz; + } + + // Compute and sort visible primitives + CImg visibles(primitives.size); + CImg zrange(primitives.size); + unsigned int nb_visibles = 0; + const float zmin = -focale+1.5f; + { cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + switch (primitive.size()) { + case 1: { // Point + const unsigned int i0 = (unsigned int)primitive(0); + const float x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z+points(i0,2)); + if (z0>zmin && x0>=0 && x0=0 && y0zmin && z1>zmin && xM>=0 && xm=0 && ymxM) xM = x2; + if (y0yM) yM = y2; + if (z0>zmin && z1>zmin && z2>zmin && xM>=0 && xm=0 && ymxM) xM = x2; + if (x3xM) xM = x3; + if (y0yM) yM = y2; + if (y3yM) yM = y3; + if (z0>zmin && z1>zmin && z2>zmin && z3>zmin && xM>=0 && xm=0 && ym::draw_object3d() : Primitive %u is invalid (size = %u, can be 1,2,3,4,6,9 or 12)", + pixel_type(),l,primitive.size()); + }} + } + if (nb_visibles<=0) return *this; + CImg permutations; + CImg(zrange.data,nb_visibles,1,1,1,true).sort(permutations,false); + + // Compute light properties + CImg lightprops; + switch (render_type) { + case 3: { // Flat Shading + lightprops.assign(nb_visibles); + cimg_forX(lightprops,l) { + const CImg& primitive = primitives(visibles(permutations(l))); + const unsigned int psize = primitive.size(); + if (psize==3 || psize==4 || psize==9 || psize==12) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + const float + x0 = (float)points(i0,0), y0 = (float)points(i0,1), z0 = (float)points(i0,2), + x1 = (float)points(i1,0), y1 = (float)points(i1,1), z1 = (float)points(i1,2), + x2 = (float)points(i2,0), y2 = (float)points(i2,1), z2 = (float)points(i2,2), + dx1 = x1-x0, dy1 = y1-y0, dz1 = z1-z0, + dx2 = x2-x0, dy2 = y2-y0, dz2 = z2-z0, + nx = dy1*dz2-dz1*dy2, + ny = dz1*dx2-dx1*dz2, + nz = dx1*dy2-dy1*dx2, + norm = (float)std::sqrt(1e-5f+nx*nx+ny*ny+nz*nz), + lx = X+(x0+x1+x2)/3-lightx, + ly = Y+(y0+y1+y2)/3-lighty, + lz = Z+(z0+z1+z2)/3-lightz, + nl = (float)std::sqrt(1e-5f+lx*lx+ly*ly+lz*lz), + factor = (-lx*nx-ly*ny-lz*nz)/(norm*nl), + nfactor = double_sided?cimg::abs(factor):cimg::max(factor,0.0f); + lightprops[l] = cimg::min(nfactor+ambiant_light,1.0f); + } else lightprops[l] = 1.0f; + } + } break; + + case 4: // Gouraud Shading + case 5: { // Phong-Shading + CImg points_normals(points.width,3,1,1,0); + cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + const unsigned int psize = primitive.size(); + const bool + triangle_flag = (psize==3) || (psize==9), + rectangle_flag = (psize==4) || (psize==12); + if (triangle_flag || rectangle_flag) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = rectangle_flag?(unsigned int)primitive(3):0; + const float + x0 = (float)points(i0,0), y0 = (float)points(i0,1), z0 = (float)points(i0,2), + x1 = (float)points(i1,0), y1 = (float)points(i1,1), z1 = (float)points(i1,2), + x2 = (float)points(i2,0), y2 = (float)points(i2,1), z2 = (float)points(i2,2), + dx1 = x1-x0, dy1 = y1-y0, dz1 = z1-z0, + dx2 = x2-x0, dy2 = y2-y0, dz2 = z2-z0, + nx = dy1*dz2-dz1*dy2, + ny = dz1*dx2-dx1*dz2, + nz = dx1*dy2-dy1*dx2, + norm = (float)std::sqrt(1e-5f+nx*nx+ny*ny+nz*nz), + nnx = nx/norm, + nny = ny/norm, + nnz = nz/norm; + points_normals(i0,0)+=nnx; points_normals(i0,1)+=nny; points_normals(i0,2)+=nnz; + points_normals(i1,0)+=nnx; points_normals(i1,1)+=nny; points_normals(i1,2)+=nnz; + points_normals(i2,0)+=nnx; points_normals(i2,1)+=nny; points_normals(i2,2)+=nnz; + if (rectangle_flag) { + points_normals(i3,0)+=nnx; points_normals(i3,1)+=nny; points_normals(i3,2)+=nnz; + } + } + } + + if (render_type==4) { + lightprops.assign(points.width); + cimg_forX(points,ll) { + const float + nx = points_normals(ll,0), + ny = points_normals(ll,1), + nz = points_normals(ll,2), + norm = (float)std::sqrt(1e-5f+nx*nx+ny*ny+nz*nz), + lx = (float)(X+points(ll,0)-lightx), + ly = (float)(Y+points(ll,1)-lighty), + lz = (float)(Z+points(ll,2)-lightz), + nl = (float)std::sqrt(1e-5f+lx*lx+ly*ly+lz*lz), + factor = (-lx*nx-ly*ny-lz*nz)/(norm*nl), + nfactor = double_sided?cimg::abs(factor):cimg::max(factor,0.0f); + lightprops[ll] = cimg::min(nfactor+ambiant_light,1.0f); + } + } else { + lightprops.assign(points.width,2); + cimg_forX(points,ll) { + const float + nx = points_normals(ll,0), + ny = points_normals(ll,1), + nz = points_normals(ll,2), + norm = (float)std::sqrt(1e-5f+nx*nx+ny*ny+nz*nz), + nnx = nx/norm, nny = ny/norm; + lightprops(ll,0) = (light_texture.width/2-1)*(1+nnx); + lightprops(ll,1) = (light_texture.height/2-1)*(1+nny); + } + } + } break; + } + + // Draw visible primitives + const unsigned int opacsize = opacities.size; + { for (unsigned int l=0; l& primitive = primitives[n_primitive]; + const CImg& color = colors[n_primitive%colors.size]; + const CImg& opacity = opacities[n_primitive%opacsize]; + const float opac = opacity.size()?(float)opacity(0):1.0f; + + switch (primitive.size()) { + case 1: { // colored point or sprite + const unsigned int n0 = (unsigned int)primitive[0]; + const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1); + if (color.size()==dim) draw_point(x0,y0,color.ptr(),opac); + else { + const float z = Z + points(n0,2); + const int factor = (int)(focale*100/(z+focale)); + const int sw = color.width*factor/200, sh = color.height*factor/200; + if (x0+sw>=0 && x0-sw<(int)width && y0+sh>=0 && y0-sh<(int)height) { + const CImg sprite = color.get_resize(-factor,-factor,1,-100,render_type<=3?1:3); + if (opacity.width==color.width && opacity.height==color.height) + draw_image(sprite,opacity.get_resize(sprite.width,sprite.height,1,sprite.dim,1),x0-sw,y0-sh,0,0); + else draw_image(sprite,x0-sw,y0-sh,0,0,opac); + } + } + } break; + case 2: { // colored line + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); + if (render_type) draw_line(x0,y0,x1,y1,color.ptr(),~0L,opac); + else draw_point(x0,y0,color.ptr(),opac).draw_point(x1,y1,color.ptr(),opac); + } break; + case 6: { // textured line + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + tx0 = (unsigned int)primitive[2], + ty0 = (unsigned int)primitive[3], + tx1 = (unsigned int)primitive[4], + ty1 = (unsigned int)primitive[5]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); + if (render_type) draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac); + else draw_point(x0,y0,color.get_vector_at(tx0,ty0).ptr(),opac). + draw_point(x1,y1,color.get_vector_at(tx1,ty1).ptr(),opac); + } break; + case 3: { // colored triangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), + x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); + switch(render_type) { + case 0: + draw_point(x0,y0,color.ptr(),opac).draw_point(x1,y1,color.ptr(),opac).draw_point(x2,y2,color.ptr(),opac); + break; + case 1: + draw_line(x0,y0,x1,y1,color.ptr(),~0L,opac).draw_line(x0,y0,x2,y2,color.ptr(),~0L,opac). + draw_line(x1,y1,x2,y2,color.ptr(),~0L,opac); + break; + case 2: + draw_triangle(x0,y0,x1,y1,x2,y2,color.ptr(),opac); + break; + case 3: + draw_triangle(x0,y0,x1,y1,x2,y2,color.ptr(),opac,lightprops(l)); + break; + case 4: + draw_triangle(x0,y0,x1,y1,x2,y2,color.ptr(),lightprops(n0),lightprops(n1),lightprops(n2),opac); + break; + case 5: + draw_triangle(x0,y0,x1,y1,x2,y2,color.ptr(),light_texture, + (unsigned int)lightprops(n0,0), (unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0), (unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0), (unsigned int)lightprops(n2,1), + opac); + break; + } + } break; + case 4: { // colored rectangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), + x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), + x3 = (int)projections(n3,0), y3 = (int)projections(n3,1); + switch(render_type) { + case 0: + draw_point(x0,y0,color.ptr(),opac).draw_point(x1,y1,color.ptr(),opac). + draw_point(x2,y2,color.ptr(),opac).draw_point(x3,y3,color.ptr(),opac); + break; + case 1: + draw_line(x0,y0,x1,y1,color.ptr(),~0L,opac).draw_line(x1,y1,x2,y2,color.ptr(),~0L,opac). + draw_line(x2,y2,x3,y3,color.ptr(),~0L,opac).draw_line(x3,y3,x0,y0,color.ptr(),~0L,opac); + break; + case 2: + draw_triangle(x0,y0,x1,y1,x2,y2,color.ptr(),opac).draw_triangle(x0,y0,x2,y2,x3,y3,color.ptr(),opac); + break; + case 3: + draw_triangle(x0,y0,x1,y1,x2,y2,color.ptr(),opac,lightprops(l)). + draw_triangle(x0,y0,x2,y2,x3,y3,color.ptr(),opac,lightprops(l)); + break; + case 4: { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); + draw_triangle(x0,y0,x1,y1,x2,y2,color.ptr(),lightprop0,lightprop1,lightprop2,opac). + draw_triangle(x0,y0,x2,y2,x3,y3,color.ptr(),lightprop0,lightprop2,lightprop3,opac); + } break; + case 5: { + const unsigned int + lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), + lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), + lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), + lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); + draw_triangle(x0,y0,x1,y1,x2,y2,color.ptr(),light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). + draw_triangle(x0,y0,x2,y2,x3,y3,color.ptr(),light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); + } break; + } + } break; + case 9: { // Textured triangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + tx0 = (unsigned int)primitive[3], + ty0 = (unsigned int)primitive[4], + tx1 = (unsigned int)primitive[5], + ty1 = (unsigned int)primitive[6], + tx2 = (unsigned int)primitive[7], + ty2 = (unsigned int)primitive[8]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), + x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); + switch(render_type) { + case 0: + draw_point(x0,y0,color.get_vector_at(tx0,ty0).ptr(),opac). + draw_point(x1,y1,color.get_vector_at(tx1,ty1).ptr(),opac). + draw_point(x2,y2,color.get_vector_at(tx2,ty2).ptr(),opac); + break; + case 1: + draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac). + draw_line(x0,y0,x2,y2,color,tx0,ty0,tx2,ty2,opac). + draw_line(x1,y1,x2,y2,color,tx1,ty1,tx2,ty2,opac); + break; + case 2: + draw_triangle(x0,y0,x1,y1,x2,y2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac); + break; + case 3: + draw_triangle(x0,y0,x1,y1,x2,y2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)); + break; + case 4: + draw_triangle(x0,y0,x1,y1,x2,y2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac); + break; + case 5: + draw_triangle(x0,y0,x1,y1,x2,y2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, + (unsigned int)lightprops(n0,0), (unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0), (unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0), (unsigned int)lightprops(n2,1), + opac); + break; + } + } break; + case 12: { // Textured rectangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3], + tx0 = (unsigned int)primitive[4], + ty0 = (unsigned int)primitive[5], + tx1 = (unsigned int)primitive[6], + ty1 = (unsigned int)primitive[7], + tx2 = (unsigned int)primitive[8], + ty2 = (unsigned int)primitive[9], + tx3 = (unsigned int)primitive[10], + ty3 = (unsigned int)primitive[11]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), + x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), + x3 = (int)projections(n3,0), y3 = (int)projections(n3,1); + switch(render_type) { + case 0: + draw_point(x0,y0,color.get_vector_at(tx0,ty0).ptr(),opac). + draw_point(x1,y1,color.get_vector_at(tx1,ty1).ptr(),opac). + draw_point(x2,y2,color.get_vector_at(tx2,ty2).ptr(),opac). + draw_point(x3,y3,color.get_vector_at(tx3,ty3).ptr(),opac); + break; + case 1: + draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac). + draw_line(x1,y1,x2,y2,color,tx1,ty1,tx2,ty2,opac). + draw_line(x2,y2,x3,y3,color,tx2,ty2,tx3,ty3,opac). + draw_line(x3,y3,x0,y0,color,tx3,ty3,tx0,ty0,opac); + break; + case 2: + draw_triangle(x0,y0,x1,y1,x2,y2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac). + draw_triangle(x0,y0,x2,y2,x3,y3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac); + break; + case 3: + draw_triangle(x0,y0,x1,y1,x2,y2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)). + draw_triangle(x0,y0,x2,y2,x3,y3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l)); + break; + case 4: { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); + draw_triangle(x0,y0,x1,y1,x2,y2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac). + draw_triangle(x0,y0,x2,y2,x3,y3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac); + } break; + case 5: { + const unsigned int + lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), + lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), + lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), + lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); + draw_triangle(x0,y0,x1,y1,x2,y2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). + draw_triangle(x0,y0,x2,y2,x3,y3,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); + } break; + } + } break; + } + } + } + return *this; + } + + //! Draw a 3D object in the instance image + template + CImg& draw_object3d(const float X, const float Y, const float Z, + const CImgList& points, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float ambiant_light = 0.05f) { + if (points.is_empty()) return *this; + CImg npoints(points.size,3,1,1,0); + tp *ptrX = npoints.ptr(), *ptrY = npoints.ptr(0,1), *ptrZ = npoints.ptr(0,2); + cimg_forX(npoints,l) { + const CImg& point = points[l]; + const unsigned int siz = point.size(); + if (!siz) + throw CImgArgumentException("CImg<%s>::draw_object3d() : Given points (size=%u) contains a null element at " + "position %u.",pixel_type(),points.size,l); + *(ptrZ++) = (siz>2)?point(2):0; + *(ptrY++) = (siz>1)?point(1):0; + *(ptrX++) = point(0); + } + return draw_object3d(X,Y,Z,npoints,primitives,colors,opacities, + render_type,double_sided,focale,lightx,lighty,lightz,ambiant_light); + } + + //! Draw a 3D object in the instance image + template + CImg& draw_object3d(const float X, const float Y, const float Z, + const CImg& points, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float ambiant_light = 0.05f) { + CImgList nopacities(opacities.size(),1); + cimglist_for(nopacities,l) nopacities(l,0) = opacities(l); + return draw_object3d(X,Y,Z,points,primitives,colors,nopacities, + render_type,double_sided,focale,lightx,lighty,lightz,ambiant_light); + } + + //! Draw a 3D object in the instance image + template + CImg& draw_object3d(const float X, const float Y, const float Z, + const CImgList& points, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float ambiant_light = 0.05f) { + CImgList nopacities(opacities.size(),1); + { cimglist_for(nopacities,l) nopacities(l,0) = opacities(l); } + if (points.is_empty()) return *this; + CImg npoints(points.size,3,1,1,0); + tp *ptrX = npoints.ptr(), *ptrY = npoints.ptr(0,1), *ptrZ = npoints.ptr(0,2); + cimg_forX(npoints,l) { + const CImg& point = points[l]; + const unsigned int siz = point.size(); + if (!siz) + throw CImgArgumentException("CImg<%s>::draw_object3d() : Given points (size=%u) contains a null element at " + "position %u.",pixel_type(),points.size,l); + *(ptrZ++) = (siz>2)?point(2):0; + *(ptrY++) = (siz>1)?point(1):0; + *(ptrX++) = point(0); + } + return draw_object3d(X,Y,Z,npoints,primitives,colors,nopacities, + render_type,double_sided,focale,lightx,lighty,lightz,ambiant_light); + } + + //! Draw a 3D object in the instance image + template + CImg& draw_object3d(const float X, const float Y, const float Z, + const tp& points, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float ambiant_light = 0.05f, const float opacity=1.0f) { + return draw_object3d(X,Y,Z,points,primitives,colors, + CImg(primitives.size,1,1,1,opacity), + render_type,double_sided,focale,lightx,lighty,lightz, + ambiant_light); + } + + //! Rescale and center a 3D object + CImg& resize_object3d(const float siz=100, const bool centering=true) { + T *ptrx = ptr(0,0), *ptry = ptr(0,1), *ptrz = ptr(0,2); + float xm = (float)*(ptrx++), ym = (float)*(ptry++), zm = (float)*(ptrz++), xM = xm, yM = ym, zM = zm; + for (unsigned int p=1; pxM) xM = x; + if (y>yM) yM = y; + if (z>zM) zM = z; + } + const float + cx = 0.5f*(xm+xM), + cy = 0.5f*(ym+yM), + cz = 0.5f*(zm+zM), + delta = cimg::max(xM-xm,yM-ym,zM-zm), + ratio = (siz>=0)?(delta<=0?0:(siz/delta)):-siz/100; + ptrx = ptr(0,0); + ptry = ptr(0,1); + ptrz = ptr(0,2); + if (centering) cimg_forX(*this,l) { + T &x = *(ptrx++), &y = *(ptry++), &z = *(ptrz++); + x = (T)((x-cx)*ratio); + y = (T)((y-cy)*ratio); + z = (T)((z-cz)*ratio); + } else cimg_forX(*this,l) { + T &x = *(ptrx++), &y = *(ptry++), &z = *(ptrz++); + x = (T)(cx+(x-cx)*ratio); + y = (T)(cy+(y-cy)*ratio); + z = (T)(cz+(z-cz)*ratio); + } + return *this; + } + + //! Get a rescaled and centered version of the 3D object + CImg get_resize_object3d(const float siz=100, const bool centering=true) const { + return CImg(*this,false).resize_object3d(siz,centering); + } + + //@} + //---------------------------- + // + //! \name Image Filtering + //@{ + //---------------------------- + + //! Return the correlation of the image by a mask. + /** + The result \p res of the correlation of an image \p img by a mask \p mask is defined to be : + + res(x,y,z) = sum_{i,j,k} img(x+i,y+j,z+k)*mask(i,j,k) + + \param mask = the correlation kernel. + \param cond = the border condition type (0=zero, 1=dirichlet) + \param weighted_correl = enable local normalization. + **/ + template CImg::type> + get_correlate(const CImg& mask,const unsigned int cond=1,const bool weighted_correl=false) const { + typedef typename cimg::largest::type restype; + typedef typename cimg::largest::type fftype; + typedef typename cimg::largest::type ftype; + + if (is_empty()) return CImg(); + if (mask.is_empty() || mask.dim!=1) + throw CImgArgumentException("CImg<%s>::get_correlate() : Specified mask (%u,%u,%u,%u,%p) is not scalar.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + CImg dest(width,height,depth,dim); + if (cond && mask.width==mask.height && ((mask.depth==1 && mask.width<=5) || (mask.depth==mask.width && mask.width<=3))) { + // A special optimization is done for 2x2,3x3,4x4,5x5,2x2x2 and 3x3x3 mask (with cond=1) + switch (mask.depth) { + case 3: { + CImg_3x3x3(I,T); + if (!weighted_correl) cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) dest(x,y,z,v) = cimg_corr3x3x3(I,mask); + else cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum3x3x3(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_corr3x3x3(I,mask)/std::sqrt(norm)):0; + } + } break; + case 2: { + CImg_2x2x2(I,T); + if (!weighted_correl) cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) dest(x,y,z,v) = cimg_corr2x2x2(I,mask); + else cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum2x2x2(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_corr2x2x2(I,mask)/std::sqrt(norm)):0; + } + } break; + default: + case 1: + switch (mask.width) { + case 5: { + CImg_5x5(I,T); + if (!weighted_correl) cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) dest(x,y,z,v) = cimg_corr5x5(I,mask); + else cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum5x5(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_corr5x5(I,mask)/std::sqrt(norm)):0; + } + } break; + case 4: { + CImg_4x4(I,T); + if (!weighted_correl) cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) dest(x,y,z,v) = cimg_corr4x4(I,mask); + else cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum4x4(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_corr4x4(I,mask)/std::sqrt(norm)):0; + } + } break; + case 3: { + CImg_3x3(I,T); + if (!weighted_correl) cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) dest(x,y,z,v) = cimg_corr3x3(I,mask); + else cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum3x3(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_corr3x3(I,mask)/std::sqrt(norm)):0; + } + } break; + case 2: { + CImg_2x2(I,T); + if (!weighted_correl) cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) dest(x,y,z,v) = cimg_corr2x2(I,mask); + else cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum2x2(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_corr2x2(I,mask)/std::sqrt(norm)):0; + } + } break; + case 1: dest = mask(0)*(*this); break; + } + } + } else { + // Generic version for other masks + const int cxm=mask.width/2, cym=mask.height/2, czm=mask.depth/2, fxm=cxm-1+(mask.width%2), fym=cym-1+(mask.height%2), fzm=czm-1+(mask.depth%2); + cimg_forV(*this,v) + if (!weighted_correl) { // Classical correlation + for (int z=czm; z=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + ftype val = 0; + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + val+= pix3d(x+xm,y+ym,z+zm,v)*mask(cxm+xm,cym+ym,czm+zm,0); + dest(x,y,z,v)=(restype)val; + } + else cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + ftype val = 0; + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + val+= pix3d(x+xm,y+ym,z+zm,v,0)*mask(cxm+xm,cym+ym,czm+zm,0); + dest(x,y,z,v)=(restype)val; + } + } else { // Weighted correlation + for (int z=czm; z=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + ftype val = 0, norm = 0; + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) { + const T cval = pix3d(x+xm,y+ym,z+zm,v); + val+= cval*mask(cxm+xm,cym+ym,czm+zm,0); + norm+=cval*cval; + } + dest(x,y,z,v)=(norm!=0)?(restype)(val/std::sqrt((double)norm)):0; + } + else cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + ftype val = 0, norm = 0; + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) { + const T cval = pix3d(x+xm,y+ym,z+zm,v,0); + val+= cval*mask(cxm+xm,cym+ym,czm+zm,0); + norm+= cval*cval; + } + dest(x,y,z,v)=(norm!=0)?(restype)(val/std::sqrt((double)norm)):0; + } + } + } + return dest; + } + + + //! Correlate the image by a mask + /** + This is the in-place version of get_correlate. + \see get_correlate + **/ + template CImg& correlate(const CImg& mask,const unsigned int cond=1,const bool weighted_correl=false) { + return get_correlate(mask,cond,weighted_correl).swap(*this); + } + + //! Return the convolution of the image by a mask + /** + The result \p res of the convolution of an image \p img by a mask \p mask is defined to be : + + res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*mask(i,j,k) + + \param mask = the correlation kernel. + \param cond = the border condition type (0=zero, 1=dirichlet) + \param weighted_convol = enable local normalization. + **/ + template CImg::type> + get_convolve(const CImg& mask, const unsigned int cond=1, const bool weighted_convol=false) const { + typedef typename cimg::largest::type restype; + typedef typename cimg::largest::type fftype; + typedef typename cimg::largest::type ftype; + + if (is_empty()) return CImg(); + if (mask.is_empty() || mask.dim!=1) + throw CImgArgumentException("CImg<%s>::get_convolve() : Specified mask (%u,%u,%u,%u,%p) is not scalar.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + CImg dest(width,height,depth,dim); + if (cond && mask.width==mask.height && ((mask.depth==1 && mask.width<=5) || (mask.depth==mask.width && mask.width<=3))) { // optimized version + switch (mask.depth) { + case 3: { + CImg_3x3x3(I,T); + if (!weighted_convol) cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) dest(x,y,z,v) = cimg_conv3x3x3(I,mask); + else cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum3x3x3(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_conv3x3x3(I,mask)/std::sqrt(norm)):0; + } + } break; + case 2: { + CImg_2x2x2(I,T); + if (!weighted_convol) cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) dest(x,y,z,v) = cimg_conv2x2x2(I,mask); + else cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum2x2x2(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_conv2x2x2(I,mask)/std::sqrt(norm)):0; + } + } break; + default: + case 1: + switch (mask.width) { + case 5: { + CImg_5x5(I,T); + if (!weighted_convol) cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) dest(x,y,z,v) = cimg_conv5x5(I,mask); + else cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum5x5(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_conv5x5(I,mask)/std::sqrt(norm)):0; + } + } break; + case 4: { + CImg_4x4(I,T); + if (!weighted_convol) cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) dest(x,y,z,v) = (T)cimg_conv4x4(I,mask); + else cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum4x4(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_conv4x4(I,mask)/std::sqrt(norm)):0; + } + } break; + case 3: { + CImg_3x3(I,T); + if (!weighted_convol) cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) dest(x,y,z,v) = (T)cimg_conv3x3(I,mask); + else cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum3x3(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_conv3x3(I,mask)/std::sqrt(norm)):0; + } + } break; + case 2: { + CImg_2x2(I,T); + if (!weighted_convol) cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) dest(x,y,z,v) = (T)cimg_conv2x2(I,mask); + else cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) { + const double norm = (double)cimg_squaresum2x2(I); + dest(x,y,z,v) = (norm!=0)?(restype)(cimg_conv2x2(I,mask)/std::sqrt(norm)):0; + } + } break; + case 1: dest = mask(0)*(*this); break; + } + } + } else { // generic version + + const int cxm=mask.width/2, cym=mask.height/2, czm=mask.depth/2, fxm=cxm-1+(mask.width%2), fym=cym-1+(mask.height%2), fzm=czm-1+(mask.depth%2); + cimg_forV(*this,v) + if (!weighted_convol) { // Classical convolution + for (int z=czm; z=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + ftype val = 0; + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + val+= pix3d(x-xm,y-ym,z-zm,v)*mask(cxm+xm,cym+ym,czm+zm,0); + dest(x,y,z,v)=(restype)val; + } + else cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + ftype val = 0; + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + val+= pix3d(x-xm,y-ym,z-zm,v,0)*mask(cxm+xm,cym+ym,czm+zm,0); + dest(x,y,z,v)=(restype)val; + } + } else { // Weighted convolution + for (int z=czm; z=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + ftype val = 0, norm = 0; + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) { + const T cval = pix3d(x-xm,y-ym,z-zm,v); + val+= cval*mask(cxm+xm,cym+ym,czm+zm,0); + norm+=cval*cval; + } + dest(x,y,z,v)=(norm!=0)?(restype)(val/std::sqrt(norm)):0; + } + else cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + double val = 0, norm = 0; + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) { + const T cval = pix3d(x-xm,y-ym,z-zm,v,0); + val+= cval*mask(cxm+xm,cym+ym,czm+zm,0); + norm+= cval*cval; + } + dest(x,y,z,v)=(norm!=0)?(restype)(val/std::sqrt(norm)):0; + } + } + } + return dest; + } + + //! Convolve the image by a mask + /** + This is the in-place version of get_convolve(). + \see get_convolve() + **/ + template CImg& convolve(const CImg& mask,const unsigned int cond=1,const bool weighted_convol=false) { + return get_convolve(mask,cond,weighted_convol).swap(*this); + } + + //! Return the erosion of the image by a structuring element. + template CImg::type> + get_erode(const CImg& mask, const unsigned int cond=1, const bool weighted_erosion=false) const { + typedef typename cimg::largest::type restype; + if (is_empty()) return CImg(); + if (mask.is_empty() || mask.dim!=1) + throw CImgArgumentException("CImg<%s>::get_erosion() : Specified mask (%u,%u,%u,%u,%p) is not a scalar image.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + CImg dest(width,height,depth,dim); + const int cxm=mask.width/2, cym=mask.height/2, czm=mask.depth/2, + fxm=cxm-1+(mask.width%2), fym=cym-1+(mask.height%2), fzm=czm-1+(mask.depth%2); + cimg_forV(*this,v) + if (!weighted_erosion) { // Classical erosion + for (int z=czm; z::max(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if (mask(cxm+xm,cym+ym,czm+zm,0)) min_val = cimg::min((restype)(*this)(x+xm,y+ym,z+zm,v),min_val); + dest(x,y,z,v)=min_val; + } + if (cond) cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + restype min_val = cimg::type::max(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if (mask(cxm+xm,cym+ym,czm+zm,0)) min_val = cimg::min((restype)pix3d(x+xm,y+ym,z+zm,v),min_val); + dest(x,y,z,v)=min_val; + } + else cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + restype min_val = cimg::type::max(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if (mask(cxm+xm,cym+ym,czm+zm,0)) min_val = cimg::min((restype)pix3d(x+xm,y+ym,z+zm,v,0),min_val); + dest(x,y,z,v)=min_val; + } + } else { // Weighted erosion + t mval=0; + for (int z=czm; z::max(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) min_val = cimg::min((restype)((*this)(x+xm,y+ym,z+zm,v)+mval),min_val); + dest(x,y,z,v)=min_val; + } + if (cond) cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + restype min_val = cimg::type::max(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) min_val = cimg::min((restype)(pix3d(x+xm,y+ym,z+zm,v)+mval),min_val); + dest(x,y,z,v)=min_val; + } + else cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + restype min_val = cimg::type::max(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) min_val = cimg::min((restype)(pix3d(x+xm,y+ym,z+zm,v,0)+mval),min_val); + dest(x,y,z,v)=min_val; + } + } + return dest; + } + + //! Erode the image by a structuring element + /** + This is the in-place version of get_erode(). + \see get_erode() + **/ + template CImg& erode(const CImg& mask,const unsigned int cond=1,const bool weighted_erosion=false) { + return get_erode(mask,cond,weighted_erosion).swap(*this); + } + + //! Erode the image by a square structuring element of size n + CImg get_erode(const unsigned int n, const unsigned int cond=1) const { + static CImg mask; + if (mask.width!=n) mask.assign(n,n,1,1,1); + const CImg res = get_erode(mask,cond,false); + if (n>20) mask.assign(); + return res; + } + + //! Erode the image by a square structuring element of size n + CImg& erode(const unsigned int n, const unsigned int cond=1) { + return get_erode(n,cond).swap(*this); + } + + //! Return the dilatation of the image by a structuring element. + template CImg::type> + get_dilate(const CImg& mask, const unsigned int cond=1, const bool weighted_dilatation=false) const { + typedef typename cimg::largest::type restype; + if (is_empty()) return CImg(); + if (mask.is_empty() || mask.dim!=1) + throw CImgArgumentException("CImg<%s>::get_dilate() : Specified mask (%u,%u,%u,%u,%p) is not a scalar image.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + CImg dest(width,height,depth,dim); + const int cxm=mask.width/2, cym=mask.height/2, czm=mask.depth/2, + fxm=cxm-1+(mask.width%2), fym=cym-1+(mask.height%2), fzm=czm-1+(mask.depth%2); + cimg_forV(*this,v) + if (!weighted_dilatation) { // Classical dilatation + for (int z=czm; z::min(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if (mask(cxm+xm,cym+ym,czm+zm,0)) max_val = cimg::max((restype)(*this)(x+xm,y+ym,z+zm,v),max_val); + dest(x,y,z,v)=max_val; + } + if (cond) cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + restype max_val = cimg::type::min(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if (mask(cxm+xm,cym+ym,czm+zm,0)) max_val = cimg::max((restype)pix3d(x+xm,y+ym,z+zm,v),max_val); + dest(x,y,z,v)=max_val; + } + else cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + restype max_val = cimg::type::min(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if (mask(cxm+xm,cym+ym,czm+zm,0)) max_val = cimg::max((restype)pix3d(x+xm,y+ym,z+zm,v,0),max_val); + dest(x,y,z,v)=max_val; + } + } else { // Weighted dilatation + t mval=0; + for (int z=czm; z::min(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) max_val = cimg::max((restype)((*this)(x+xm,y+ym,z+zm,v)-mval),max_val); + dest(x,y,z,v)=max_val; + } + if (cond) cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + restype max_val = cimg::type::min(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) max_val = cimg::max((restype)(pix3d(x+xm,y+ym,z+zm,v)-mval),max_val); + dest(x,y,z,v)=max_val; + } + else cimg_forYZV(*this,y,z,v) + for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { + restype max_val = cimg::type::min(); + for (int zm=-czm; zm<=fzm; zm++) for (int ym=-cym; ym<=fym; ym++) for (int xm=-cxm; xm<=fxm; xm++) + if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) max_val = cimg::max((restype)(pix3d(x+xm,y+ym,z+zm,v,0)-mval),max_val); + dest(x,y,z,v)=max_val; + } + } + return dest; + } + + //! Dilate the image by a structuring element + /** + This is the in-place version of get_dilate(). + \see get_dilate() + **/ + template CImg& dilate(const CImg& mask,const unsigned int cond=1,const bool weighted_dilatation=false) { + return get_dilate(mask,cond,weighted_dilatation).swap(*this); + } + + //! Dilate the image by a square structuring element of size n + CImg get_dilate(const unsigned int n, const unsigned int cond=1) const { + static CImg mask; + if (mask.width!=n) mask.assign(n,n,1,1,1); + const CImg res = get_dilate(mask,cond,false); + if (n>20) mask.assign(); + return res; + } + + //! Dilate the image by a square structuring element of size n + CImg& dilate(const unsigned int n, const unsigned int cond=1) { + return get_dilate(n,cond).swap(*this); + } + + //! Add noise to the image + /** + This is the in-place version of get_noise. + \see get_noise. + **/ + CImg& noise(const double sigma=-20, const unsigned int ntype=0) { + if (!is_empty()) { + double nsigma = sigma, max = (double)cimg::type::max(), min = (double)cimg::type::min(); + static bool first_time = true; + if (first_time) { std::srand((unsigned int)::time(0)); first_time = false; } + CImgStats st; + if (nsigma==0) return *this; + if (nsigma<0 || ntype==2) st = CImgStats(*this,false); + if (nsigma<0) nsigma = -nsigma*(st.max-st.min)/100.0; + switch (ntype) { + case 0: { // Gaussian noise + cimg_for(*this,ptr,T) { + double val = *ptr+nsigma*cimg::grand(); + if (val>max) val = max; + if (valmax) val = max; + if (val100.0) *ptr = (T)(unsigned int)((std::sqrt(z) * cimg::grand()) + z); + else { + unsigned int k = 0; + const double y=std::exp(-z); + for (double s=1.0; s>=y; k++) s *= cimg::rand(); + *ptr=(T)(k-1); + } + } + } + } break; + case 4: { // Rice noise + const double sqrt2 = (double)std::sqrt(2.0); + cimg_for(*this,ptr,T) { + const double + val0 = (double)*ptr/sqrt2, + re = val0 + nsigma*cimg::grand(), + im = val0 + nsigma*cimg::grand(); + double val = std::sqrt(re*re + im*im); + if (val>max) val = max; + if (val=0; i--) { Y0=a3*I1+a4*I2+b1*Y1+b2*Y2; ima-=offset; \ + I2=I1; I1=*ima; *ima=(T)(*(--Y)+Y0); Y2=Y1; Y1=Y0; } \ + } + + //! Apply a deriche filter on the image + /** + This is the in-place version of get_deriche + \see get_deriche. + **/ + CImg& deriche(const float sigma=1,const int order=0,const char axe='x',const unsigned int cond=1) { + if (!is_empty()) { + if (sigma<0 || order<0 || order>2) + throw CImgArgumentException("CImg<%s>::deriche() : Bad arguments (sigma=%g, order=%d)",pixel_type(),sigma,order); + const float alpha=sigma>0?(1.695f/sigma):20,ea=(float)std::exp(alpha),ema=(float)std::exp(-alpha),em2a=ema*ema,b1=2*ema,b2=-em2a; + float ek,ekn,parity,a1,a2,a3,a4,g0,sumg1,sumg0; + double *Y,Y0,Y1,Y2; + int i,offset,nb; + T *ima,I1,I2; + switch(order) { + case 1: // first derivative + ek = -(1-ema)*(1-ema)*(1-ema)/(2*(ema+1)*ema); a1 = a4 = 0; a2 = ek*ema; a3 = -ek*ema; parity =-1; + if (cond) { sumg1 = (ek*ea) / ((ea-1)*(ea-1)); g0 = 0; sumg0 = g0+sumg1; } + else g0 = sumg0 = sumg1 = 0; + break; + case 2: // second derivative + ekn = ( -2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea) ); + ek = -(em2a-1)/(2*alpha*ema); a1 = ekn; a2 = -ekn*(1+ek*alpha)*ema; a3 = ekn*(1-ek*alpha)*ema; a4 = -ekn*em2a; parity =1; + if (cond) { sumg1 = ekn/2; g0 = ekn; sumg0 = g0+sumg1; } + else g0=sumg0=sumg1=0; + break; + default: // smoothing + ek = (1-ema)*(1-ema) / (1+2*alpha*ema - em2a); a1 = ek; a2 = ek*ema*(alpha-1); a3 = ek*ema*(alpha+1); a4 = -ek*em2a; parity = 1; + if (cond) { sumg1 = ek*(alpha*ea+ea-1) / ((ea-1)*(ea-1)); g0 = ek; sumg0 = g0+sumg1; } + else g0=sumg0=sumg1=0; + break; + } + // filter init + Y = new double[cimg::max(width,height,depth)]; + switch(cimg::uncase(axe)) { + case 'x': if (width>1) { offset = 1; nb = width; cimg_forYZV(*this,y,z,k) cimg_deriche_apply(0,y,z,k,nb,offset,T); } break; + case 'y': if (height>1) { offset = width; nb = height; cimg_forXZV(*this,x,z,k) cimg_deriche_apply(x,0,z,k,nb,offset,T); } break; + case 'z': if (depth>1) { offset = width*height; nb = depth; cimg_forXYV(*this,x,y,k) cimg_deriche_apply(x,y,0,k,nb,offset,T); } break; + default: throw CImgArgumentException("CImg<%s>::deriche() : unknow axe '%c', must be 'x','y' or 'z'",pixel_type(),axe); + } + delete[] Y; + } + return *this; + } + + //! Return the result of the Deriche filter + /** + The Canny-Deriche filter is a recursive algorithm allowing to compute blurred derivatives of + order 0,1 or 2 of an image. + \see blur + **/ + CImg get_deriche(const float sigma=1,const int order=0,const char axe='x',const unsigned int cond=1) const { + return (+*this).deriche(sigma,order,axe,cond); + } + + //! Blur the image with a Deriche filter (anisotropically) + /** + This is the in-place version of get_blur(). + \see get_blur(). + **/ + CImg& blur(const float sigmax,const float sigmay,const float sigmaz,const unsigned int cond=1) { + if (!is_empty()) { + if (width>1 && sigmax>0) deriche(sigmax,0,'x',cond); + if (height>1 && sigmay>0) deriche(sigmay,0,'y',cond); + if (depth>1 && sigmaz>0) deriche(sigmaz,0,'z',cond); + } + return *this; + } + + //! Blur the image with a Canny-Deriche filter. + /** This is the in-place version of get_blur(). **/ + CImg& blur(const float sigma,const unsigned int cond=1) { return blur(sigma,sigma,sigma,cond); } + + //! Return a blurred version of the image, using a Canny-Deriche filter. + /** + Blur the image with an anisotropic exponential filter (Deriche filter of order 0). + **/ + CImg get_blur(const float sigmax,const float sigmay,const float sigmaz,const unsigned int cond=1) const { + return (+*this).blur(sigmax,sigmay,sigmaz,cond); + } + + //! Return a blurred version of the image, using a Canny-Deriche filter. + CImg get_blur(const float sigma,const unsigned int cond=1) const { + return (+*this).blur(sigma,cond); + } + + //! Blur an image following a field of diffusion tensors. + /** This is the in-place version of get_blur_anisotropic(). **/ + template + CImg& blur_anisotropic(const CImg& G, const float amplitude=60.0f, const float dl=0.8f,const float da=30.0f, + const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true) { +#define cimg_valign2d(i,j) \ + { ftype &u = W(i,j,0,0), &v = W(i,j,0,1); \ + if (u*curru + v*currv<0) { u=-u; v=-v; }} +#define cimg_valign3d(i,j,k) \ + { ftype &u = W(i,j,k,0), &v = W(i,j,k,1), &w = W(i,j,k,2); \ + if (u*curru + v*currv + w*currw<0) { u=-u; v=-v; w=-w; }} + + // Check arguments and init variables + typedef typename cimg::largest::type ftype; + if (!is_empty() && amplitude>0) { + if (G.is_empty() || (G.dim!=3 && G.dim!=6) || G.width!=width || G.height!=height || G.depth!=depth) + throw CImgArgumentException("CImg<%s>::blur_anisotropic() : Specified tensor field (%u,%u,%u,%u) is not valid.", + pixel_type(),G.width,G.height,G.depth,G.dim); + + const float sqrt2amplitude = (float)std::sqrt(2*amplitude); + const bool threed = (G.dim>=6); + const int + dx1 = dimx()-1, + dy1 = dimy()-1, + dz1 = dimz()-1; + CImg + dest(width,height,depth,dim,0), + W(width,height,depth,threed?4:3), + tmp(dim); + int N = 0; + + if (threed) + // 3D version of the algorithm + for (float phi=(180%(int)da)/2.0f; phi<=180; phi+=da) { + const float + phir = (float)(phi*cimg::PI/180), + datmp = (float)(da/std::cos(phir)), + da2 = datmp<1?360.0f:datmp; + + for (float theta=0; theta<360; (theta+=da2),N++) { + const float + thetar = (float)(theta*cimg::PI/180), + vx = (float)(std::cos(thetar)*std::cos(phir)), + vy = (float)(std::sin(thetar)*std::cos(phir)), + vz = (float)std::sin(phir); + const t + *pa = G.ptr(0,0,0,0), + *pb = G.ptr(0,0,0,1), + *pc = G.ptr(0,0,0,2), + *pd = G.ptr(0,0,0,3), + *pe = G.ptr(0,0,0,4), + *pf = G.ptr(0,0,0,5); + ftype + *pd0 = W.ptr(0,0,0,0), + *pd1 = W.ptr(0,0,0,1), + *pd2 = W.ptr(0,0,0,2), + *pd3 = W.ptr(0,0,0,3); + cimg_forXYZ(G,xg,yg,zg) { + const t + a = *(pa++), b = *(pb++), c = *(pc++), + d = *(pd++), e = *(pe++), f = *(pf++); + const float + u = (float)(a*vx + b*vy + c*vz), + v = (float)(b*vx + d*vy + e*vz), + w = (float)(c*vx + e*vy + f*vz), + n = (float)std::sqrt(1e-5+u*u+v*v+w*w), + dln = dl/n; + *(pd0++) = (ftype)(u*dln); + *(pd1++) = (ftype)(v*dln); + *(pd2++) = (ftype)(w*dln); + *(pd3++) = (ftype)n; + } + + cimg_forXYZ(*this,x,y,z) { + tmp.fill(0); + const float + cu = (float)W(x,y,z,0), + cv = (float)W(x,y,z,1), + cw = (float)W(x,y,z,2), + n = (float)W(x,y,z,3), + fsigma = (float)(n*sqrt2amplitude), + length = gauss_prec*fsigma, + fsigma2 = 2*fsigma*fsigma; + float + S = 0, + pu = cu, + pv = cv, + pw = cw, + X = (float)x, + Y = (float)y, + Z = (float)z; + + switch (interpolation) { + case 0: + // Nearest neighbor + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)(X+0.5f), + cy = (int)(Y+0.5f), + cz = (int)(Z+0.5f); + float + u = (float)W(cx,cy,cz,0), + v = (float)W(cx,cy,cz,1), + w = (float)W(cx,cy,cz,2); + if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)(*this)(cx,cy,cz,k); S++; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(ftype)(coef*(*this)(cx,cy,cz,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); Z+=(pw=w); + } break; + + case 1: + // Linear interpolation + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, + cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1, + cz = (int)Z, pz = (cz-1<0)?0:cz-1, nz = (cz+1>dz1)?dz1:cz+1; + const float + curru = (float)W(cx,cy,cz,0), + currv = (float)W(cx,cy,cz,1), + currw = (float)W(cx,cy,cz,2); + cimg_valign3d(px,py,pz); cimg_valign3d(cx,py,pz); cimg_valign3d(nx,py,pz); + cimg_valign3d(px,cy,pz); cimg_valign3d(cx,cy,pz); cimg_valign3d(nx,cy,pz); + cimg_valign3d(px,ny,pz); cimg_valign3d(cx,ny,pz); cimg_valign3d(nx,ny,pz); + cimg_valign3d(px,py,cz); cimg_valign3d(cx,py,cz); cimg_valign3d(nx,py,cz); + cimg_valign3d(px,cy,cz); cimg_valign3d(nx,cy,cz); + cimg_valign3d(px,ny,cz); cimg_valign3d(cx,ny,cz); cimg_valign3d(nx,ny,cz); + cimg_valign3d(px,py,nz); cimg_valign3d(cx,py,nz); cimg_valign3d(nx,py,nz); + cimg_valign3d(px,cy,nz); cimg_valign3d(cx,cy,nz); cimg_valign3d(nx,cy,nz); + cimg_valign3d(px,ny,nz); cimg_valign3d(cx,ny,nz); cimg_valign3d(nx,ny,nz); + float + u = (float)(W.linear_pix3d(X,Y,Z,0)), + v = (float)(W.linear_pix3d(X,Y,Z,1)), + w = (float)(W.linear_pix3d(X,Y,Z,2)); + if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)linear_pix3d(X,Y,Z,k); S++; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(ftype)(coef*linear_pix3d(X,Y,Z,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); Z+=(pw=w); + } break; + + default: + // 2nd order Runge Kutta + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, + cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1, + cz = (int)Z, pz = (cz-1<0)?0:cz-1, nz = (cz+1>dz1)?dz1:cz+1; + const float + curru = (float)W(cx,cy,cz,0), + currv = (float)W(cx,cy,cz,1), + currw = (float)W(cx,cy,cz,2); + cimg_valign3d(px,py,pz); cimg_valign3d(cx,py,pz); cimg_valign3d(nx,py,pz); + cimg_valign3d(px,cy,pz); cimg_valign3d(cx,cy,pz); cimg_valign3d(nx,cy,pz); + cimg_valign3d(px,ny,pz); cimg_valign3d(cx,ny,pz); cimg_valign3d(nx,ny,pz); + cimg_valign3d(px,py,cz); cimg_valign3d(cx,py,cz); cimg_valign3d(nx,py,cz); + cimg_valign3d(px,cy,cz); cimg_valign3d(nx,cy,cz); + cimg_valign3d(px,ny,cz); cimg_valign3d(cx,ny,cz); cimg_valign3d(nx,ny,cz); + cimg_valign3d(px,py,nz); cimg_valign3d(cx,py,nz); cimg_valign3d(nx,py,nz); + cimg_valign3d(px,cy,nz); cimg_valign3d(cx,cy,nz); cimg_valign3d(nx,cy,nz); + cimg_valign3d(px,ny,nz); cimg_valign3d(cx,ny,nz); cimg_valign3d(nx,ny,nz); + const float + u0 = (float)(0.5f*W.linear_pix3d(X,Y,Z,0)), + v0 = (float)(0.5f*W.linear_pix3d(X,Y,Z,1)), + w0 = (float)(0.5f*W.linear_pix3d(X,Y,Z,2)); + float + u = (float)(W.linear_pix3d(X+u0,Y+v0,Z+w0,0)), + v = (float)(W.linear_pix3d(X+u0,Y+v0,Z+w0,1)), + w = (float)(W.linear_pix3d(X+u0,Y+v0,Z+w0,2)); + if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)linear_pix3d(X,Y,Z,k); S++; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(ftype)(coef*linear_pix3d(X,Y,Z,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); Z+=(pw=w); + } break; + } + if (S>0) cimg_forV(dest,k) dest(x,y,z,k)+=tmp[k]/S; + else cimg_forV(dest,k) dest(x,y,z,k)+=(ftype)((*this)(x,y,z,k)); +#ifdef cimg_plugin_greycstoration + if (!*(greycstoration_params->stop_request)) (*greycstoration_params->counter)++; + else return *this; +#endif + } + } + } else + // 2D version of the algorithm + for (float theta=(360%(int)da)/2.0f; theta<360; (theta+=da),N++) { + const float + thetar = (float)(theta*cimg::PI/180), + vx = (float)(std::cos(thetar)), + vy = (float)(std::sin(thetar)); + const t + *pa = G.ptr(0,0,0,0), + *pb = G.ptr(0,0,0,1), + *pc = G.ptr(0,0,0,2); + ftype + *pd0 = W.ptr(0,0,0,0), + *pd1 = W.ptr(0,0,0,1), + *pd2 = W.ptr(0,0,0,2); + cimg_forXY(G,xg,yg) { + const t a = *(pa++), b = *(pb++), c = *(pc++); + const float + u = (float)(a*vx + b*vy), + v = (float)(b*vx + c*vy), + n = (float)std::sqrt(1e-5+u*u+v*v), + dln = dl/n; + *(pd0++) = (ftype)(u*dln); + *(pd1++) = (ftype)(v*dln); + *(pd2++) = (ftype)n; + } + + cimg_forXY(*this,x,y) { + tmp.fill(0); + const float + cu = (float)W(x,y,0,0), + cv = (float)W(x,y,0,1), + n = (float)W(x,y,0,2), + fsigma = (float)(n*sqrt2amplitude), + length = gauss_prec*fsigma, + fsigma2 = 2*fsigma*fsigma; + float + S = 0, + pu = cu, + pv = cv, + X = (float)x, + Y = (float)y; + + switch (interpolation) { + + case 0: + // Nearest-neighbor interpolation for 2D images + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)(X+0.5f), + cy = (int)(Y+0.5f); + float + u = (float)W(cx,cy,0,0), + v = (float)W(cx,cy,0,1); + if ((pu*u + pv*v)<0) { u=-u; v=-v; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)(*this)(cx,cy,0,k); S++; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(ftype)(coef*(*this)(cx,cy,0,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); + } break; + + case 1: + // Linear interpolation for 2D images + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, + cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1; + const float + curru = (float)W(cx,cy,0,0), + currv = (float)W(cx,cy,0,1); + cimg_valign2d(px,py); cimg_valign2d(cx,py); cimg_valign2d(nx,py); + cimg_valign2d(px,cy); cimg_valign2d(nx,cy); + cimg_valign2d(px,ny); cimg_valign2d(cx,ny); cimg_valign2d(nx,ny); + float + u = (float)(W.linear_pix2d(X,Y,0,0)), + v = (float)(W.linear_pix2d(X,Y,0,1)); + if ((pu*u + pv*v)<0) { u=-u; v=-v; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)linear_pix2d(X,Y,0,k); S++; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(ftype)(coef*linear_pix2d(X,Y,0,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); + } break; + + default: + // 2nd-order Runge-kutta interpolation for 2D images + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, + cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1; + const float + curru = (float)W(cx,cy,0,0), + currv = (float)W(cx,cy,0,1); + cimg_valign2d(px,py); cimg_valign2d(cx,py); cimg_valign2d(nx,py); + cimg_valign2d(px,cy); cimg_valign2d(nx,cy); + cimg_valign2d(px,ny); cimg_valign2d(cx,ny); cimg_valign2d(nx,ny); + const float + u0 = (float)(0.5f*W.linear_pix2d(X,Y,0,0)), + v0 = (float)(0.5f*W.linear_pix2d(X,Y,0,1)); + float + u = (float)(W.linear_pix2d(X+u0,Y+v0,0,0)), + v = (float)(W.linear_pix2d(X+u0,Y+v0,0,1)); + if ((pu*u + pv*v)<0) { u=-u; v=-v; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)linear_pix2d(X,Y,0,k); S++; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(ftype)(coef*linear_pix2d(X,Y,0,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); + } break; + } + if (S>0) cimg_forV(dest,k) dest(x,y,0,k)+=tmp[k]/S; + else cimg_forV(dest,k) dest(x,y,0,k)+=(ftype)((*this)(x,y,0,k)); +#ifdef cimg_plugin_greycstoration + if (!*(greycstoration_params->stop_request)) (*greycstoration_params->counter)++; + else return *this; +#endif + } + } + const ftype *ptrs = dest.data+dest.size(); + const T m = cimg::type::min(), M = cimg::type::max(); + cimg_for(*this,ptrd,T) { const ftype val = *(--ptrs)/N; *ptrd = valM?M:(T)val); } + } + return *this; + } + + //! Get a blurred version of an image following a field of diffusion tensors. + /** + \param G = Field of square roots of diffusion tensors used to drive the smoothing. + \param amplitude = amplitude of the smoothing. + \param dl = spatial discretization. + \param da = angular discretization. + \param gauss_prec = precision of the gaussian function. + \param interpolation Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta) + \param fast_approx = Tell to use the fast approximation or not. + **/ + template + CImg get_blur_anisotropic(const CImg& G, const float amplitude=60.0f, const float dl=0.8f,const float da=30.0f, + const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true) const { + return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation,fast_approx); + } + + //! Blur an image following a field of diffusion tensors. + template + CImg& blur_anisotropic(const CImg& mask, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,const float da=30.0f, + const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, + const float geom_factor=1.0f) { + if (!is_empty() && amplitude>0) { + if (amplitude==0) return *this; + if (amplitude<0 || sharpness<0 || anisotropy<0 || anisotropy>1 || alpha<0 || sigma<0 || dl<0 || da<0 || gauss_prec<0) + throw CImgArgumentException("CImg<%s>::blur_anisotropic() : Given parameters are amplitude(%g), sharpness(%g), " + "anisotropy(%g), alpha(%g), sigma(%g), dl(%g), da(%g), gauss_prec(%g).\n" + "Admissible parameters are in the range : amplitude>0, sharpness>0, anisotropy in [0,1], " + "alpha>0, sigma>0, dl>0, da>0, gauss_prec>0.", + pixel_type(),amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec); + const bool threed = (depth>1), no_mask = mask.is_empty(); + const float nsharpness = cimg::max(sharpness,1e-5f), power1 = 0.5f*nsharpness, power2 = power1/(1e-7f+1.0f-anisotropy); + + CImg blurred = CImg(*this,false).blur(alpha); + if (geom_factor>0) blurred*=geom_factor; + else blurred.normalize(0,-geom_factor); + + if (threed) { // Field for 3D volumes + CImg val(3), vec(3,3), G(blurred.get_structure_tensorXYZ()); + if (sigma>0) G.blur(sigma); + cimg_forXYZ(*this,x,y,z) { + if (no_mask || mask(x,y,z)) { + G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + const float l1 = val[2], l2 = val[1], l3 = val[0], + ux = vec(0,0), uy = vec(0,1), uz = vec(0,2), + vx = vec(1,0), vy = vec(1,1), vz = vec(1,2), + wx = vec(2,0), wy = vec(2,1), wz = vec(2,2), + n1 = (float)std::pow(1.0f+l1+l2+l3,-power1), + n2 = (float)std::pow(1.0f+l1+l2+l3,-power2); + G(x,y,z,0) = n1*(ux*ux + vx*vx) + n2*wx*wx; + G(x,y,z,1) = n1*(ux*uy + vx*vy) + n2*wx*wy; + G(x,y,z,2) = n1*(ux*uz + vx*vz) + n2*wx*wz; + G(x,y,z,3) = n1*(uy*uy + vy*vy) + n2*wy*wy; + G(x,y,z,4) = n1*(uy*uz + vy*vz) + n2*wy*wz; + G(x,y,z,5) = n1*(uz*uz + vz*vz) + n2*wz*wz; + } else G(x,y,z,0) = G(x,y,z,1) = G(x,y,z,2) = G(x,y,z,3) = G(x,y,z,4) = G(x,y,z,5) = 0; +#ifdef cimg_plugin_greycstoration + if (!*(greycstoration_params->stop_request)) (*greycstoration_params->counter)++; + else return *this; +#endif + } + blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation,fast_approx); + } else { // Field for 2D images + CImg val(2), vec(2,2), G(blurred.get_structure_tensorXY()); + if (sigma>0) G.blur(sigma); + cimg_forXY(*this,x,y) { + if (no_mask || mask(x,y)) { + G.get_tensor_at(x,y).symmetric_eigen(val,vec); + const float l1 = val[1], l2 = val[0], + ux = vec(1,0), uy = vec(1,1), + vx = vec(0,0), vy = vec(0,1), + n1 = (float)std::pow(1.0f+l1+l2,-power1), + n2 = (float)std::pow(1.0f+l1+l2,-power2); + G(x,y,0,0) = n1*ux*ux + n2*vx*vx; + G(x,y,0,1) = n1*ux*uy + n2*vx*vy; + G(x,y,0,2) = n1*uy*uy + n2*vy*vy; + } else G(x,y,0,0) = G(x,y,0,1) = G(x,y,0,2) = 0; +#ifdef cimg_plugin_greycstoration + if (!*(greycstoration_params->stop_request)) (*greycstoration_params->counter)++; + else return *this; +#endif + } + blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation,fast_approx); + } + } + return *this; + } + + //! Blur an image in an anisotropic way. + /** + \param amplitude = amplitude of the anisotropic blur. + \param sharpness = define the contour preservation. + \param anisotropy = define the smoothing anisotropy. + \param alpha = image pre-blurring (gaussian). + \param sigma = regularity of the tensor-valued geometry. + \param dl = spatial discretization. + \param da = angular discretization. + \param gauss_prec = precision of the gaussian function. + \param interpolation Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta) + \param fast_approx = Tell to use the fast approximation or not + **/ + template + CImg get_blur_anisotropic(const CImg& mask, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, + const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, + const bool fast_approx=true, const float geom_factor=1.0f) const { + return (+*this).blur_anisotropic(mask,amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation,fast_approx,geom_factor); + } + + //! Blur an image following in an anistropic way. + CImg& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,const float da=30.0f, + const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, + const float geom_factor=1.0f) { + return blur_anisotropic(CImg(),amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation,fast_approx,geom_factor); + } + + //! Blur an image following in an anistropic way. + CImg get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, + const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, + const bool fast_approx=true, const float geom_factor=1.0f) const { + return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation,fast_approx,geom_factor); + } + + //! Return the Fast Fourier Transform of an image (along a specified axis) + CImgList::type> get_FFT(const char axe, const bool inverse=false) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).FFT(axe,inverse); + } + + //! Return the Fast Fourier Transform on an image + CImgList::type> get_FFT(const bool inverse=false) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).FFT(inverse); + } + + //! Apply a median filter. + CImg get_blur_median(const unsigned int n=3) { + CImg res(width,height,depth,dim); + if (!n || n==1) return *this; + const int hl=n/2, hr=hl-1+n%2; + if (res.depth!=1) { // 3D median filter + CImg vois; + cimg_forXYZV(*this,x,y,z,k) { + vois = get_crop(x-hl,y-hl,z-hl,k,x+hr,y+hr,z+hr,k); + res(x,y,z,k) = vois.median(); + } + } else { // 2D median filter +#define _median_sort(a,b) if ((a)>(b)) cimg::swap(a,b) + switch (n) { + case 3: { + CImg_3x3(I,T); + CImg_3x3(J,T); + cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { + cimg_copy3x3(I,J); + _median_sort(Jcp, Jnp); _median_sort(Jcc, Jnc); _median_sort(Jcn, Jnn); + _median_sort(Jpp, Jcp); _median_sort(Jpc, Jcc); _median_sort(Jpn, Jcn); + _median_sort(Jcp, Jnp); _median_sort(Jcc, Jnc); _median_sort(Jcn, Jnn); + _median_sort(Jpp, Jpc); _median_sort(Jnc, Jnn); _median_sort(Jcc, Jcn); + _median_sort(Jpc, Jpn); _median_sort(Jcp, Jcc); _median_sort(Jnp, Jnc); + _median_sort(Jcc, Jcn); _median_sort(Jcc, Jnp); _median_sort(Jpn, Jcc); + _median_sort(Jcc, Jnp); + res(x,y,0,k) = Jcc; + } + } break; + case 5: { + CImg_5x5(I,T); + CImg_5x5(J,T); + cimg_forV(*this,k) cimg_for5x5(*this,x,y,0,k,I) { + cimg_copy5x5(I,J); + _median_sort(Jbb, Jpb); _median_sort(Jnb, Jab); _median_sort(Jcb, Jab); _median_sort(Jcb, Jnb); + _median_sort(Jpp, Jcp); _median_sort(Jbp, Jcp); _median_sort(Jbp, Jpp); _median_sort(Jap, Jbc); + _median_sort(Jnp, Jbc); _median_sort(Jnp, Jap); _median_sort(Jcc, Jnc); _median_sort(Jpc, Jnc); + _median_sort(Jpc, Jcc); _median_sort(Jbn, Jpn); _median_sort(Jac, Jpn); _median_sort(Jac, Jbn); + _median_sort(Jnn, Jan); _median_sort(Jcn, Jan); _median_sort(Jcn, Jnn); _median_sort(Jpa, Jca); + _median_sort(Jba, Jca); _median_sort(Jba, Jpa); _median_sort(Jna, Jaa); _median_sort(Jcb, Jbp); + _median_sort(Jnb, Jpp); _median_sort(Jbb, Jpp); _median_sort(Jbb, Jnb); _median_sort(Jab, Jcp); + _median_sort(Jpb, Jcp); _median_sort(Jpb, Jab); _median_sort(Jpc, Jac); _median_sort(Jnp, Jac); + _median_sort(Jnp, Jpc); _median_sort(Jcc, Jbn); _median_sort(Jap, Jbn); _median_sort(Jap, Jcc); + _median_sort(Jnc, Jpn); _median_sort(Jbc, Jpn); _median_sort(Jbc, Jnc); _median_sort(Jba, Jna); + _median_sort(Jcn, Jna); _median_sort(Jcn, Jba); _median_sort(Jpa, Jaa); _median_sort(Jnn, Jaa); + _median_sort(Jnn, Jpa); _median_sort(Jan, Jca); _median_sort(Jnp, Jcn); _median_sort(Jap, Jnn); + _median_sort(Jbb, Jnn); _median_sort(Jbb, Jap); _median_sort(Jbc, Jan); _median_sort(Jpb, Jan); + _median_sort(Jpb, Jbc); _median_sort(Jpc, Jba); _median_sort(Jcb, Jba); _median_sort(Jcb, Jpc); + _median_sort(Jcc, Jpa); _median_sort(Jnb, Jpa); _median_sort(Jnb, Jcc); _median_sort(Jnc, Jca); + _median_sort(Jab, Jca); _median_sort(Jab, Jnc); _median_sort(Jac, Jna); _median_sort(Jbp, Jna); + _median_sort(Jbp, Jac); _median_sort(Jbn, Jaa); _median_sort(Jpp, Jaa); _median_sort(Jpp, Jbn); + _median_sort(Jcp, Jpn); _median_sort(Jcp, Jan); _median_sort(Jnc, Jpa); _median_sort(Jbn, Jna); + _median_sort(Jcp, Jnc); _median_sort(Jcp, Jbn); _median_sort(Jpb, Jap); _median_sort(Jnb, Jpc); + _median_sort(Jbp, Jcn); _median_sort(Jpc, Jcn); _median_sort(Jap, Jcn); _median_sort(Jab, Jbc); + _median_sort(Jpp, Jcc); _median_sort(Jcp, Jac); _median_sort(Jab, Jpp); _median_sort(Jab, Jcp); + _median_sort(Jcc, Jac); _median_sort(Jbc, Jac); _median_sort(Jpp, Jcp); _median_sort(Jbc, Jcc); + _median_sort(Jpp, Jbc); _median_sort(Jpp, Jcn); _median_sort(Jcc, Jcn); _median_sort(Jcp, Jcn); + _median_sort(Jcp, Jbc); _median_sort(Jcc, Jnn); _median_sort(Jcp, Jcc); _median_sort(Jbc, Jnn); + _median_sort(Jcc, Jba); _median_sort(Jbc, Jba); _median_sort(Jbc, Jcc); + res(x,y,0,k) = Jcc; + } + } break; + default: { + CImg vois; + cimg_forXYV(*this,x,y,k) { + vois = get_crop(x-hl,y-hl,0,k,x+hr,y+hr,0,k); + res(x,y,0,k) = vois.median(); + } + } break; + } + } + return res; + } + + //! Apply a median filter + CImg& blur_median(const unsigned int n=3) { + return get_blur_median(n).swap(*this); + } + + //! Sharpen image using anisotropic shock filters + CImg& sharpen(const float amplitude=50.0f, const float edge=1.0f, const float alpha=0.0f, const float sigma=0.0f) { + if (is_empty()) return *this; + const bool threed = (depth>1); + const float nedge = 0.5f*edge; + typedef typename cimg::largest::type ftype; + CImg val, vec, veloc(width,height,depth,dim); + + if (threed) { + CImg G = (alpha>0?get_blur(alpha).get_structure_tensorXYZ():get_structure_tensorXYZ()); + if (sigma>0) G.blur(sigma); + CImg_3x3x3(I,float); + cimg_forXYZ(G,x,y,z) { + G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + G(x,y,z,0) = vec(0,0); + G(x,y,z,1) = vec(0,1); + G(x,y,z,2) = vec(0,2); + G(x,y,z,3) = 1.0f-(float)std::pow(1.0f+val[0]+val[1]+val[2],-nedge); + } + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + const float + u = G(x,y,z,0), + v = G(x,y,z,1), + w = G(x,y,z,2), + amp = G(x,y,z,3), + ixx = Incc+Ipcc-2*Iccc, + ixy = 0.25f*(Innc+Ippc-Inpc-Ipnc), + ixz = 0.25f*(Incn+Ipcp-Incp-Ipcn), + iyy = Icnc+Icpc-2*Iccc, + iyz = 0.25f*(Icnn+Icpp-Icnp-Icpn), + izz = Iccn+Iccp-2*Iccc, + ixf = Incc-Iccc, + ixb = Iccc-Ipcc, + iyf = Icnc-Iccc, + iyb = Iccc-Icpc, + izf = Iccn-Iccc, + izb = Iccc-Iccp, + itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb); + veloc(x,y,z,k) = -amp*cimg::sign(itt)*cimg::abs(it); + } + } else { + CImg G = (alpha>0?get_blur(alpha).get_structure_tensorXY():get_structure_tensorXY()); + if (sigma>0) G.blur(sigma); + CImg_3x3(I,float); + cimg_forXY(G,x,y) { + G.get_tensor_at(x,y).symmetric_eigen(val,vec); + G(x,y,0) = vec(0,0); + G(x,y,1) = vec(0,1); + G(x,y,2) = 1.0f-(float)std::pow(1.0f+val[0]+val[1],-nedge); + } + cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { + const float + u = G(x,y,0), + v = G(x,y,1), + amp = G(x,y,2), + ixx = Inc+Ipc-2*Icc, + ixy = 0.25f*(Inn+Ipp-Inp-Ipn), + iyy = Icn+Icp-2*Icc, + ixf = Inc-Icc, + ixb = Icc-Ipc, + iyf = Icn-Icc, + iyb = Icc-Icp, + itt = u*u*ixx + v*v*iyy + 2*u*v*ixy, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb); + veloc(x,y,k) = -amp*cimg::sign(itt)*cimg::abs(it); + } + } + const CImgStats stats(veloc); + const float vmax = (float)cimg::max(cimg::abs(stats.min),cimg::abs(stats.max)); + if (vmax!=0) { veloc*=amplitude/vmax; (*this)+=veloc; } + return *this; + } + + CImg get_sharpen(const float amplitude=50.0f, const float edge=1.0f, const float alpha=0.0f, const float sigma=0.0f) const { + return (+*this).sharpen(amplitude,edge,alpha,sigma); + } + + //@} + //----------------------------- + // + //! \name Matrix and Vectors + //@{ + //----------------------------- + + //! Return a vector with specified coefficients + static CImg vector(const T& a1) { + return CImg(1,1).fill(a1); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2) { + return CImg(1,2).fill(a1,a2); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3) { + return CImg(1,3).fill(a1,a2,a3); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4) { + return CImg(1,4).fill(a1,a2,a3,a4); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4,const T& a5) { + return CImg(1,5).fill(a1,a2,a3,a4,a5); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4,const T& a5,const T& a6) { + return CImg(1,6).fill(a1,a2,a3,a4,a5,a6); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4, + const T& a5,const T& a6,const T& a7) { + return CImg(1,7).fill(a1,a2,a3,a4,a5,a6,a7); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4, + const T& a5,const T& a6,const T& a7,const T& a8) { + return CImg(1,8).fill(a1,a2,a3,a4,a5,a6,a7,a8); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4, + const T& a5,const T& a6,const T& a7,const T& a8,const T& a9) { + return CImg(1,9).fill(a1,a2,a3,a4,a5,a6,a7,a8,a9); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4, + const T& a5,const T& a6,const T& a7,const T& a8, + const T& a9,const T& a10) { + return CImg(1,10).fill(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4, + const T& a5,const T& a6,const T& a7,const T& a8, + const T& a9,const T& a10, const T& a11) { + return CImg(1,11).fill(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4, + const T& a5,const T& a6,const T& a7,const T& a8, + const T& a9,const T& a10, const T& a11, const T& a12) { + return CImg(1,12).fill(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12); + } + + //! Return a vector with specified coefficients + static CImg vector(const T& a1,const T& a2,const T& a3,const T& a4, + const T& a5,const T& a6,const T& a7,const T& a8, + const T& a9,const T& a10, const T& a11, const T& a12, + const T& a13) { + return CImg(1,13).fill(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13); + } + + //! Return a 1x1 square matrix with specified coefficients + static CImg matrix(const T& a1) { + return vector(a1); + } + + //! Return a 2x2 square matrix with specified coefficients + static CImg matrix(const T& a1,const T& a2, + const T& a3,const T& a4) { + return CImg(2,2).fill(a1,a2, + a3,a4); + } + + //! Return a 3x3 square matrix with specified coefficients + static CImg matrix(const T& a1,const T& a2,const T& a3, + const T& a4,const T& a5,const T& a6, + const T& a7,const T& a8,const T& a9) { + return CImg(3,3).fill(a1,a2,a3, + a4,a5,a6, + a7,a8,a9); + } + + //! Return a 4x4 square matrix with specified coefficients + static CImg matrix(const T& a1,const T& a2,const T& a3,const T& a4, + const T& a5,const T& a6,const T& a7,const T& a8, + const T& a9,const T& a10,const T& a11,const T& a12, + const T& a13,const T& a14,const T& a15,const T& a16) { + return CImg(4,4).fill(a1,a2,a3,a4, + a5,a6,a7,a8, + a9,a10,a11,a12, + a13,a14,a15,a16); + } + + //! Return a 5x5 square matrix with specified coefficients + static CImg matrix(const T& a1,const T& a2,const T& a3,const T& a4,const T& a5, + const T& a6,const T& a7,const T& a8,const T& a9,const T& a10, + const T& a11,const T& a12,const T& a13,const T& a14,const T& a15, + const T& a16,const T& a17,const T& a18,const T& a19,const T& a20, + const T& a21,const T& a22,const T& a23,const T& a24,const T& a25) { + return CImg(5,5).fill(a1,a2,a3,a4,a5, + a6,a7,a8,a9,a10, + a11,a12,a13,a14,a15, + a16,a17,a18,a19,a20, + a21,a22,a23,a24,a25); + } + + //! In-place version of get_matrix(). + CImg& matrix() { + const unsigned int siz = size(); + switch (siz) { + case 1: break; + case 4: width = height = 2; break; + case 9: width = height = 3; break; + case 16: width = height = 4; break; + case 25: width = height = 5; break; + case 36: width = height = 6; break; + case 49: width = height = 7; break; + case 64: width = height = 8; break; + case 81: width = height = 9; break; + case 100: width = height = 10; break; + default: { + unsigned int i=11, i2=i*i; + while (i2::matrix() : Image size = %u is not a square number",pixel_type(),siz); + } break; + } + return *this; + } + + //! Realign pixel values of the instance image as a square matrix + CImg get_matrix() const { + return (+*this).matrix(); + } + + //! Return a 1x1 symmetric matrix with specified coefficients + static CImg tensor(const T& a1) { + return matrix(a1); + } + + //! Return a 2x2 symmetric matrix tensor with specified coefficients + static CImg tensor(const T& a1,const T& a2,const T& a3) { + return matrix(a1,a2, + a2,a3); + } + + //! Return a 3x3 symmetric matrix with specified coefficients + static CImg tensor(const T& a1,const T& a2,const T& a3,const T& a4,const T& a5,const T& a6) { + return matrix(a1,a2,a3, + a2,a4,a5, + a3,a5,a6); + } + + CImg get_tensor() const { + CImg res; + const unsigned int siz = size(); + switch (siz) { + case 1: break; + case 3: + res.assign(2,2); + res(0,0) = (*this)(0); + res(1,0) = res(0,1) = (*this)(1); + res(1,1) = (*this)(2); + break; + case 6: + res.assign(3,3); + res(0,0) = (*this)(0); + res(1,0) = res(0,1) = (*this)(1); + res(2,0) = res(0,2) = (*this)(2); + res(1,1) = (*this)(3); + res(2,1) = res(1,2) = (*this)(4); + res(2,2) = (*this)(5); + break; + default: + throw CImgInstanceException("CImg<%s>::get_tensor() : Wrong vector dimension = %u in instance image.", + pixel_type(), dim); + break; + } + return res; + } + + //! In-place version of get_tensor(). + CImg& tensor() { + return get_tensor().swap(*this); + } + + //! Return a 1x1 diagonal matrix with specified coefficients + static CImg diagonal(const T& a1) { + return matrix(a1); + } + + //! Return a 2x2 diagonal matrix with specified coefficients + static CImg diagonal(const T& a1,const T& a2) { + return matrix(a1,0, + 0,a2); + } + + //! Return a 3x3 diagonal matrix with specified coefficients + static CImg diagonal(const T& a1,const T& a2,const T& a3) { + return matrix(a1,0,0, + 0,a2,0, + 0,0,a3); + } + + //! Return a 4x4 diagonal matrix with specified coefficients + static CImg diagonal(const T& a1,const T& a2,const T& a3,const T& a4) { + return matrix(a1,0,0,0, + 0,a2,0,0, + 0,0,a3,0, + 0,0,0,a4); + } + + //! Return a 5x5 diagonal matrix with specified coefficients + static CImg diagonal(const T& a1,const T& a2,const T& a3,const T& a4,const T& a5) { + return matrix(a1,0,0,0,0, + 0,a2,0,0,0, + 0,0,a3,0,0, + 0,0,0,a4,0, + 0,0,0,0,a5); + } + + //! Unroll all images values into specified axis. + CImg& unroll(const char axe='x') { + const unsigned int siz = size(); + if (siz) switch (axe) { + case 'x': width = siz; height=depth=dim=1; break; + case 'y': height = siz; width=depth=dim=1; break; + case 'z': depth = siz; width=height=dim=1; break; + case 'v': dim = siz; width=height=depth=1; break; + default: throw CImgArgumentException("CImg<%s>::unroll() : Given axe is '%c' which is not 'x','y','z' or 'v'", + pixel_type(),axe); + } + return *this; + } + + CImg get_unroll(const char axe='x') const { + return (+*this).unroll(axe); + } + + CImg& vector() { + return unroll('y'); + } + + CImg get_vector() const { + return get_unroll('y'); + } + + //! Get a diagonal matrix, whose diagonal coefficients are the coefficients of the input image + CImg get_diagonal() const { + if (is_empty()) return CImg(); + CImg res(size(),size(),1,1,0); + cimg_foroff(*this,off) res(off,off)=(*this)(off); + return res; + } + + //! Replace a vector by a diagonal matrix containing the original vector coefficients. + CImg& diagonal() { + return get_diagonal().swap(*this); + } + + //! Return a NxN identity matrix + static CImg identity_matrix(const unsigned int N) { + CImg res(N,N,1,1,0); + cimg_forX(res,x) res(x,x)=1; + return res; + } + + CImg& identity_matrix() { + return get_identity_matrix(cimg::max(width,height)).swap(*this); + } + + CImg get_identity_matrix() const { + return identity_matrix(cimg::max(width,height)); + } + + //! Return a N-numbered sequence vector from \p a0 to \p a1 + CImg& sequence(const T& a0, const T& a1) { + if (!is_empty()) { + const unsigned int siz = size()-1; + const float delta = (float)((float)a1-a0); + T* ptr = data; + cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); + } + return *this; + } + + CImg get_sequence(const T& a0, const T& a1) const { + return (+*this).sequence(a0,a1); + } + + static CImg sequence(const unsigned int N, const T& a0, const T& a1) { + if (N) return CImg(1,N).sequence(a0,a1); + return CImg(); + } + + //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w. + static CImg rotation_matrix(const float x, const float y, const float z, const float w, const bool quaternion_data=false) { + float X,Y,Z,W; + if (!quaternion_data) { + const float norm = (float)std::sqrt(x*x + y*y + z*z), + nx = norm>0?x/norm:0, + ny = norm>0?y/norm:0, + nz = norm>0?z/norm:1, + nw = norm>0?w:0, + sina = (float)std::sin(nw/2), + cosa = (float)std::cos(nw/2); + X = nx*sina; + Y = ny*sina; + Z = nz*sina; + W = cosa; + } else { + const float norm = (float)std::sqrt(x*x + y*y + z*z + w*w); + if (norm>0) { X=x/norm; Y=y/norm; Z=z/norm; W=w/norm; } + else { X=Y=Z=0; W=1; } + } + const float xx=X*X, xy=X*Y, xz=X*Z, xw=X*W, yy=Y*Y, yz=Y*Z, yw=Y*W, zz=Z*Z, zw=Z*W; + return CImg::matrix(1-2*(yy+zz), 2*(xy+zw), 2*(xz-yw), + 2*(xy-zw), 1-2*(xx+zz), 2*(yz+xw), + 2*(xz+yw), 2*(yz-xw), 1-2*(xx+yy)); + } + + //! Return a new image corresponding to the vector located at (\p x,\p y,\p z) of the current vector-valued image. + CImg get_vector_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { + CImg dest(1,dim); + cimg_forV(*this,k) dest[k]=(*this)(x,y,z,k); + return dest; + } + + //! Return a new image corresponding to the \a square \a matrix located at (\p x,\p y,\p z) of the current vector-valued image. + CImg get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { + const int n = (int)std::sqrt((double)dim); + CImg dest(n,n); + cimg_forV(*this,k) dest[k]=(*this)(x,y,z,k); + return dest; + } + + //! Return a new image corresponding to the \a diffusion \a tensor located at (\p x,\p y,\p z) of the current vector-valued image. + CImg get_tensor_at(const unsigned int x=0,const unsigned int y=0,const unsigned int z=0) const { + if (dim==6) return tensor((*this)(x,y,z,0),(*this)(x,y,z,1),(*this)(x,y,z,2), + (*this)(x,y,z,3),(*this)(x,y,z,4),(*this)(x,y,z,5)); + if (dim==3) return tensor((*this)(x,y,z,0),(*this)(x,y,z,1),(*this)(x,y,z,2)); + return tensor((*this)(x,y,z,0)); + } + + //! Set the image \p vec as the \a vector \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. + CImg& set_vector_at(const CImg& vec,const unsigned int x=0,const unsigned int y=0,const unsigned int z=0) { + return draw_point(x,y,z,vec.data,1); + } + + //! Set the image \p vec as the \a square \a matrix-valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. + CImg& set_matrix_at(const CImg& mat,const unsigned int x=0,const unsigned int y=0,const unsigned int z=0) { + return set_vector_at(mat,x,y,z); + } + + //! Set the image \p vec as the \a tensor \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. + CImg& set_tensor_at(const CImg& ten,const unsigned int x=0,const unsigned int y=0,const unsigned int z=0) { + if (ten.height==2) { + (*this)(x,y,z,0)=ten[0]; + (*this)(x,y,z,1)=ten[1]; + (*this)(x,y,z,2)=ten[3]; + } + else { + (*this)(x,y,z,0)=ten[0]; + (*this)(x,y,z,1)=ten[1]; + (*this)(x,y,z,2)=ten[2]; + (*this)(x,y,z,3)=ten[4]; + (*this)(x,y,z,4)=ten[5]; + (*this)(x,y,z,5)=ten[8]; + } + return *this; + } + + //! Return the transpose version of the current matrix. + CImg get_transpose() const { + CImg res(height,width,depth,dim); + cimg_forXYZV(*this,x,y,z,v) res(y,x,z,v) = (*this)(x,y,z,v); + return res; + } + + //! Replace the current matrix by its transpose. + CImg& transpose() { + if (width==1) { width=height; height=1; return *this; } + if (height==1) { height=width; width=1; return *this; } + if (width==height) { + cimg_forYZV(*this,y,z,v) for (int x=y; x<(int)width; x++) cimg::swap((*this)(x,y,z,v),(*this)(y,x,z,v)); + return *this; + } + return (*this)=get_transpose(); + } + + //! Inverse the current matrix. + CImg& inverse(const bool use_LU=true) { + if (!is_empty()) { + if (width!=height || depth!=1 || dim!=1) + throw CImgInstanceException("CImg<%s>::inverse() : Instance matrix (%u,%u,%u,%u,%p) is not square.", + pixel_type(),width,height,depth,dim,data); + const double dete = width>3?-1.0:det(); + if (dete!=0.0 && width==2) { + const double + a = data[0], c = data[1], + b = data[2], d = data[3]; + data[0] = (T)(d/dete); data[1] = (T)(-c/dete); + data[2] = (T)(-b/dete), data[3] = (T)(a/dete); + } else if (dete!=0.0 && width==3) { + const double + a = data[0], d = data[1], g = data[2], + b = data[3], e = data[4], h = data[5], + c = data[6], f = data[7], i = data[8]; + data[0] = (T)((i*e-f*h)/dete), data[1] = (T)((g*f-i*d)/dete), data[2] = (T)((d*h-g*e)/dete); + data[3] = (T)((h*c-i*b)/dete), data[4] = (T)((i*a-c*g)/dete), data[5] = (T)((g*b-a*h)/dete); + data[6] = (T)((b*f-e*c)/dete), data[7] = (T)((d*c-a*f)/dete), data[8] = (T)((a*e-d*b)/dete); + } else { + if (use_LU) { // LU-based inverse computation + CImg A(*this), indx, col(1,width); + bool d; + A._LU(indx,d); + cimg_forX(*this,j) { + col.fill(0); col(j)=1; + col._solve(A,indx); + cimg_forX(*this,i) (*this)(j,i) = col(i); + } + } else { // SVD-based inverse computation + CImg U(width,width),S(1,width),V(width,width); + SVD(U,S,V,false); + U.transpose(); + cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k]; + S.diagonal(); + *this = V*S*U; + } + } + } + return *this; + } + + //! Return the inverse of the current matrix. + CImg::type> get_inverse(const bool use_LU=true) const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).inverse(use_LU); + } + + //! Return the pseudo-inverse (Moore-Penrose) of the matrix + CImg::type> get_pseudoinverse() const { + typedef typename cimg::largest::type restype; + CImg At = get_transpose(), At2(At); + return (((At*=*this).inverse())*=At2); + } + + //! Replace the matrix by its pseudo-inverse + CImg& pseudoinverse() { + typedef typename cimg::largest::type restype; + CImg At = get_transpose(), At2(At); + ((At*=*this).inverse())*=At2; + return ((*this)=At); + } + + //! Return the trace of the current matrix. + double trace() const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::trace() : Instance matrix (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + double res=0; + cimg_forX(*this,k) res+=(*this)(k,k); + return res; + } + + //! Return the kth smallest element of the image + // (Adapted from the numerical recipies for CImg) + const T kth_smallest(const unsigned int k) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::kth_smallest() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + CImg arr(*this); + unsigned long l=0,ir=size()-1; + for (;;) { + if (ir<=l+1) { + if (ir==l+1 && arr[ir]>1; + cimg::swap(arr[mid],arr[l+1]); + if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]); + if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]); + if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]); + unsigned long i = l+1, j = ir; + const T pivot = arr[l+1]; + for (;;) { + do i++; while (arr[i]pivot); + if (j=k) ir=j-1; + if (j<=k) l=i; + } + } + return 0; + } + + //! Return the median of the image + const T median() const { + const unsigned int s = size(); + const T res = kth_smallest(s>>1); + return (s%2)?res:((res+kth_smallest((s>>1)-1))/2); + } + + //! Return the dot product of the current vector/matrix with the vector/matrix \p img. + double dot(const CImg& img) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::dot() : Instance object (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + if (img.is_empty()) + throw CImgArgumentException("CImg<%s>::trace() : Specified argument (%u,%u,%u,%u,%p) is empty.", + pixel_type(),img.width,img.height,img.depth,img.dim,img.data); + const unsigned long nb = cimg::min(size(),img.size()); + double res=0; + for (unsigned long off=0; off::cross() : Arguments (%u,%u,%u,%u,%p) and (%u,%u,%u,%u,%p) must be both 3d vectors.", + pixel_type(),width,height,depth,dim,data,img.width,img.height,img.depth,img.dim,img.data); + const T x = (*this)[0], y = (*this)[1], z = (*this)[2]; + (*this)[0] = y*img[2]-z*img[1]; + (*this)[1] = z*img[0]-x*img[2]; + (*this)[2] = x*img[1]-y*img[0]; + return *this; + } + + //! Return the cross product between two 3d vectors + CImg get_cross(const CImg& img) const { + return (+*this).cross(img); + } + + //! Return the determinant of the current matrix. + double det() const { + if (is_empty() || width!=height || depth!=1 || dim!=1) + throw CImgInstanceException("CImg<%s>::det() : Instance matrix (%u,%u,%u,%u,%p) is not square or is empty.", + pixel_type(),width,height,depth,dim,data); + switch (width) { + case 1: return (*this)(0,0); + case 2: return (*this)(0,0)*(*this)(1,1)-(*this)(0,1)*(*this)(1,0); + case 3: { + const double + a = data[0], d = data[1], g = data[2], + b = data[3], e = data[4], h = data[5], + c = data[6], f = data[7], i = data[8]; + return i*a*e-a*h*f-i*b*d+b*g*f+c*d*h-c*g*e; + } + default: { + typedef typename cimg::largest::type ftype; + CImg lu(*this); + CImg indx; + bool d; + lu._LU(indx,d); + double res = d?1.0:-1.0; + cimg_forX(lu,i) res*=lu(i,i); + return res; + } + } + return 0; + } + + //! Return the norm of the current vector/matrix. \p ntype = norm type (0=L2, 1=L1, -1=Linf). + double norm(const int ntype=2) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::norm() : Instance object (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + double res = 0; + switch (ntype) { + case -1: { + cimg_foroff(*this,off) { + const double tmp = cimg::abs((double)data[off]); + if (tmp>res) res = tmp; + } + return res; + } break; + case 1 : { + cimg_foroff(*this,off) res+=cimg::abs((double)data[off]); + return res; + } break; + default: { return std::sqrt(dot(*this)); } + } + return 0; + } + + //! Return the sum of all the pixel values in an image. + double sum() const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::sum() : Instance object (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + double res=0; + cimg_for(*this,ptr,T) res+=*ptr; + return res; + } + + //! Compute the SVD of a general matrix. + template const CImg& SVD(CImg& U, CImg& S, CImg& V, + const bool sorting=true, const unsigned int max_iter=40) const { + if (is_empty()) { U.assign(); S.assign(); V.assign(); } + else { + U = *this; + if (S.size() rv1(width); + t anorm=0,c,f,g=0,h,s,scale=0; + int l=0, nm=0; + + cimg_forX(U,i) { + l = i+1; rv1[i] = scale*g; g = s = scale = 0; + if (i=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i)=f-g; + for (int j=l; j=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g; + { for (int k=l; k=0; i--) { + if (i=0; i--) { + l = i+1; g = S[i]; + for (int j=l; j=0; k--) { + for (unsigned int its=0; its=1; l--) { + nm = l-1; + if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; } + if ((cimg::abs(S[nm])+anorm)==anorm) break; + } + if (flag) { + c = 0; s = 1; + for (int i=l; i<=k; i++) { + f = s*rv1[i]; rv1[i] = c*rv1[i]; + if ((cimg::abs(f)+anorm)==anorm) break; + g = S[i]; h = (t)cimg::pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h; + cimg_forY(U,j) { const t y = U(nm,j), z = U(i,j); U(nm,j) = y*c+z*s; U(i,j) = z*c-y*s; } + } + } + const t& z = S[k]; + if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; } + nm = k-1; + t x = S[l], y = S[nm]; + g = rv1[nm]; h = rv1[k]; + f = ((y-z)*(y+z)+(g-h)*(g+h))/(2*h*y); + g = (t)cimg::pythagore(f,1.0); + f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x; + c = s = 1; + for (int j=l; j<=nm; j++) { + const int i = j+1; + g = rv1[i]; h = s*g; g = c*g; + t y = S[i]; + t z = (t)cimg::pythagore(f,h); + rv1[j] = z; c = f/z; s = h/z; + f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c; + cimg_forX(U,jj) { const t x = V(j,jj), z = V(i,jj); V(j,jj) = x*c+z*s; V(i,jj) = z*c-x*s; } + z = (t)cimg::pythagore(f,h); S[j] = z; + if (z) { z = 1/z; c = f*z; s = h*z; } + f = c*g+s*y; x = c*y-s*g; + { cimg_forY(U,jj) { const t y = U(j,jj); z = U(i,jj); U(j,jj) = y*c+z*s; U(i,jj) = z*c-y*s; }} + } + rv1[l] = 0; rv1[k]=f; S[k]=x; + } + } + + if (sorting) { + CImg permutations(width); + CImg tmp(width); + S.sort(permutations,false); + cimg_forY(U,k) { + cimg_forX(permutations,x) tmp(x) = U(permutations(x),k); + std::memcpy(U.ptr(0,k),tmp.data,sizeof(t)*width); + } + { cimg_forY(V,k) { + cimg_forX(permutations,x) tmp(x) = V(permutations(x),k); + std::memcpy(V.ptr(0,k),tmp.data,sizeof(t)*width); + }} + } + } + return *this; + } + + //! Compute the SVD of a general matrix. + template const CImg& SVD(CImgList& USV) const { + if (USV.size<3) USV.assign(3); + return SVD(USV[0],USV[1],USV[2]); + } + + //! Compute the SVD of a general matrix. + CImgList::type> get_SVD(const bool sorting=true) const { + typedef typename cimg::largest::type restype; + CImgList res(3); + SVD(res[0],res[1],res[2],sorting); + return res; + } + + // INNER ROUTINE : Compute the LU decomposition of a permuted matrix (c.f. numerical recipies) + template CImg& _LU(CImg& indx, bool& d) { + typedef typename cimg::largest::type ftype; + const int N = dimx(); + int imax=0; + CImg vv(N); + indx.assign(N); + d=true; + cimg_forX(*this,i) { + ftype vmax=0.0; + cimg_forX(*this,j) { + const ftype tmp = cimg::abs((*this)(j,i)); + if (tmp>vmax) vmax = tmp; + } + if (vmax==0) return fill(0); + vv[i] = 1/vmax; + } + cimg_forX(*this,j) { + for (int i=0; i=vmax) { vmax=tmp; imax=i; } + }} + if (j!=imax) { + cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j)); + d =!d; + vv[imax] = vv[j]; + } + indx[j] = (t)imax; + if ((*this)(j,j)==0) (*this)(j,j)=(T)1e-20; + if (j CImg& _solve(const CImg& A, const CImg& indx) { + typedef typename cimg::largest::type ftype; + const int N = size(); + int ii=-1; + ftype sum; + for (int i=0; i=0) for (int j=ii; j<=i-1; j++) sum-=A(j,i)*(*this)(j); + else if (sum!=0) ii=i; + (*this)(i)=sum; + } + { for (int i=N-1; i>=0; i--) { + sum = (*this)(i); + for (int j=i+1; j::solve() : Instance matrix size is (%u,%u,%u,%u) while " + "size of given matrix A is (%u,%u,%u,%u).", + pixel_type(),width,height,depth,dim,A.width,A.height,A.depth,A.dim); + if (A.width==A.height) { + CImg lu(A); + CImg indx; + bool d; + lu._LU(indx,d); + _solve(lu,indx); + } else assign(A.get_pseudoinverse()*(*this)); + return *this; + } + + //! Solve a linear system AX=B where B=*this. + CImg::type> get_solve(const CImg& A) const { + typedef typename cimg::largest::type restype; + return CImg(*this,false).solve(A); + } + + //! Compute the eigenvalues and eigenvectors of a matrix. + template const CImg& eigen(CImg& val, CImg &vec) const { + if (is_empty()) { val.assign(); vec.assign(); } + else { + if (width!=height || depth>1 || dim>1) + throw CImgInstanceException("CImg<%s>::eigen() : Instance object (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + if (val.size()::eigen() : Complex eigenvalues",pixel_type()); + f = std::sqrt(f); + const double l1 = 0.5*(e-f), l2 = 0.5*(e+f); + const double theta1 = std::atan2(l2-a,b), theta2 = std::atan2(l1-a,b); + val[0]=(t)l2; + val[1]=(t)l1; + vec(0,0) = (t)std::cos(theta1); + vec(0,1) = (t)std::sin(theta1); + vec(1,0) = (t)std::cos(theta2); + vec(1,1) = (t)std::sin(theta2); + } break; + default: + throw CImgInstanceException("CImg<%s>::eigen() : Eigenvalues computation of general matrices is limited" + "to 2x2 matrices (given is %ux%u)", pixel_type(),width,height); + } + } + return *this; + } + + //! Return the eigenvalues and eigenvectors of a matrix. + CImgList::type> get_eigen() const { + typedef typename cimg::largest::type restype; + CImgList res(2); + eigen(res[0],res[1]); + return res; + } + + //! Compute the eigenvalues and eigenvectors of a matrix. + template const CImg& eigen(CImgList& eig) const { + if (eig.size<2) eig.assign(2); + eigen(eig[0],eig[1]); + return *this; + } + + //! Compute the eigenvalues and eigenvectors of a symmetric matrix. + template const CImg& symmetric_eigen(CImg& val, CImg& vec) const { + if (is_empty()) { val.assign(); vec.assign(); } + else { + if (width!=height || depth>1 || dim>1) + throw CImgInstanceException("CImg<%s>::eigen() : Instance object (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + + if (val.size() V(width,width); + SVD(vec,val,V,false); + cimg_forX(vec,x) { // check for negative eigenvalues + t scal=0; + cimg_forY(vec,y) scal+=vec(x,y)*V(x,y); + if (scal<0) val[x]=-val[x]; + } + CImg permutations(width); // sort eigenvalues in decreasing order + CImg tmp(width); + val.sort(permutations,false); + cimg_forY(vec,k) { + cimg_forX(permutations,x) tmp(x) = vec(permutations(x),k); + std::memcpy(vec.ptr(0,k),tmp.data,sizeof(t)*width); + } + } + return *this; + } + + //! Compute the eigenvalues and eigenvectors of a symmetric matrix. + CImgList::type> get_symmetric_eigen() const { + typedef typename cimg::largest::type restype; + CImgList res(2); + symmetric_eigen(res[0],res[1]); + return res; + } + + //! Compute the eigenvalues and eigenvectors of a symmetric matrix. + template const CImg& symmetric_eigen(CImgList& eig) const { + if (eig.size<2) eig.assign(2); + symmetric_eigen(eig[0],eig[1]); + return *this; + } + + template CImg& _quicksort(const int min,const int max,CImg& permutations,const bool increasing) { + if (min(*this)[mid]) { + cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } + if ((*this)[mid]>(*this)[max]) { + cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); } + if ((*this)[min]>(*this)[mid]) { + cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } + } else { + if ((*this)[min]<(*this)[mid]) { + cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } + if ((*this)[mid]<(*this)[max]) { + cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); } + if ((*this)[min]<(*this)[mid]) { + cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } + } + if (max-min>=3) { + const T pivot = (*this)[mid]; + int i = min, j = max; + if (increasing) { + do { + while ((*this)[i]pivot) j--; + if (i<=j) { + cimg::swap((*this)[i],(*this)[j]); + cimg::swap(permutations[i++],permutations[j--]); + } + } while (i<=j); + } else { + do { + while ((*this)[i]>pivot) i++; + while ((*this)[j] + CImg& sort(CImg& permutations,const bool increasing=true) { + if (is_empty()) permutations.assign(); + else { + if (permutations.size()!=size()) permutations.assign(size()); + cimg_foroff(permutations,off) permutations[off] = (t)off; + _quicksort(0,size()-1,permutations,increasing); + } + return *this; + } + + //! Sort values of a vector. + CImg& sort(const bool increasing=true) { CImg foo; return sort(foo,increasing); } + + //! Get a sorted version a of vector, with permutations. + template CImg get_sort(CImg& permutations,const bool increasing=true) { + return (+*this).sort(permutations,increasing); + } + + //! Get a sorted version of a vector. + CImg get_sort(const bool increasing=true) { + return (+*this).sort(increasing); + } + + //! Get a permutation of the pixels + template CImg get_permute(const CImg& permutation) const { + if (permutation.size()!=size()) + throw CImgArgumentException("CImg<%s>::get_permute() : Instance image (%u,%u,%u,%u,%p) and permutation (%u,%u,%u,%u,%p)" + "have different sizes.",pixel_type(), + width,height,depth,dim,data, + permutation.width,permutation.height,permutation.depth,permutation.dim,permutation.data); + CImg res(width,height,depth,dim); + const t *p = permutation.ptr(permutation.size()); + cimg_for(res,ptr,T) *ptr = (*this)[*(--p)]; + return res; + } + + //! In-place version of the previous function + template CImg& permute(const CImg& permutation) { + return get_permute(permutation).swap(*this); + } + + //@} + //------------------- + // + //! \name Display + //@{ + //------------------- + + //! Display an image into a CImgDisplay window. + const CImg& display(CImgDisplay& disp) const { + disp.display(*this); + return *this; + } + + //! Display an image in a window with a title \p title, and wait a 'is_closed' or 'keyboard' event.\n + //! Parameters \p min_size and \p max_size set the minimum and maximum dimensions of the display window. + //! If negative, they corresponds to a percentage of the original image size. + const CImg& display(const char* title, const int min_size=128, const int max_size=1024) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::display() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + CImgDisplay *disp; + unsigned int w = width+(depth>1?depth:0), h = height+(depth>1?depth:0), XYZ[3]; + print(title); + const unsigned int dmin = cimg::min(w,h), minsiz = min_size>=0?min_size:(-min_size)*dmin/100; + if (dmin=0?max_size:(-max_size)*dmax/100; + if (dmax>maxsiz) { w=w*maxsiz/dmax; w+=(w==0); h=h*maxsiz/dmax; h+=(h==0); } + disp = new CImgDisplay(w,h,title,3,3); + XYZ[0] = width/2; XYZ[1] = height/2; XYZ[2] = depth/2; + while (!disp->is_closed && !disp->key) feature_selection(0,1,*disp,XYZ); + delete disp; + return *this; + } + + //! Display an image in a window, with a default title. See also \see display() for details on parameters. + const CImg& display(const int min_size=128, const int max_size=1024) const { + char title[256]={0}; + std::sprintf(title,"CImg<%s>",pixel_type()); + return display(title,min_size,max_size); + } + + //! High-level interface to select features from images + const CImg& feature_selection(int* const selection, const int feature_type, CImgDisplay &disp, + unsigned int *const XYZ=0,const unsigned char *const color=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::feature_selection() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + + const unsigned int + old_events = disp.events, + old_normalization = disp.normalization, + hatch = 0x55555555; + + bool old_is_resized = disp.is_resized; + disp.events = 3; + disp.normalization = 0; + disp.show().key = 0; + + unsigned char fgcolor[3] = { 255,255,105 }, bgcolor[3] = { 0,0,0 }; + if (color) std::memcpy(fgcolor,color,sizeof(unsigned char)*cimg::min(3,dimv())); + + int area = 0, clicked_area = 0, phase = 0, + X0 = (int)((XYZ?XYZ[0]:width/2)%width), Y0 = (int)((XYZ?XYZ[1]:height/2)%height), Z0 = (int)((XYZ?XYZ[2]:depth/2)%depth), + X1 =-1, Y1 = -1, Z1 = -1, + X = -1, Y = -1, Z = -1, + oX = X, oY = Y, oZ = Z; + unsigned int old_button = 0, key = 0; + + bool feature_selected = false, text_down = false; + CImg visu, visu0; + char text[1024] = { 0 }; + + while (!key && !disp.is_closed && !feature_selected) { + + // Handle mouse motion and selection + oX = X; oY = Y; oZ = Z; + int mx = disp.mouse_x, my = disp.mouse_y; + const int mX = mx*(width+(depth>1?depth:0))/disp.width, mY = my*(height+(depth>1?depth:0))/disp.height; + + area = 0; + if (mX=dimy()) { area = 2; X = mX; Z = mY-height; Y = phase?Y1:Y0; } + if (mX>=dimx() && mY=2) { + switch (clicked_area) { + case 1: Z1 = Z; break; + case 2: Y1 = Y; break; + case 3: X1 = X; break; + } + } + if (disp.button&2) { if (phase) { X1 = X; Y1 = Y; Z1 = Z; } else { X0 = X; Y0 = Y; Z0 = Z; } } + if (disp.button&4) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu.assign(); } + if (disp.wheel) { + switch (area) { + case 1: if (phase) Z = (Z1+=disp.wheel); else Z = (Z0+=disp.wheel); break; + case 2: if (phase) Y = (Y1+=disp.wheel); else Y = (Y0+=disp.wheel); break; + case 3: if (phase) X = (X1+=disp.wheel); else X = (X0+=disp.wheel); break; + default: break; + } + disp.wheel = 0; + } + if ((disp.button&1)!=old_button) { + switch (phase++) { + case 0: X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; clicked_area = area; break; + case 1: X1 = X; Y1 = Y; Z1 = Z; break; + default: break; + } + old_button = disp.button&1; + } + if (depth>1 && (X!=oX || Y!=oY || Z!=oZ)) visu0.assign(); + } + + if (phase) { + if (!feature_type) feature_selected = phase?true:false; + else { + if (depth>1) feature_selected = (phase==3)?true:false; + else feature_selected = (phase==2)?true:false; + } + } + + if (X0<0) X0 = 0; if (X0>=(int)width) X0 = (int)width-1; if (Y0<0) Y0 = 0; if (Y0>=(int)height) Y0 = (int)height-1; + if (Z0<0) Z0 = 0; if (Z0>=(int)depth) Z0 = (int)depth-1; + if (X1<1) X1 = 0; if (X1>=(int)width) X1 = (int)width-1; if (Y1<0) Y1 = 0; if (Y1>=(int)height) Y1 = (int)height-1; + if (Z1<0) Z1 = 0; if (Z1>=(int)depth) Z1 = (int)depth-1; + + // Draw visualization image on the display + if (oX!=X || oY!=Y || oZ!=Z || visu0.is_empty()) { + if (visu0.is_empty()) { + CImg tmp; + if (depth==1) tmp = get_resize(disp.width,disp.height,1,cimg::min(3,dimv())); + else tmp = (!phase?get_projections2d(X0,Y0,Z0):get_projections2d(X1,Y1,Z1)).get_resize(disp.width,disp.height,1,cimg::min(3,dimv())); + if (old_normalization) { + if (old_normalization<3 || cimg::type::is_float()) { + if (sizeof(T)>1) visu0.assign(tmp.normalize(0,255)); + else visu0.assign(tmp).normalize(0,255); + } else { + if (cimg::type::id()!=cimg::type::id()) { + const float m = cimg::type::min(), M = cimg::type::max(); + visu0.assign((CImg(tmp)-=m)*=255.0f/(M-m)); + } else visu0.assign(tmp); + } + } else visu0.assign(tmp); + } + visu = visu0; + + const int d=(depth>1)?depth:0; + if (phase) switch (feature_type) { + case 1: { + const int + x0=(int)((X0+0.5f)*disp.width/(width+d)), y0=(int)((Y0+0.5f)*disp.height/(height+d)), + x1=(int)((X1+0.5f)*disp.width/(width+d)), y1=(int)((Y1+0.5f)*disp.height/(height+d)); + visu.draw_arrow(x0,y0,x1,y1,fgcolor,30.0f,5.0f,hatch); + if (d) { + const int zx0=(int)((width+Z0+0.5f)*disp.width/(width+d)), zx1=(int)((width+Z1+0.5f)*disp.width/(width+d)), + zy0=(int)((height+Z0+0.5f)*disp.height/(height+d)), zy1=(int)((height+Z1+0.5f)*disp.height/(height+d)); + visu.draw_arrow(zx0,y0,zx1,y1,fgcolor,30.0f,5.0f,hatch).draw_arrow(x0,zy0,x1,zy1,fgcolor,30.0f,5.0f,hatch); + } + } break; + case 2: { + const int + x0=(X0=visu.dimy()-11) text_down = false; + if (!feature_type || !phase) { + if (X>=0 && Y>=0 && Z>=0 && X<(int)width && Y<(int)height && Z<(int)depth) { + if (depth>1) std::sprintf(text,"Coords (%d,%d,%d)={ ",X,Y,Z); else std::sprintf(text,"Coords (%d,%d)={ ",X,Y); + char *ctext = text + cimg::strlen(text), *const ltext = text+512; + for (unsigned int k=0; k1) std::sprintf(text,"Vect (%d,%d,%d)-(%d,%d,%d), norm=%g",X0,Y0,Z0,X1,Y1,Z1,norm); + else std::sprintf(text,"Vect (%d,%d)-(%d,%d), norm=%g",X0,Y0,X1,Y1,norm); + } break; + case 2: + if (depth>1) std::sprintf(text,"Box (%d,%d,%d)-(%d,%d,%d), Size=(%d,%d,%d)", + X01) std::sprintf(text,"Ellipse (%d,%d,%d)-(%d,%d,%d), Radii=(%d,%d,%d)", + X0,Y0,Z0,X1,Y1,Z1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1)); + else std::sprintf(text,"Ellipse (%d,%d)-(%d,%d), Radii=(%d,%d)", + X0,Y0,X1,Y1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1)); + + break; + } + if (phase || (mx>=0 && my>=0)) visu.draw_text(text,0,text_down?visu.dimy()-11:0,fgcolor,bgcolor,11,0.7f); + disp.display(visu).wait(25); + } else disp.wait(); + + if (disp.is_resized) { disp.resize(false); old_is_resized = true; disp.is_resized = false; visu0.assign(); } + } + + // Return result + if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; } + if (feature_selected) { + if (feature_type==2) { + if (X0>X1) cimg::swap(X0,X1); + if (Y0>Y1) cimg::swap(Y0,Y1); + if (Z0>Z1) cimg::swap(Z0,Z1); + } + if (selection) { + if (X1<0 || Y1<0 || Z1<0) X0=Y0=Z0=X1=Y1=Z1=-1; + switch(feature_type) { + case 1: + case 2: selection[3] = X1; selection[4] = Y1; selection[5] = Z1; + default: selection[0] = X0; selection[1] = Y0; selection[2] = Z0; + } + } + } else if (selection) selection[0]=selection[1]=selection[2]=selection[3]=selection[4]=selection[5]=-1; + disp.button = 0; + disp.events = old_events; + disp.normalization = old_normalization; + disp.is_resized = old_is_resized; + disp.key = key; + return *this; + } + + //! High-level interface to select features in images + const CImg& feature_selection(int *const selection, const int feature_type, + unsigned int *const XYZ=0,const unsigned char *const color=0) const { + unsigned int w = width + (depth>1?depth:0), h = height + (depth>1?depth:0); + const unsigned int dmin = cimg::min(w,h), minsiz = 256; + if (dminmaxsiz) { w=w*maxsiz/dmax; h=h*maxsiz/dmax; } + CImgDisplay disp(w,h," ",1,3); + return feature_selection(selection,feature_type,disp,XYZ,color); + } + + //! High-level interface for displaying a 3d object + template + const CImg& display_object3d(const CImg& points,const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, CImgDisplay& disp, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, + const float focale=500.0f, const float ambiant_light=0.05f, + const bool display_axes=true, float *const pose_matrix=0) const { + + // Check input arguments + if (points.is_empty() || primitives.is_empty() || opacities.is_empty()) + throw CImgArgumentException("CImg<%s>::display_object3d() : Given points (%u), primitives (%u) or opacities (%u) are empty.", + pixel_type(),points.size()/3,primitives.size,opacities.size); + if (is_empty()) + return CImg(disp.width,disp.height,1,colors[0].size(),0). + display_object3d(points,primitives,colors,opacities,disp,centering, + render_static,render_motion,double_sided,focale,ambiant_light); + if (points.height<3) + return display_object3d(points.get_resize(-100,3,1,1,0),primitives,colors,opacities,disp, + centering,render_static,render_motion,double_sided,focale,ambiant_light); + + // Init 3D objects and compute object statistics + CImg pose, rot_mat, + centered_points = centering?CImg(points.width,3):CImg(), + rotated_points(points.width,3), + bbox_points, rotated_bbox_points, + axes_points, rotated_axes_points; + CImgList bbox_opacities, axes_opacities; + CImgList bbox_colors, axes_colors; + CImgList bbox_primitives, axes_primitives; + float dx=0, dy=0, dz=0, ratio=1; + const T valmax = cimg::type::max(); + + const CImgStats + sx(points.get_shared_line(0),false), + sy(points.get_shared_line(1),false), + sz(points.get_shared_line(2),false); + const float + xm = (float)sx.min, xM = (float)sx.max, + ym = (float)sy.min, yM = (float)sy.max, + zm = (float)sz.min, zM = (float)sz.max, + delta = cimg::max(xM-xm,yM-ym,zM-zm); + + if (display_axes) { + axes_points.assign(7,3); + rotated_axes_points.assign(7,3); + axes_opacities.assign(3,1,1,1,1,1.0f); + axes_colors.assign(3,dim,1,1,1,valmax); + axes_points(0,0) = 0; axes_points(0,1) = 0; axes_points(0,2) = 0; + axes_points(1,0) = 20; axes_points(1,1) = 0; axes_points(1,2) = 0; + axes_points(2,0) = 0; axes_points(2,1) = 20; axes_points(2,2) = 0; + axes_points(3,0) = 0; axes_points(3,1) = 0; axes_points(3,2) = 20; + axes_points(4,0) = 22; axes_points(4,1) = -6; axes_points(4,2) = 0; + axes_points(5,0) = -6; axes_points(5,1) = 22; axes_points(5,2) = 0; + axes_points(6,0) = -6; axes_points(6,1) = -6; axes_points(6,2) = 22; + axes_primitives.insert(CImg::vector(0,1)); + axes_primitives.insert(CImg::vector(0,2)); + axes_primitives.insert(CImg::vector(0,3)); + } + + // Begin user interaction loop + CImg visu0(*this), visu; + bool init = true, clicked = false, redraw = true; + unsigned int key = 0; + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + const unsigned int old_events = disp.events; + disp.show().button = disp.key = 0; + disp.events = 3; + + while (!disp.is_closed && !key) { + + // Init object position and scale if necessary + if (init) { + ratio = delta>0?(2.0f*cimg::min(disp.width,disp.height)/(3.0f*delta)):0; + dx = 0.5f*(xM+xm); dy = 0.5f*(yM+ym); dz = 0.5f*(zM+zm); + if (centering) { + cimg_forX(centered_points,l) { + centered_points(l,0) = (float)((points(l,0)-dx)*ratio); + centered_points(l,1) = (float)((points(l,1)-dy)*ratio); + centered_points(l,2) = (float)((points(l,2)-dz)*ratio); + } + } + + if (render_static<0 || render_motion<0) { + bbox_colors.assign(12,dim,1,1,1,valmax); + bbox_primitives.assign(12,1,2); + bbox_points.assign(8,3); + rotated_bbox_points.assign(8,3); + bbox_points(0,0) = xm; bbox_points(0,1) = ym; bbox_points(0,2) = zm; + bbox_points(1,0) = xM; bbox_points(1,1) = ym; bbox_points(1,2) = zm; + bbox_points(2,0) = xM; bbox_points(2,1) = yM; bbox_points(2,2) = zm; + bbox_points(3,0) = xm; bbox_points(3,1) = yM; bbox_points(3,2) = zm; + bbox_points(4,0) = xm; bbox_points(4,1) = ym; bbox_points(4,2) = zM; + bbox_points(5,0) = xM; bbox_points(5,1) = ym; bbox_points(5,2) = zM; + bbox_points(6,0) = xM; bbox_points(6,1) = yM; bbox_points(6,2) = zM; + bbox_points(7,0) = xm; bbox_points(7,1) = yM; bbox_points(7,2) = zM; + bbox_primitives[0].fill(0,1); bbox_primitives[1].fill(1,2); bbox_primitives[2].fill(2,3); bbox_primitives[3].fill(3,0); + bbox_primitives[4].fill(4,5); bbox_primitives[5].fill(5,6); bbox_primitives[6].fill(6,7); bbox_primitives[7].fill(7,4); + bbox_primitives[8].fill(0,4); bbox_primitives[9].fill(1,5); bbox_primitives[10].fill(2,6); bbox_primitives[11].fill(3,7); + bbox_opacities.assign(bbox_primitives.size,1,1,1,1,1.0f); + } + + if (pose_matrix) pose = CImg(pose_matrix,4,4,1,1,false); else pose = CImg::identity_matrix(4); + init = false; + redraw = true; + } + + // Rotate and Draw 3D object + if (redraw) { + const float + r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0), + r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1), + r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2); + if ((clicked && render_motion>=0) || (!clicked && render_static>=0)) { + if (centering) cimg_forX(centered_points,l) { + const float x = centered_points(l,0), y = centered_points(l,1), z = centered_points(l,2); + rotated_points(l,0) = r00*x + r10*y + r20*z + r30; + rotated_points(l,1) = r01*x + r11*y + r21*z + r31; + rotated_points(l,2) = r02*x + r12*y + r22*z + r32; + } else cimg_forX(points,l) { + const float x = (float)points(l,0), y = (float)points(l,1), z = (float)points(l,2); + rotated_points(l,0) = r00*x + r10*y + r20*z + r30; + rotated_points(l,1) = r01*x + r11*y + r21*z + r31; + rotated_points(l,2) = r02*x + r12*y + r22*z + r32; + } + } else { + if (!centering) cimg_forX(bbox_points,l) { + const float x = bbox_points(l,0), y = bbox_points(l,1), z = bbox_points(l,2); + rotated_bbox_points(l,0) = r00*x + r10*y + r20*z + r30; + rotated_bbox_points(l,1) = r01*x + r11*y + r21*z + r31; + rotated_bbox_points(l,2) = r02*x + r12*y + r22*z + r32; + } else cimg_forX(bbox_points,l) { + const float x = (bbox_points(l,0)-dx)*ratio, y = (bbox_points(l,1)-dy)*ratio, z = (bbox_points(l,2)-dz)*ratio; + rotated_bbox_points(l,0) = r00*x + r10*y + r20*z + r30; + rotated_bbox_points(l,1) = r01*x + r11*y + r21*z + r31; + rotated_bbox_points(l,2) = r02*x + r12*y + r22*z + r32; + } + } + + // Draw object + visu = visu0; + if ((clicked && render_motion<0) || (!clicked && render_static<0)) + visu.draw_object3d(visu.width/2.0f, visu.height/2.0f, 0, + rotated_bbox_points,bbox_primitives,bbox_colors,bbox_opacities,1, + false,focale,visu.dimx()/2.0f,visu.dimy()/2.0f,-5000.0f,0.2f); + else visu.draw_object3d(visu.width/2.0f, visu.height/2.0f, 0, + rotated_points,primitives,colors,opacities,clicked?render_motion:render_static, + double_sided,focale,visu.dimx()/2.0f,visu.dimy()/2.0f,-5000.0f,ambiant_light); + + // Draw axes + if (display_axes) { + const float Xaxes = 25.0f, Yaxes = visu.height-35.0f; + cimg_forX(axes_points,l) { + const float x = axes_points(l,0), y = axes_points(l,1), z = axes_points(l,2); + rotated_axes_points(l,0) = r00*x + r10*y + r20*z; + rotated_axes_points(l,1) = r01*x + r11*y + r21*z; + rotated_axes_points(l,2) = r02*x + r12*y + r22*z; + } + axes_opacities(0,0) = (rotated_axes_points(1,2)>0)?0.5f:1.0f; + axes_opacities(1,0) = (rotated_axes_points(2,2)>0)?0.5f:1.0f; + axes_opacities(2,0) = (rotated_axes_points(3,2)>0)?0.5f:1.0f; + visu.draw_object3d(Xaxes, Yaxes, 0, rotated_axes_points,axes_primitives,axes_colors,axes_opacities,1,false,focale,0,0,0,0). + draw_text("X",(int)(Xaxes+rotated_axes_points(4,0)), (int)(Yaxes+rotated_axes_points(4,1)), axes_colors[0].ptr(), 0, 11, axes_opacities(0,0)). + draw_text("Y",(int)(Xaxes+rotated_axes_points(5,0)), (int)(Yaxes+rotated_axes_points(5,1)), axes_colors[1].ptr(), 0, 11, axes_opacities(1,0)). + draw_text("Z",(int)(Xaxes+rotated_axes_points(6,0)), (int)(Yaxes+rotated_axes_points(6,1)), axes_colors[2].ptr(), 0, 11, axes_opacities(2,0)); + } + + visu.display(disp); + if (!clicked || render_motion==render_static) redraw = false; + } + + // Handle user interaction + if ((disp.button || disp.wheel) && disp.mouse_x>=0 && disp.mouse_y>=0) { + redraw = true; + if (!clicked) { x0 = x1 = disp.mouse_x; y0 = y1 = disp.mouse_y; if (!disp.wheel) clicked = true; } + else { x1 = disp.mouse_x; y1 = disp.mouse_y; } + if (disp.button&1) { + const float + R = 0.4f*cimg::min(disp.width,disp.height), + R2 = R*R, + u0 = (float)(x0-disp.dimx()/2), + v0 = (float)(y0-disp.dimy()/2), + u1 = (float)(x1-disp.dimx()/2), + v1 = (float)(y1-disp.dimy()/2), + n0 = (float)std::sqrt(u0*u0+v0*v0), + n1 = (float)std::sqrt(u1*u1+v1*v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)std::sqrt(cimg::max(0.0f,R2-nu0*nu0-nv0*nv0)), + nu1 = n1>R?(u1*R/n1):u1, + nv1 = n1>R?(v1*R/n1):v1, + nw1 = (float)std::sqrt(cimg::max(0.0f,R2-nu1*nu1-nv1*nv1)), + u = nv0*nw1-nw0*nv1, + v = nw0*nu1-nu0*nw1, + w = nv0*nu1-nu0*nv1, + n = (float)std::sqrt(u*u+v*v+w*w), + alpha = (float)std::asin(n/R2); + rot_mat = CImg::rotation_matrix(u,v,w,alpha); + rot_mat *= pose.get_crop(0,0,2,2); + pose.draw_image(rot_mat,0,0); + x0=x1; y0=y1; + } + if (disp.button&2) { pose(3,2)+=(y1-y0); x0=x1; y0=y1; } + if (disp.wheel) { pose(3,2)-=15*disp.wheel; disp.wheel=0; } + if (disp.button&4) { pose(3,0)+=(x1-x0); pose(3,1)+=(y1-y0); x0=x1; y0=y1; } + if ((disp.button&1) && (disp.button&2)) { init = true; disp.button = 0; x0 = x1; y0 = y1; pose = CImg::identity_matrix(4); } + } else if (clicked) { x0=x1; y0=y1; clicked = false; redraw = true; } + + key = disp.key; + if (key && key!=cimg::keyCTRLLEFT) { + if (disp.is_pressed(cimg::keyCTRLLEFT)) { + switch (key) { + case cimg::keyD: if (disp.is_fullscreen) disp.toggle_fullscreen(); disp.resize(-200,-200); disp.is_resized = true; break; + case cimg::keyC: if (disp.is_fullscreen) disp.toggle_fullscreen(); disp.resize(-50,-50); disp.is_resized = true; break; + case cimg::keyF: disp.resize(disp.screen_dimx(),disp.screen_dimy()).toggle_fullscreen().is_resized = true; break; + case cimg::keyS: { // Save snapshot + static unsigned int snap_number = 0; + char filename[32] = {0}; + std::FILE *file; + do { + std::sprintf(filename,"CImg_%.4u.bmp",snap_number++); + if ((file=std::fopen(filename,"r"))!=0) std::fclose(file); + } while (file); + visu.save(filename); + } break; + } + disp.key = key = 0; + } + } else key = 0; + if (disp.is_resized) { disp.resize(false); visu0 = get_resize(disp,1); redraw = true; } + } + if (pose_matrix) std::memcpy(pose_matrix,pose.data,16*sizeof(float)); + disp.events = old_events; + disp.button = 0; + return *this; + } + + //! High-level interface for displaying a 3d object + template + const CImg& display_object3d(const CImgList& points,const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, CImgDisplay &disp, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, + const float focale=500.0f, const float ambiant_light=0.05f, + const bool display_axes=true, float *const pose_matrix=0) const { + CImg npoints(points.size,3,1,1,0); + tp *ptrX = npoints.ptr(), *ptrY = npoints.ptr(0,1), *ptrZ = npoints.ptr(0,2); + cimg_forX(npoints,l) { + const CImg& point = points[l]; + const unsigned int siz = point.size(); + if (!siz) + throw CImgArgumentException("CImg<%s>::display_object3d() : Given points (size=%u) contains a null element at " + "position %u.",pixel_type(),points.size,l); + *(ptrZ++) = (siz>2)?point(2):0; + *(ptrY++) = (siz>1)?point(1):0; + *(ptrX++) = point(0); + } + return display_object3d(npoints,primitives,colors,opacities,disp,centering, + render_static,render_motion,double_sided,focale,ambiant_light,display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object + template + const CImg& display_object3d(const CImg& points, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, CImgDisplay& disp, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, + const float focale=500.0f, const float ambiant_light=0.05f, + const bool display_axes=true, float *const pose_matrix=0) const { + CImgList nopacities(opacities.size(),1); + cimglist_for(nopacities,l) nopacities(l,0) = opacities(l); + return display_object3d(points,primitives,colors,nopacities,disp,centering, + render_static,render_motion,double_sided,focale,ambiant_light,display_axes,pose_matrix); + + } + + //! High-level interface for displaying a 3d object + template + const CImg& display_object3d(const CImgList& points,const CImgList& primitives, + const CImgList& colors, const CImg& opacities, CImgDisplay& disp, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, + const float focale=500.0f, const float ambiant_light=0.05f, + const bool display_axes=true, float *const pose_matrix=0) const { + CImgList nopacities(opacities.size(),1); + cimglist_for(nopacities,l) nopacities(l,0) = opacities(l); + if (points.is_empty()) throw CImgArgumentException("CImg<%s>::display_object3d() : Given points are empty.", + pixel_type()); + CImg npoints(points.size,3,1,1,0); + tp *ptrX = npoints.ptr(), *ptrY = npoints.ptr(0,1), *ptrZ = npoints.ptr(0,2); + { cimg_forX(npoints,l) { + const CImg& point = points[l]; + const unsigned int siz = point.size(); + if (!siz) + throw CImgArgumentException("CImg<%s>::display_object3d() : Given points (size=%u) contains a null element at " + "position %u.",pixel_type(),points.size,l); + *(ptrZ++) = (siz>2)?point(2):0; + *(ptrY++) = (siz>1)?point(1):0; + *(ptrX++) = point(0); + } + } + return display_object3d(npoints,primitives,colors,nopacities,disp,centering, + render_static,render_motion,double_sided,focale,ambiant_light,display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object + template + const CImg& display_object3d(const tp& points, const CImgList& primitives, + const CImgList& colors, const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, + const float focale=500.0f, const float ambiant_light=0.05f, + const bool display_axes=true, float *const pose_matrix=0) const { + CImgDisplay disp(width,height," ",0); + return display_object3d(points,primitives,colors,opacities,disp,centering, + render_static,render_motion,double_sided,focale,ambiant_light,display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object + template + const CImg& display_object3d(const tp& points, const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, + const float focale=500.0f, const float ambiant_light=0.05f, + const float opacity=1.0f, const bool display_axes=true, float *const pose_matrix=0) const { + CImgDisplay disp(width,height," ",0); + return display_object3d(points,primitives,colors,CImg::vector(opacity), + disp,centering,render_static,render_motion,double_sided, + focale,ambiant_light,display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object + template + const CImg& display_object3d(const tp& points, const CImgList& primitives, + const CImgList& colors, CImgDisplay &disp, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, + const float focale=500.0f, const float ambiant_light=0.05f, + const float opacity=1.0f, const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(points,primitives,colors,CImg::vector(opacity), + disp,centering,render_static,render_motion,double_sided, + focale,ambiant_light,display_axes,pose_matrix); + } + + //@} + //---------------------- + // + //! \name Input-Output + //@{ + //---------------------- + + //! Load an image from a file. + /** + \param filename = name of the image file to load. + \return A CImg instance containing the pixel data defined in the image file. + \note The extension of \c filename defines the file format. If no filename + extension is provided, CImg::get_load() will try to load a CRAW file (CImg Raw file). + **/ + static CImg get_load(const char *const filename) { + const char *ext = cimg::filename_split(filename); + if (!cimg::strncasecmp(ext,"asc",3)) return get_load_ascii(filename); + if (!cimg::strncasecmp(ext,"dlm",3) || + !cimg::strncasecmp(ext,"txt",3)) return get_load_dlm(filename); + if (!cimg::strncasecmp(ext,"inr",3)) return get_load_inr(filename); + if (!cimg::strncasecmp(ext,"hdr",3)) return get_load_analyze(filename); + if (!cimg::strncasecmp(ext,"par",3) || + !cimg::strncasecmp(ext,"rec",3)) return get_load_parrec(filename); + if (!cimg::strncasecmp(ext,"pan",3)) return get_load_pandore(filename); + if (!cimg::strncasecmp(ext,"bmp",3)) return get_load_bmp(filename); + if (!cimg::strncasecmp(ext,"png",3)) return get_load_png(filename); + if (!cimg::strncasecmp(ext,"tif",3)) return get_load_tiff(filename); + if (!cimg::strncasecmp(ext,"jpg",3) || + !cimg::strncasecmp(ext,"jpeg",4)) return get_load_jpeg(filename); + if (!cimg::strncasecmp(ext,"ppm",3) || + !cimg::strncasecmp(ext,"pgm",3) || + !cimg::strncasecmp(ext,"pnm",3)) return get_load_pnm(filename); + if (!cimg::strncasecmp(ext,"cimg",4) || + ext[0]=='\0') return get_load_cimg(filename); + if (!cimg::strncasecmp(ext,"dcm",3) || + !cimg::strncasecmp(ext,"dicom",5)) return get_load_dicom(filename); + return get_load_other(filename); + } + + //! Load an image from a file + /** This is the in-place version of get_load(). **/ + CImg& load(const char *const filename) { + return get_load(filename).swap(*this); + } + + //! Load an image from an ASCII file. + static CImg get_load_ascii(std::FILE *const file, const char *const filename=0) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + char line[256] = {0}; + std::fscanf(nfile,"%255[^\n]",line); + unsigned int off, dx = 0, dy = 1, dz = 1, dv = 1; + int err = 1; + std::sscanf(line,"%u %u %u %u",&dx,&dy,&dz,&dv); + if (!dx || !dy || !dz || !dv) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_ascii() : File '%s' is not a valid .ASC file.\n" + "Specified image dimensions are (%u,%u,%u,%u).", + pixel_type(),filename?filename:"(FILE*)",dx,dy,dz,dv); + } + CImg dest(dx,dy,dz,dv); + double val; + T *ptr = dest.data; + for (off=0; off::get_load_ascii() : File '%s', only %u/%u values read.", + pixel_type(),filename?filename:"(FILE*)",off,dest.size()); + if (!file) cimg::fclose(nfile); + return dest; + } + + //! Load an image from an ASCII file. + static CImg get_load_ascii(const char *const filename) { + return get_load_ascii(0,filename); + } + + //! Load an image from an ASCII file (in-place version). + CImg& load_ascii(std::FILE *const file, const char *const filename=0) { + return get_load_ascii(file,filename).swap(*this); + } + + //! Load an image from an ASCII file (in-place version). + CImg& load_ascii(const char *const filename) { + return get_load_ascii(filename).swap(*this); + } + + //! Load an image from a DLM file + static CImg get_load_dlm(std::FILE *const file, const char *const filename=0) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + CImg dest(256,256); + char c, delimiter[256]={0}, tmp[256]; + unsigned int cdx=0,dx=0,dy=0; + int oerr=0, err; + double val; + while ((err = std::fscanf(nfile,"%lf%255[^0-9.eE+-]",&val,delimiter))!=EOF) { + oerr = err; + if (err>0) dest(cdx++,dy) = (T)val; + if (cdx>=dest.width) dest.resize(dest.width+256,1,1,1,0); + c=0; if (!std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') { + dx = cimg::max(cdx,dx); + dy++; + if (dy>=dest.height) dest.resize(dest.width,dest.height+256,1,1,0); + cdx=0; + } + } + if (cdx && oerr==1) { dx=cdx; dy++; } + if (!dx || !dy) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_dlm() : File '%s' is not a valid DLM file.\n" + "Specified image dimensions are (%u,%u).", + pixel_type(),filename?filename:"(FILE*)",dx,dy); + } + dest.resize(dx,dy,1,1,0); + if (!file) cimg::fclose(nfile); + return dest; + } + + //! Load an image from a DLM file + static CImg get_load_dlm(const char *const filename=0) { + return get_load_dlm(0,filename); + } + + //! Load an image from a DLM file (in-place version). + CImg& load_dlm(std::FILE *const file, const char *const filename=0) { + return get_load_dlm(file,filename).swap(*this); + } + + //! Load an image from a DLM file (in-place version). + CImg& load_dlm(const char *const filename) { + return get_load_dlm(filename).swap(*this); + } + + //! Load an image from a PNM file + static CImg get_load_pnm(std::FILE *const file, const char *const filename=0) { + std::FILE *const nfile=file?file:cimg::fopen(filename,"rb"); + unsigned int ppm_type,width,height,colormax=255; + char item[1024]={0}; + int err; + while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile); + if(std::sscanf(item," P%u",&ppm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_pnm() : File '%s', PNM header 'P?' not found.", + pixel_type(),filename?filename:"(FILE*)"); + } + while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile); + if ((err=std::sscanf(item," %u %u %u",&width,&height,&colormax))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_pnm() : File '%s', WIDTH and HEIGHT fields are not defined in PNM header.", + pixel_type(),filename?filename:"(FILE*)"); + } + if (err==2) { + while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile); + cimg::warn(std::sscanf(item,"%u",&colormax)!=1, + "CImg<%s>::get_load_pnm() : File '%s', COLORMAX field is not defined in PNM header.", + pixel_type(),filename?filename:"(FILE*)"); + } + std::fgetc(nfile); + + CImg dest; + int rval,gval,bval; + + switch (ppm_type) { + case 2: { // Grey Ascii + dest.assign(width,height,1,1); + T* rdata = dest.ptr(); + cimg_foroff(dest,off) { std::fscanf(nfile,"%d",&rval); *(rdata++)=(T)rval; } + } break; + case 3: { // Color Ascii + dest.assign(width,height,1,3); + T *rdata = dest.ptr(0,0,0,0), *gdata = dest.ptr(0,0,0,1), *bdata = dest.ptr(0,0,0,2); + cimg_forXY(dest,x,y) { + std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval); + *(rdata++)=(T)rval; + *(gdata++)=(T)gval; + *(bdata++)=(T)bval; } + } break; + case 5: { // Grey Binary + if (colormax<256) { // 8 bits + CImg raw(width,height,1,1); + cimg::fread(raw.data,width*height,nfile); + dest=raw; + } else { // 16 bits + CImg raw(width,height,1,1); + cimg::fread(raw.data,width*height,nfile); + if (!cimg::endian()) cimg::endian_swap(raw.data,width*height); + dest=raw; + } + } break; + case 6: { // Color Binary + if (colormax<256) { // 8 bits + CImg raw(width,height,1,3); + cimg::fread(raw.data,width*height*3,nfile); + dest.assign(width,height,1,3); + T *rdata = dest.ptr(0,0,0,0), *gdata = dest.ptr(0,0,0,1), *bdata = dest.ptr(0,0,0,2); + const unsigned char *ptrs = raw.ptr(); + for (unsigned int off = raw.width*raw.height; off; --off) { + *(rdata++) = (T)*(ptrs++); + *(gdata++) = (T)*(ptrs++); + *(bdata++) = (T)*(ptrs++); + } + } else { // 16 bits + CImg raw(width,height,1,3); + cimg::fread(raw.data,width*height*3,nfile); + if (!cimg::endian()) cimg::endian_swap(raw.data,width*height*3); + dest.assign(width,height,1,3); + T *rdata = dest.ptr(0,0,0,0), *gdata = dest.ptr(0,0,0,1), *bdata = dest.ptr(0,0,0,2); + const unsigned short *ptrs = raw.ptr(); + for (unsigned int off = raw.width*raw.height; off; --off) { + *(rdata++) = (T)*(ptrs++); + *(gdata++) = (T)*(ptrs++); + *(bdata++) = (T)*(ptrs++); + } + } + } break; + default: + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_pnm() : File '%s', PPM type 'P%d' not supported.", + pixel_type(),filename?filename:"(FILE*)",ppm_type); + } + if (!file) cimg::fclose(nfile); + return dest; + } + + //! Load an image from a PNM file. + static CImg get_load_pnm(const char *const filename) { + return get_load_pnm(0,filename); + } + + //! Load an image from a PNM file (in-place version). + CImg& load_pnm(std::FILE *const file, const char *const filename=0) { + return get_load_pnm(file,filename).swap(*this); + } + + //! Load an image from a PNM file (in-place version). + CImg& load_pnm(const char *const filename) { + return get_load_pnm(filename).swap(*this); + } + + //! Load a YUV image sequence file. + static CImg get_load_yuv(std::FILE *const file, const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const int last_frame=-1, + const bool yuv2rgb = false) { + return CImgList::get_load_yuv(file,filename,sizex,sizey,first_frame,last_frame,yuv2rgb).get_append('z','c'); + } + + //! Load a YUV image sequence file. + static CImg get_load_yuv(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const int last_frame=-1, + const bool yuv2rgb = false) { + return CImgList::get_load_yuv(filename,sizex,sizey,first_frame,last_frame,yuv2rgb).get_append('z','c'); + } + + //! Load a YUV image sequence file (in-place). + CImg& load_yuv(std::FILE *const file, const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const int last_frame=-1, + const bool yuv2rgb = false) { + return get_load_yuv(file,filename,sizex,sizey,first_frame,last_frame,yuv2rgb).swap(*this); + } + + //! Load a YUV image sequence file (in-place). + CImg& load_yuv(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const int last_frame=-1, + const bool yuv2rgb = false) { + return get_load_yuv(filename,sizex,sizey,first_frame,last_frame,yuv2rgb).swap(*this); + } + + //! Load an image from a BMP file. + static CImg get_load_bmp(std::FILE *const file, const char *const filename=0) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned char header[64]; + cimg::fread(header,54,nfile); + if (header[0]!='B' || header[1]!='M') { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_bmp() : File '%s' is not a valid BMP file.", + pixel_type(),filename?filename:"(FILE*)"); + } + + // Read header and pixel buffer + int + file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24), + offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24), + dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24), + dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24), + compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24), + nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24), + bpp = header[0x1C] + (header[0x1D]<<8), + *palette = 0; + const int + dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)), + align = (4-dx_bytes%4)%4, + buf_size = cimg::min(cimg::abs(dy)*(dx_bytes+align),file_size-offset); + + if (bpp<16) { if (!nb_colors) nb_colors=1<0) std::fseek(nfile,xoffset,SEEK_CUR); + unsigned char *buffer = new unsigned char[buf_size], *ptrs = buffer; + cimg::fread(buffer,buf_size,nfile); + if (!file) cimg::fclose(nfile); + + // Decompress buffer (if necessary) + if (compression) { + delete[] buffer; + if (file) { + throw CImgIOException("CImg<%s>::get_load_bmp() : Not able to read a compressed BMP file using a *FILE input",pixel_type()); + } else return get_load_other(filename); + } + + // Read pixel data + CImg res(dx,cimg::abs(dy),1,3); + switch (bpp) { + case 1: { // Monochrome + for (int y=res.height-1; y>=0; y--) { + unsigned char mask = 0x80, val = 0; + cimg_forX(res,x) { + if (mask==0x80) val = *(ptrs++); + const unsigned char *col = (unsigned char*)(palette+(val&mask?1:0)); + res(x,y,2) = (T)*(col++); + res(x,y,1) = (T)*(col++); + res(x,y,0) = (T)*(col++); + mask = cimg::ror(mask); + } ptrs+=align; } + } break; + case 4: { // 16 colors + for (int y=res.height-1; y>=0; y--) { + unsigned char mask = 0xF0, val = 0; + cimg_forX(res,x) { + if (mask==0xF0) val = *(ptrs++); + const unsigned char color = (mask<16)?(val&mask):((val&mask)>>4); + unsigned char *col = (unsigned char*)(palette+color); + res(x,y,2) = (T)*(col++); + res(x,y,1) = (T)*(col++); + res(x,y,0) = (T)*(col++); + mask = cimg::ror(mask,4); + } ptrs+=align; } + } break; + case 8: { // 256 colors + for (int y=res.height-1; y>=0; y--) { cimg_forX(res,x) { + const unsigned char *col = (unsigned char*)(palette+*(ptrs++)); + res(x,y,2) = (T)*(col++); + res(x,y,1) = (T)*(col++); + res(x,y,0) = (T)*(col++); + } ptrs+=align; } + } break; + case 16: { // 16 bits colors + for (int y=res.height-1; y>=0; y--) { cimg_forX(res,x) { + const unsigned char c1 = *(ptrs++), c2 = *(ptrs++); + const unsigned short col = c1+(c2<<8); + res(x,y,2) = (T)(col&0x1F); + res(x,y,1) = (T)((col>>5)&0x1F); + res(x,y,0) = (T)((col>>10)&0x1F); + } ptrs+=align; } + } break; + case 24: { // 24 bits colors + for (int y=res.height-1; y>=0; y--) { cimg_forX(res,x) { + res(x,y,2) = (T)*(ptrs++); + res(x,y,1) = (T)*(ptrs++); + res(x,y,0) = (T)*(ptrs++); + } ptrs+=align; } + } break; + case 32: { // 32 bits colors + for (int y=res.height-1; y>=0; y--) { cimg_forX(res,x) { + res(x,y,2) = (T)*(ptrs++); + res(x,y,1) = (T)*(ptrs++); + res(x,y,0) = (T)*(ptrs++); + ptrs++; + } ptrs+=align; } + } break; + } + if (palette) delete[] palette; + delete[] buffer; + if (dy<0) res.mirror('y'); + return res; + } + + //! Load an image from a BMP file + static CImg get_load_bmp(const char *const filename) { + return get_load_bmp(0,filename); + } + + //! Load an image from a BMP file + CImg& load_bmp(std::FILE *const file, const char *const filename=0) { + return get_load_bmp(file,filename).swap(*this); + } + + //! Load an image from a BMP file + CImg& load_bmp(const char *const filename) { + return get_load_bmp(filename).swap(*this); + } + + //! Load an image from a PNG file. + // Note : Most of this function has been written by Eric Fausett + static CImg get_load_png(std::FILE *const file, const char *const filename=0) { +#ifndef cimg_use_png + if (file) + throw CImgIOException("CImg<%s>::get_load_png() : File '(FILE*)' cannot be read without using libpng.",pixel_type()); + else return get_load_other(filename); +#else + // Open file and check for PNG validity + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned char pngCheck[8]; + cimg::fread(pngCheck,8,nfile); + if (png_sig_cmp(pngCheck,0,8)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_png() : File '%s' is not a valid PNG file.", + pixel_type(),filename?filename:"(FILE*)"); + } + + // Setup PNG structures for read + png_voidp user_error_ptr=0; + png_error_ptr user_error_fn=0, user_warning_fn=0; + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, // Verifies libpng version correct + user_error_ptr, user_error_fn, user_warning_fn); + if(!png_ptr){ + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_png() : File '%s', trouble initializing 'png_ptr' data structure.", + pixel_type(),filename?filename:"(FILE*)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if(!info_ptr){ + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0); + throw CImgIOException("CImg<%s>::get_load_png() : File '%s', trouble initializing 'info_ptr' data structure.", + pixel_type(),filename?filename:"(FILE*)"); + } + png_infop end_info = png_create_info_struct(png_ptr); + if(!end_info){ + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0); + throw CImgIOException("CImg<%s>::get_load_png() : File '%s', trouble initializing 'end_info' data structure.", + pixel_type(),filename?filename:"(FILE*)"); + } + + // Error handling callback for png file reading + if (setjmp(png_jmpbuf(png_ptr))){ + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); + throw CImgIOException("CImg<%s>::get_load_png() : File '%s', unknown fatal error.", + pixel_type(),filename?filename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + png_set_sig_bytes(png_ptr, 8); + + // Get PNG Header Info up to data block + png_read_info(png_ptr, info_ptr); + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, + int_p_NULL, int_p_NULL); + int new_bit_depth = bit_depth; + int new_color_type = color_type; + + // Transforms to unify image data + if (new_color_type == PNG_COLOR_TYPE_PALETTE){ + png_set_palette_to_rgb(png_ptr); + new_color_type -= PNG_COLOR_MASK_PALETTE; + new_bit_depth = 8; + } + if (new_color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8){ + png_set_gray_1_2_4_to_8(png_ptr); + new_bit_depth = 8; + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + if (new_color_type == PNG_COLOR_TYPE_GRAY || new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA){ + png_set_gray_to_rgb(png_ptr); + new_color_type |= PNG_COLOR_MASK_COLOR; + } + if (new_color_type == PNG_COLOR_TYPE_RGB) png_set_filler(png_ptr, 0xffffU, PNG_FILLER_AFTER); + png_read_update_info(png_ptr, info_ptr); + if (!(new_bit_depth==8 || new_bit_depth==16)) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); + throw CImgIOException("CImg<%s>::get_load_png() : File '%s', wrong bit coding (bit_depth=%u)", + pixel_type(),filename?filename:"(FILE*)",new_bit_depth); + } + const int byte_depth = new_bit_depth>>3; + + // Allocate Memory for Image Read + png_bytep *imgData = new png_bytep[height]; + for (unsigned int row=0; row < height; row++) imgData[row] = new png_byte[byte_depth * 4 * width]; + png_read_image(png_ptr, imgData); + png_read_end(png_ptr, end_info); + + // Read pixel data + if (!(new_color_type==PNG_COLOR_TYPE_RGB || new_color_type==PNG_COLOR_TYPE_RGB_ALPHA)) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); + throw CImgIOException("CImg<%s>::get_load_png() : File '%s', wrong color coding (new_color_type=%u)", + pixel_type(),filename?filename:"(FILE*)",new_color_type); + } + const bool no_alpha_channel = (new_color_type==PNG_COLOR_TYPE_RGB); + CImg res(width,height,1,no_alpha_channel?3:4); + const unsigned long off = width*height; + T *ptr1 = res.data, *ptr2 = ptr1+off, *ptr3 = ptr2+off, *ptr4 = ptr3+off; + switch(new_bit_depth){ + case 8: { + cimg_forY(res,y){ + const unsigned char *ptrs = (unsigned char*)imgData[y]; + cimg_forX(res,x){ + *(ptr1++) = (T)*(ptrs++); + *(ptr2++) = (T)*(ptrs++); + *(ptr3++) = (T)*(ptrs++); + if (no_alpha_channel) ptrs++; else *(ptr4++) = (T)*(ptrs++); + } + } + } break; + case 16: { + cimg_forY(res,y){ + const unsigned short *ptrs = (unsigned short*)(imgData[y]); + cimg_forX(res,x){ + *(ptr1++) = (T)*(ptrs++); + *(ptr2++) = (T)*(ptrs++); + *(ptr3++) = (T)*(ptrs++); + if (no_alpha_channel) ptrs++; else *(ptr4++) = (T)*(ptrs++); + } + } + } break; + } + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + + // Deallocate Image Read Memory + for (unsigned int n=0; n=3 + TIFFSetWarningHandler(0); + TIFFSetErrorHandler(0); +#endif + if (tif) { + unsigned int number_of_directories = 0; + do number_of_directories++; while (TIFFReadDirectory(tif)); + uint16 samplesperpixel, bitspersample; + uint32 nx,ny; + TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); + TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); + TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); + if (samplesperpixel!=1 && samplesperpixel!=3 && samplesperpixel!=4) { + cimg::warn(true,"CImg<%s>::get_load_tiff() : File '%s', unknow value for tag : TIFFTAG_SAMPLESPERPIXEL, will force it to 1.", + pixel_type(),filename?filename:"(FILE*)"); + samplesperpixel=1; + } + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample); + TIFFClose(tif); + tif = TIFFOpen(filename,"r"); + dest.assign(nx,ny,number_of_directories,samplesperpixel); + + unsigned int dir=0; + do { + if (bitspersample!=8 || !(samplesperpixel == 1 || samplesperpixel == 3 || samplesperpixel == 4)){ //if !rgba 8bit + uint16 photo, config; + TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); + TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); + if (TIFFIsTiled(tif)) { + uint32 tw, th; + TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw); + TIFFGetField(tif, TIFFTAG_TILELENGTH, &th); + if (config==PLANARCONFIG_CONTIG) { + switch(bitspersample){ + case 8:{ + unsigned char *buf; + buf = (unsigned char *)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int row = 0; row::get_load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename?filename:"(FILE*)"); + } else { + unsigned char * ptr = buf; + for (unsigned int rr=row;rr::get_load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename?filename:"(FILE*)"); + } else { + unsigned short * ptr = buf; + for (unsigned int rr=row;rr::get_load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename?filename:"(FILE*)"); + } else { + float * ptr = buf; + for (unsigned int rr=row;rr::get_load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename?filename:"(FILE*)"); + } else { + unsigned char * ptr = buf; + for (unsigned int rr=row;rr::get_load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename?filename:"(FILE*)"); + } else { + unsigned short * ptr = buf; + for (unsigned int rr=row;rr::get_load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename?filename:"(FILE*)"); + } else { + float * ptr = buf; + for (unsigned int rr=row;rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); + TIFFClose(tif); + throw CImgException("CImg<%s>::get_load_tiff() : File '%s', an error occure while reading a strip.", + pixel_type(),filename?filename:"(FILE*)"); + } + unsigned char * ptr = buf; + for (unsigned int rr=0; rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); + TIFFClose(tif); + throw CImgException("CImg<%s>::get_load_tiff() : File '%s', error while reading a strip.", + pixel_type(),filename?filename:"(FILE*)"); + } + unsigned short * ptr = buf; + for (unsigned int rr=0; rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); + TIFFClose(tif); + throw CImgException("CImg<%s>::get_load_tiff() : File '%s', error while reading a strip.", + pixel_type(),filename?filename:"(FILE*)"); + } + float * ptr = buf; + for (unsigned int rr=0; rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); + TIFFClose(tif); + throw CImgException("CImg<%s>::get_load_tiff() : File '%s', an error occure while reading a strip.", + pixel_type(),filename?filename:"(FILE*)"); + } + unsigned char * ptr = buf; + for (unsigned int rr=0;rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); + TIFFClose(tif); + throw CImgException("CImg<%s>::get_load_tiff() : File '%s', error while reading a strip.", + pixel_type(),filename?filename:"(FILE*)"); + } + unsigned short * ptr = buf; + for (unsigned int rr=0;rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); + TIFFClose(tif); + throw CImgException("CImg<%s>::get_load_tiff() : File '%s', error while reading a strip.", + pixel_type(),filename?filename:"(FILE*)"); + } + float * ptr = buf; + for (unsigned int rr=0;rr::get_load_tiff() : File '%s', not enough memory for buffer allocation.", + pixel_type(),filename?filename:"(FILE*)"); + } + TIFFReadRGBAImage(tif,nx,ny,raster,0); + switch (samplesperpixel){ + case 1:{ + cimg_forXY(dest,x,y) dest(x,y,dir)=(T)(float)((raster[nx*(ny-1-y)+x]+ 128) / 257); + break; + } + case 3:{ + cimg_forXY(dest,x,y) { + dest(x,y,dir,0)=(T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]); + dest(x,y,dir,1)=(T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]); + dest(x,y,dir,2)=(T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]); + } + break; + } + case 4:{ + cimg_forXY(dest,x,y) { + dest(x,y,dir,0)=(T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]); + dest(x,y,dir,1)=(T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]); + dest(x,y,dir,2)=(T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]); + dest(x,y,dir,3)=(T)(float)TIFFGetA(raster[nx*(ny-1-y)+x]); + } + break; + } + } + _TIFFfree(raster); + } + dir++; + } while (TIFFReadDirectory(tif)); + TIFFClose(tif); + } else + throw CImgException("CImg<%s>::get_load_tiff() : File '%s', error while loading the image.", + pixel_type(),filename?filename:"(FILE*)"); + return dest; +#endif + } + + //! Load an image from a TIFF file + CImg& load_tiff(const char *const filename) { + return get_load_tiff(filename).swap(*this); + } + + //! Load a file in JPEG format. + static CImg get_load_jpeg(std::FILE *const file, const char *const filename=0) { +#ifndef cimg_use_jpeg + if (file) + throw CImgIOException("CImg<%s>::get_load_jpeg() : File '(FILE*)' cannot be read without using libjpeg.", + pixel_type()); + else return get_load_other(filename); +#else + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo,nfile); + jpeg_read_header(&cinfo,TRUE); + jpeg_start_decompress(&cinfo); + + if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) { + cimg::warn(true,"CImg<%s>::get_load_jpeg() : Don't know how to read image '%s' with libpeg, trying ImageMagick's convert", + pixel_type(),filename?filename:"(unknown)"); + if (!file) return get_load_other(filename); + else { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_jpeg() : Cannot read JPEG image '%s' using a *FILE input.", + pixel_type(),filename?filename:"(FILE*)"); + } + } + + const unsigned int row_stride = cinfo.output_width * cinfo.output_components; + unsigned char *buf = new unsigned char[cinfo.output_width*cinfo.output_height*cinfo.output_components], *buf2 = buf; + JSAMPROW row_pointer[1]; + while (cinfo.output_scanline < cinfo.output_height) { + row_pointer[0] = &buf[cinfo.output_scanline*row_stride]; + jpeg_read_scanlines(&cinfo,row_pointer,1); + } + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + if (!file) cimg::fclose(nfile); + + CImg dest(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); + switch (dest.dim) { + case 1: { + T *ptr_g = dest.ptr(); + cimg_forXY(dest,x,y) *(ptr_g++) = (T)*(buf2++); + } break; + case 3: { + T *ptr_r = dest.ptr(0,0,0,0), *ptr_g = dest.ptr(0,0,0,1), *ptr_b = dest.ptr(0,0,0,2); + cimg_forXY(dest,x,y) { + *(ptr_r++) = (T)*(buf2++); + *(ptr_g++) = (T)*(buf2++); + *(ptr_b++) = (T)*(buf2++); + } + } break; + case 4: { + T *ptr_r = dest.ptr(0,0,0,0), *ptr_g = dest.ptr(0,0,0,1), + *ptr_b = dest.ptr(0,0,0,2), *ptr_a = dest.ptr(0,0,0,3); + cimg_forXY(dest,x,y) { + *(ptr_r++) = (T)*(buf2++); + *(ptr_g++) = (T)*(buf2++); + *(ptr_b++) = (T)*(buf2++); + *(ptr_a++) = (T)*(buf2++); + } + } break; + } + delete[] buf; + return dest; +#endif + } + + //! Load an image from a JPEG file + static CImg get_load_jpeg(const char *const filename) { + return get_load_jpeg(0,filename); + } + + //! Load an image from a JPEG file + CImg& load_jpeg(std::FILE *const file, const char *const filename=0) { + return get_load_jpeg(file,filename).swap(*this); + } + + //! Load an image from a JPEG file + CImg& load_jpeg(const char *const filename) { + return get_load_jpeg(filename).swap(*this); + } + + //! Load an image using builtin ImageMagick++ Library + /** + Added April/may 2006 by Christoph Hormann + This is experimental code, not much tested, use with care. + **/ + static CImg get_load_magick(const char *const filename) { + CImg dest; +#ifdef cimg_use_magick + Magick::Image image(filename); + const unsigned int width = image.size().width(), height = image.size().height(); + switch (image.type()) { + case Magick::PaletteMatteType: + case Magick::TrueColorMatteType: + case Magick::ColorSeparationType: { + dest.assign(width,height,1,4); + T *rdata = dest.ptr(0,0,0,0), *gdata = dest.ptr(0,0,0,1), *bdata = dest.ptr(0,0,0,2), *adata = dest.ptr(0,0,0,3); + Magick::PixelPacket *pixels = image.getPixels(0,0,width,height); + for (unsigned int off = width*height; off; --off) { + *(rdata++) = (T)(pixels->red); + *(gdata++) = (T)(pixels->green); + *(bdata++) = (T)(pixels->blue); + *(adata++) = (T)(pixels->opacity); + pixels++; + } + } break; + case Magick::PaletteType: + case Magick::TrueColorType: { + dest.assign(width,height,1,3); + T *rdata = dest.ptr(0,0,0,0), *gdata = dest.ptr(0,0,0,1), *bdata = dest.ptr(0,0,0,2); + Magick::PixelPacket *pixels = image.getPixels(0,0,width,height); + for (unsigned int off = width*height; off; --off) { + *(rdata++) = (T)(pixels->red); + *(gdata++) = (T)(pixels->green); + *(bdata++) = (T)(pixels->blue); + pixels++; + } + } break; + case Magick::GrayscaleMatteType: { + dest.assign(width,height,1,2); + T *data = dest.ptr(0,0,0,0), *adata = dest.ptr(0,0,0,1); + Magick::PixelPacket *pixels = image.getPixels(0,0,width,height); + for (unsigned int off = width*height; off; --off) { + *(data++) = (T)(pixels->red); + *(adata++) = (T)(pixels->opacity); + pixels++; + } + } break; + default: { + dest.assign(width,height,1,1); + T *data = dest.ptr(0,0,0,0); + Magick::PixelPacket *pixels = image.getPixels(0,0,width,height); + for (unsigned int off = width*height; off; --off) { + *(data++) = (T)(pixels->red); + pixels++; + } + } break; + } + return dest; +#else + throw CImgIOException("CImg<%s>::get_load_magick() : File '%s', Magick++ has not been linked during compilation.", + pixel_type(),filename?filename:"(null)"); + return dest; +#endif + } + + //! Load an image using builtin ImageMagick++ Library (in-place version). + CImg& load_magick(const char *const filename) { + return get_load_magick(filename).swap(*this); + } + + //! Load an image from a RAW file. + static CImg get_load_raw(std::FILE *const file, const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int sizez=1, const unsigned int sizev=1, + const bool multiplexed=false, const bool endian_swap=false) { + CImg res(sizex,sizey,sizez,sizev,0); + if (res.is_empty()) return res; + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + if (!multiplexed) { + cimg::fread(res.ptr(),res.size(),nfile); + if (endian_swap) cimg::endian_swap(res.ptr(),res.size()); + } + else { + CImg buf(1,1,1,sizev); + cimg_forXYZ(res,x,y,z) { + cimg::fread(buf.ptr(),sizev,nfile); + if (endian_swap) cimg::endian_swap(buf.ptr(),sizev); + res.set_vector_at(buf,x,y,z); } + } + if (!file) cimg::fclose(nfile); + return res; + } + + //! Load an image from a RAW file. + static CImg get_load_raw(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int sizez=1, const unsigned int sizev=1, + const bool multiplexed = false, const bool endian_swap = false) { + return get_load_raw(0,filename,sizex,sizey,sizez,sizev,multiplexed,endian_swap); + } + + //! In-place version of get_load_raw() + CImg& load_raw(std::FILE *const file, const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int sizez=1, const unsigned int sizev=1, + const bool multiplexed = false, const bool endian_swap = false) { + return get_load_raw(file,filename,sizex,sizey,sizez,sizev,multiplexed,endian_swap).swap(*this); + } + + //! In-place version of get_load_raw() + CImg& load_raw(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int sizez=1, const unsigned int sizev=1, + const bool multiplexed = false, const bool endian_swap = false) { + return get_load_raw(filename,sizex,sizey,sizez,sizev,multiplexed,endian_swap).swap(*this); + } + + //! Load an image from a RGBA file. + static CImg get_load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned char *buffer = new unsigned char[dimw*dimh*4]; + cimg::fread(buffer,dimw*dimh*4,nfile); + if (!file) cimg::fclose(nfile); + CImg res(dimw,dimh,1,4); + T *pR = res.ptr(0,0,0,0), *pG = res.ptr(0,0,0,1), *pB = res.ptr(0,0,0,2), *pA = res.ptr(0,0,0,3); + const unsigned char *ptrs = buffer; + for (unsigned int off=res.width*res.height; off>0; --off) { + *(pR++) = (T)*(ptrs++); + *(pG++) = (T)*(ptrs++); + *(pB++) = (T)*(ptrs++); + *(pA++) = (T)*(ptrs++); + } + delete[] buffer; + return res; + } + + //! Load an image from a RGBA file. + static CImg get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh) { + return get_load_rgba(0,filename,dimw,dimh); + } + + //! In-place version of get_load_rgba() + CImg& load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + return get_load_rgba(file, filename,dimw,dimh).swap(*this); + } + + //! In-place version of get_load_rgba() + CImg& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh) { + return get_load_rgba(filename,dimw,dimh).swap(*this); + } + + //! Load an image from a RGB file. + static CImg get_load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned char *buffer = new unsigned char[dimw*dimh*3]; + cimg::fread(buffer,dimw*dimh*3,nfile); + if (!file) cimg::fclose(nfile); + CImg res(dimw,dimh,1,3); + T *pR = res.ptr(0,0,0,0), *pG = res.ptr(0,0,0,1), *pB=res.ptr(0,0,0,2); + const unsigned char *ptrs = buffer; + for (unsigned int off=res.width*res.height; off>0; --off) { + *(pR++) = (T)*(ptrs++); + *(pG++) = (T)*(ptrs++); + *(pB++) = (T)*(ptrs++); + } + delete[] buffer; + return res; + } + + //! Load an image from a RGB file. + static CImg get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh) { + return get_load_rgb(0,filename,dimw,dimh); + } + + //! In-place version of get_load_rgb() + CImg& load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + return get_load_rgb(file, filename,dimw,dimh).swap(*this); + } + + //! In-place version of get_load_rgb() + CImg& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh) { + return get_load_rgb(filename,dimw,dimh).swap(*this); + } + +#define cimg_load_inr_case(Tf,sign,pixsize,Ts) \ + if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \ + Ts *xval, *val = new Ts[fopt[0]*fopt[3]]; \ + cimg_forYZ(dest,y,z) { \ + cimg::fread(val,fopt[0]*fopt[3],nfile); \ + if (fopt[7]!=endian) cimg::endian_swap(val,fopt[0]*fopt[3]); \ + xval = val; cimg_forX(dest,x) cimg_forV(dest,k) \ + dest(x,y,z,k) = (T)*(xval++); \ + } \ + delete[] val; \ + loaded = true; \ + } + + static void _load_inr(std::FILE *file, int out[8], float *const voxsize=0) { + char item[1024],tmp1[64],tmp2[64]; + out[0]=out[1]=out[2]=out[3]=out[5]=1; out[4]=out[6]=out[7]=-1; + std::fscanf(file,"%63s",item); + if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0) + throw CImgIOException("CImg<%s>::get_load_inr() : File does not appear to be a valid INR file.\n" + "(INRIMAGE-4 identifier not found)",pixel_type()); + while (std::fscanf(file," %63[^\n]%*c",item)!=EOF && cimg::strncmp(item,"##}",3)) { + std::sscanf(item," XDIM%*[^0-9]%d",out); + std::sscanf(item," YDIM%*[^0-9]%d",out+1); + std::sscanf(item," ZDIM%*[^0-9]%d",out+2); + std::sscanf(item," VDIM%*[^0-9]%d",out+3); + std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6); + if (voxsize) { + std::sscanf(item," VX%*[^0-9.eE+-]%f",voxsize); + std::sscanf(item," VY%*[^0-9.eE+-]%f",voxsize+1); + std::sscanf(item," VZ%*[^0-9.eE+-]%f",voxsize+2); + } + if (std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1; + switch(std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) { + case 0: break; + case 2: out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strcpy(tmp1,tmp2); + case 1: + if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4]=0; + if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4]=1; + if (!cimg::strncasecmp(tmp1,"packed",6)) out[4]=2; + if (out[4]>=0) break; + default: throw CImgIOException("cimg::inr_header_read() : Invalid TYPE '%s'",tmp2); + } + } + if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) + throw CImgIOException("CImg<%s>::get_load_inr() : Bad dimensions in .inr file = ( %d , %d , %d , %d )", + pixel_type(),out[0],out[1],out[2],out[3]); + if(out[4]<0 || out[5]<0) throw CImgIOException("CImg<%s>::get_load_inr() : TYPE is not fully defined",pixel_type()); + if(out[6]<0) throw CImgIOException("CImg<%s>::get_load_inr() : PIXSIZE is not fully defined",pixel_type()); + if(out[7]<0) throw CImgIOException("CImg<%s>::get_load_inr() : Big/Little Endian coding type is not defined",pixel_type()); + } + + //! Load an image from an INRIMAGE-4 file. + static CImg get_load_inr(std::FILE *const file, const char *const filename=0, float *voxsize=0) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + int fopt[8], endian=cimg::endian()?1:0; + bool loaded = false; + if (voxsize) voxsize[0]=voxsize[1]=voxsize[2]=1; + _load_inr(nfile,fopt,voxsize); + CImg dest(fopt[0],fopt[1],fopt[2],fopt[3]); + cimg_load_inr_case(0,0,8, unsigned char); + cimg_load_inr_case(0,1,8, char); + cimg_load_inr_case(0,0,16,unsigned short); + cimg_load_inr_case(0,1,16,short); + cimg_load_inr_case(0,0,32,unsigned int); + cimg_load_inr_case(0,1,32,int); + cimg_load_inr_case(1,0,32,float); + cimg_load_inr_case(1,1,32,float); + cimg_load_inr_case(1,0,64,double); + cimg_load_inr_case(1,1,64,double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_inr() : File '%s', cannot read images of the type specified in the file", + pixel_type(),filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return dest; + } + + //! Load an image from an INRIMAGE-4 file. + static CImg get_load_inr(const char *const filename, float *const voxsize=0) { + return get_load_inr(0,filename,voxsize); + } + + //! In-place version of get_load_inr() + CImg& load_inr(std::FILE *const file, const char *const filename=0, float *const voxsize=0) { + return get_load_inr(file,filename,voxsize).swap(*this); + } + + //! In-place version of get_load_inr() + CImg& load_inr(const char *const filename, float *const voxsize=0) { + return get_load_inr(filename,voxsize).swap(*this); + } + +#define cimg_load_pandore_case(nid,nbdim,nwidth,nheight,ndepth,ndim,stype) \ + case nid: { \ + cimg::fread(dims,nbdim,nfile); \ + if (endian) cimg::endian_swap(dims,nbdim); \ + dest.assign(nwidth,nheight,ndepth,ndim); \ + stype *buffer = new stype[dest.size()]; \ + cimg::fread(buffer,dest.size(),nfile); \ + if (endian) cimg::endian_swap(buffer,dest.size()); \ + T *ptrd = dest.ptr(); \ + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); \ + buffer-=dest.size(); \ + delete[] buffer; \ + } \ + break; + + //! Load an image from a PANDORE-5 file. + static CImg get_load_pandore(std::FILE *const file, const char *const filename=0) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + typedef unsigned char uchar; + typedef unsigned short ushort; + typedef unsigned int uint; + typedef unsigned long ulong; + CImg dest; + char tmp[32]; + cimg::fread(tmp,12,nfile); + if (cimg::strncasecmp("PANDORE",tmp,7)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_pandore() : File '%s' is not a valid PANDORE file.\n" + "(PANDORE identifier not found).",pixel_type(),filename?filename:"(FILE*)"); + } + unsigned int imageid,dims[8]; + int ptbuf[4]; + cimg::fread(&imageid,1,nfile); + const bool endian = (imageid>255); + if (endian) cimg::endian_swap(imageid); + + cimg::fread(tmp,20,nfile); + switch (imageid) { + cimg_load_pandore_case(2,2,dims[1],1,1,1,uchar); + cimg_load_pandore_case(3,2,dims[1],1,1,1,long); + cimg_load_pandore_case(4,2,dims[1],1,1,1,float); + cimg_load_pandore_case(5,3,dims[2],dims[1],1,1,uchar); + cimg_load_pandore_case(6,3,dims[2],dims[1],1,1,long); + cimg_load_pandore_case(7,3,dims[2],dims[1],1,1,float); + cimg_load_pandore_case(8,4,dims[3],dims[2],dims[1],1,uchar); + cimg_load_pandore_case(9,4,dims[3],dims[2],dims[1],1,long); + cimg_load_pandore_case(10,4,dims[3],dims[2],dims[1],1,float); + + case 11: { // Region 1D + cimg::fread(dims,3,nfile); + if (endian) cimg::endian_swap(dims,3); + dest.assign(dims[1],1,1,1); + if (dims[2]<256) { + unsigned char *buffer = new unsigned char[dest.size()]; + cimg::fread(buffer,dest.size(),nfile); + T *ptrd = dest.ptr(); + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); + buffer-=dest.size(); + delete[] buffer; + } else { + if (dims[2]<65536) { + unsigned short *buffer = new unsigned short[dest.size()]; + cimg::fread(buffer,dest.size(),nfile); + if (endian) cimg::endian_swap(buffer,dest.size()); + T *ptrd = dest.ptr(); + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); + buffer-=dest.size(); + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[dest.size()]; + cimg::fread(buffer,dest.size(),nfile); + if (endian) cimg::endian_swap(buffer,dest.size()); + T *ptrd = dest.ptr(); + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); + buffer-=dest.size(); + delete[] buffer; + } + } + } + break; + case 12: { // Region 2D + cimg::fread(dims,4,nfile); + if (endian) cimg::endian_swap(dims,4); + dest.assign(dims[2],dims[1],1,1); + if (dims[3]<256) { + unsigned char *buffer = new unsigned char[dest.size()]; + cimg::fread(buffer,dest.size(),nfile); + T *ptrd = dest.ptr(); + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); + buffer-=dest.size(); + delete[] buffer; + } else { + if (dims[3]<65536) { + unsigned short *buffer = new unsigned short[dest.size()]; + cimg::fread(buffer,dest.size(),nfile); + if (endian) cimg::endian_swap(buffer,dest.size()); + T *ptrd = dest.ptr(); + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); + buffer-=dest.size(); + delete[] buffer; + } else { + unsigned long *buffer = new unsigned long[dest.size()]; + cimg::fread(buffer,dest.size(),nfile); + if (endian) cimg::endian_swap(buffer,dest.size()); + T *ptrd = dest.ptr(); + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); + buffer-=dest.size(); + delete[] buffer; + } + } + } + break; + case 13: { // Region 3D + cimg::fread(dims,5,nfile); + if (endian) cimg::endian_swap(dims,5); + dest.assign(dims[3],dims[2],dims[1],1); + if (dims[4]<256) { + unsigned char *buffer = new unsigned char[dest.size()]; + cimg::fread(buffer,dest.size(),nfile); + T *ptrd = dest.ptr(); + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); + buffer-=dest.size(); + delete[] buffer; + } else { + if (dims[4]<65536) { + unsigned short *buffer = new unsigned short[dest.size()]; + cimg::fread(buffer,dest.size(),nfile); + if (endian) cimg::endian_swap(buffer,dest.size()); + T *ptrd = dest.ptr(); + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); + buffer-=dest.size(); + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[dest.size()]; + cimg::fread(buffer,dest.size(),nfile); + if (endian) cimg::endian_swap(buffer,dest.size()); + T *ptrd = dest.ptr(); + cimg_foroff(dest,off) *(ptrd++) = (T)*(buffer++); + buffer-=dest.size(); + delete[] buffer; + } + } + } + break; + cimg_load_pandore_case(16,4,dims[2],dims[1],1,3,uchar); + cimg_load_pandore_case(17,4,dims[2],dims[1],1,3,long); + cimg_load_pandore_case(18,4,dims[2],dims[1],1,3,float); + cimg_load_pandore_case(19,5,dims[3],dims[2],dims[1],3,uchar); + cimg_load_pandore_case(20,5,dims[3],dims[2],dims[1],3,long); + cimg_load_pandore_case(21,5,dims[3],dims[2],dims[1],3,float); + cimg_load_pandore_case(22,2,dims[1],1,1,dims[0],uchar); + cimg_load_pandore_case(23,2,dims[1],1,1,dims[0],long); + cimg_load_pandore_case(24,2,dims[1],1,1,dims[0],ulong); + cimg_load_pandore_case(25,2,dims[1],1,1,dims[0],float); + cimg_load_pandore_case(26,3,dims[2],dims[1],1,dims[0],uchar); + cimg_load_pandore_case(27,3,dims[2],dims[1],1,dims[0],long); + cimg_load_pandore_case(28,3,dims[2],dims[1],1,dims[0],ulong); + cimg_load_pandore_case(29,3,dims[2],dims[1],1,dims[0],float); + cimg_load_pandore_case(30,4,dims[3],dims[2],dims[1],dims[0],uchar); + cimg_load_pandore_case(31,4,dims[3],dims[2],dims[1],dims[0],long); + cimg_load_pandore_case(32,4,dims[3],dims[2],dims[1],dims[0],ulong); + cimg_load_pandore_case(33,4,dims[3],dims[2],dims[1],dims[0],float); + case 34: // Points 1D + cimg::fread(ptbuf,1,nfile); + if (endian) cimg::endian_swap(ptbuf,1); + dest.assign(1); dest[0]=(T)ptbuf[0]; + break; + case 35: // Points 2D + cimg::fread(ptbuf,2,nfile); + if (endian) cimg::endian_swap(ptbuf,2); + dest.assign(2); dest[0]=(T)ptbuf[1]; dest[1]=(T)ptbuf[0]; + break; + case 36: // Points 3D + cimg::fread(ptbuf,3,nfile); + if (endian) cimg::endian_swap(ptbuf,3); + dest.assign(3); dest[0]=(T)ptbuf[2]; dest[1]=(T)ptbuf[1]; dest[2]=(T)ptbuf[0]; + break; + default: + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::get_load_pandore() : File '%s', cannot read images with ID_type=%u", + pixel_type(),filename?filename:"(FILE*)",imageid); + } + if (!file) cimg::fclose(nfile); + return dest; + } + + //! Load an image from a PANDORE-5 file. + static CImg get_load_pandore(const char *const filename) { + return get_load_pandore(0,filename); + } + + //! In-place version of get_load_pandore() + CImg& load_pandore(std::FILE *const file, const char *const filename) { + return get_load_pandore(file,filename).swap(*this); + } + + //! In-place version of get_load_pandore() + CImg& load_pandore(const char *const filename) { + return get_load_pandore(filename).swap(*this); + } + + //! Load an image from an ANALYZE7.5 file + static CImg get_load_analyze(const char *const filename, float *const voxsize=0) { + + // Open header and data files + std::FILE *file_header=0, *file=0; + char body[1024]; + const char *ext = cimg::filename_split(filename,body); + if (!cimg::strncasecmp(ext,"hdr",3) || + !cimg::strncasecmp(ext,"img",3)) { + std::sprintf(body+cimg::strlen(body),".hdr"); + file_header = cimg::fopen(body,"rb"); + if (!file_header) return CImg(); + std::sprintf(body+cimg::strlen(body)-3,"img"); + file = cimg::fopen(body,"rb"); + if (!file) { cimg::fclose(file_header); return CImg(); } + } else throw CImgIOException("CImg<%s>::get_load_analyze() : Filename '%s', not recognized as an Analyze 7.5 file.", + pixel_type(),filename); + + // Read header + bool endian = false; + unsigned int header_size; + cimg::fread(&header_size,1,file_header); + if (header_size>=4096) { endian = true; cimg::endian_swap(header_size); } + unsigned char *header = new unsigned char[header_size]; + cimg::fread(header+4,header_size-4,file_header); + cimg::fclose(file_header); + if (endian) { + cimg::endian_swap((short*)(header+40),5); + cimg::endian_swap((short*)(header+70),1); + cimg::endian_swap((short*)(header+72),1); + cimg::endian_swap((float*)(header+76),4); + cimg::endian_swap((float*)(header+112),1); + } + unsigned short *dim = (unsigned short*)(header+40), dimx=1, dimy=1, dimz=1, dimv=1; + cimg::warn(!dim[0],"CImg<%s>::get_load_analyze() : Specified image has zero dimensions.",pixel_type()); + cimg::warn(dim[0]>4,"CImg<%s>::get_load_analyze() : Number of image dimension is %d, reading only the 4 first dimensions", + pixel_type(),dim[0]); + if (dim[0]>=1) dimx = dim[1]; + if (dim[0]>=2) dimy = dim[2]; + if (dim[0]>=3) dimz = dim[3]; + if (dim[0]>=4) dimv = dim[4]; + + float scalefactor = *(float*)(header+112); if (scalefactor==0) scalefactor=1; + const unsigned short datatype = *(short*)(header+70); + if (voxsize) { const float *vsize = (float*)(header+76); voxsize[0] = vsize[1]; voxsize[1] = vsize[2]; voxsize[2] = vsize[3]; } + delete[] header; + + // Read pixel data + CImg dest(dimx,dimy,dimz,dimv); + switch (datatype) { + case 2: { + unsigned char *buffer = new unsigned char[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,file); + cimg_foroff(dest,off) dest.data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 4: { + short *buffer = new short[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,file); + if (endian) cimg::endian_swap(buffer,dimx*dimy*dimz*dimv); + cimg_foroff(dest,off) dest.data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 8: { + int *buffer = new int[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,file); + if (endian) cimg::endian_swap(buffer,dimx*dimy*dimz*dimv); + cimg_foroff(dest,off) dest.data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 16: { + float *buffer = new float[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,file); + if (endian) cimg::endian_swap(buffer,dimx*dimy*dimz*dimv); + cimg_foroff(dest,off) dest.data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 64: { + double *buffer = new double[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,file); + if (endian) cimg::endian_swap(buffer,dimx*dimy*dimz*dimv); + cimg_foroff(dest,off) dest.data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + default: + cimg::fclose(file); + throw CImgIOException("CImg<%s>::get_load_analyze() : File '%s, cannot read images width 'datatype = %d'", + pixel_type(),filename,datatype); + } + cimg::fclose(file); + return dest; + } + + //! In-place version of get_load_analyze() + CImg& load_analyze(const char *const filename, float *const voxsize = 0) { + return get_load_analyze(filename,voxsize).swap(*this); + } + + //! Load PAR-REC (Philips) image file + static CImg get_load_parrec(const char *const filename, const char axe='v', const char align='p') { + return CImgList::get_load_parrec(filename).get_append(axe,align); + } + + //! In-place version of get_load_parrec() + CImg& load_parrec(const char *const filename, const char axis='v', const char align='p') { + return get_load_parrec(filename,axis,align).swap(*this); + } + + //! Load an image from a CImg RAW file + static CImg get_load_cimg(std::FILE *const file, const char *const filename=0, const char axis='v', const char align='p') { + return CImgList::get_load_cimg(file,filename).get_append(axis,align); + } + + //! Load an image from a CImg RAW file + static CImg get_load_cimg(const char *const filename, const char axis='v', const char align='p') { + return get_load_cimg(0,filename,axis,align); + } + + //! In-place version of get_load_cimg() + CImg& load_cimg(std::FILE *const file, const char *const filename=0, const char axis='v', const char align='p') { + return get_load_cimg(file,filename,axis,align).swap(*this); + } + + //! In-place version of get_load_cimg() + CImg& load_cimg(const char *const filename, const char axis='v', const char align='p') { + return get_load_cimg(filename,axis,align).swap(*this); + } + + //! Function that loads the image for other file formats that are not natively handled by CImg. + //! This is the case for all compressed image formats (GIF,PNG,JPG,TIF,...). + static CImg get_load_imagemagick(const char *const filename) { + static bool first_time = true; + char command[1024], filetmp[512]; + if (first_time) { std::srand((unsigned int)::time(0)); first_time = false; } + std::FILE *file = 0; + do { + std::sprintf(filetmp,"%s%sCImg%.4d.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",std::rand()%10000); + if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); + } while (file); + std::sprintf(command,"%s \"%s\" %s",cimg::imagemagick_path(),filename,filetmp); + cimg::system(command,cimg::imagemagick_path()); + if (!(file = std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException("CImg<%s>::get_load_imagemagick() : Failed to open image '%s'.\n\n" + "Path of 'ImageMagick's convert' : \"%s\"\n" + "Path of temporary filename : \"%s\"", + pixel_type(),filename,cimg::imagemagick_path(),filetmp); + } else cimg::fclose(file); + const CImg dest = CImg::get_load_pnm(filetmp); + std::remove(filetmp); + return dest; + } + + //! In-place version of get_load_imagemagick() + CImg& load_imagemagick(const char *const filename) { + return get_load_imagemagick(filename).swap(*this); + } + + //! Function that loads the image for other file formats that are not natively handled by CImg. + //! This is the case for all compressed image formats (GIF,PNG,JPG,TIF,...). + static CImg get_load_graphicsmagick(const char *const filename) { + static bool first_time = true; + char command[1024], filetmp[512]; + if (first_time) { std::srand((unsigned int)::time(0)); first_time = false; } + std::FILE *file = 0; + do { + std::sprintf(filetmp,"%s%sCImg%.4d.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",std::rand()%10000); + if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); + } while (file); + std::sprintf(command,"%s convert \"%s\" %s",cimg::graphicsmagick_path(),filename,filetmp); + cimg::system(command,cimg::graphicsmagick_path()); + if (!(file = std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException("CImg<%s>::get_load_graphicsmagick() : Failed to open image '%s'.\n\n" + "Path of 'GraphicsMagick's gm' : \"%s\"\n" + "Path of temporary filename : \"%s\"", + pixel_type(),filename,cimg::graphicsmagick_path(),filetmp); + } else cimg::fclose(file); + const CImg dest = CImg::get_load_pnm(filetmp); + std::remove(filetmp); + return dest; + } + + //! In-place version of get_load_graphicsmagick() + CImg& load_graphicsmagick(const char *const filename) { + return get_load_graphicsmagick(filename).swap(*this); + } + + //! Function that loads the image for other file formats that are not natively handled by CImg. + //! This is the case for all compressed image formats (GIF,PNG,JPG,TIF,...). + static CImg get_load_other(const char *const filename) { + CImg res; + const unsigned int odebug = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { res.load_magick(filename); } + catch (CImgException&) { + try { res.load_imagemagick(filename); } + catch (CImgException&) { + try { res.load_graphicsmagick(filename); } + catch (CImgException&) { + res.assign(); + } + } + } + cimg::exception_mode()=odebug; + if (res.is_empty()) + throw CImgIOException("CImg<%s>::get_load_other() : Failed to open image '%s'.\n" + "Check you have either the ImageMagick or GraphicsMagick package installed.", + pixel_type(),filename); + return res; + } + + //! In-place version of get_load_graphicsmagick() + CImg& load_other(const char *const filename) { + return get_load_other(filename).swap(*this); + } + + //! Load an image from a Dicom file (need '(X)Medcon' : http://xmedcon.sourceforge.net ) + static CImg get_load_dicom(const char *const filename) { + static bool first_time = true; + char command[1024], filetmp[512], body[512]; + if (first_time) { std::srand((unsigned int)::time(0)); first_time = false; } + cimg::fclose(cimg::fopen(filename,"r")); + std::FILE *file; + do { + std::sprintf(filetmp,"CImg%.4d.hdr",std::rand()%10000); + if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); + } while (file); + std::sprintf(command,"%s -w -c anlz -o %s -f %s",cimg::medcon_path(),filetmp,filename); + cimg::system(command); + cimg::filename_split(filetmp,body); + std::sprintf(command,"m000-%s.hdr",body); + file = std::fopen(command,"rb"); + if (!file) { + throw CImgIOException("CImg<%s>::get_load_dicom() : Failed to open image '%s'.\n\n" + "Path of 'medcon' : \"%s\"\n" + "Path of temporary filename : \"%s\"", + pixel_type(),filename,cimg::medcon_path,filetmp); + } else cimg::fclose(file); + const CImg dest = CImg::get_load_analyze(command); + std::remove(command); + std::sprintf(command,"m000-%s.img",body); + std::remove(command); + return dest; + } + + //! In-place version of get_load_dicom() + CImg& load_dicom(const char *const filename) { + return get_load_dicom(filename).swap(*this); + } + + //! Load OFF files (GeomView 3D object files) + template + static CImg get_load_off(const char *const filename, CImgList& primitives, CImgList& colors, + const bool invert_faces=false) { + std::FILE *file=cimg::fopen(filename,"r"); + unsigned int nb_points=0, nb_triangles=0; + int err; + if ((err = std::fscanf(file,"OFF%u%u%*[^\n]",&nb_points,&nb_triangles))!=2) { + cimg::fclose(file); + throw CImgIOException("CImg<%s>::get_load_off() : File '%s' is not a valid OFF file.",pixel_type(),filename); + } + + // Read points data + CImg points(nb_points,3); + float X=0,Y=0,Z=0; + cimg_forX(points,l) { + if ((err = std::fscanf(file,"%f%f%f%*[^\n]",&X,&Y,&Z))!=3) { + cimg::fclose(file); + throw CImgIOException("CImg<%s>::get_load_off() : File '%s', cannot read point %u.\n",pixel_type(),filename,l); + } + points(l,0) = (T)X; points(l,1) = (T)Y; points(l,2) = (T)Z; + } + + // Read primitive data + primitives.assign(); + colors.assign(); + bool stopflag = false; + while (!stopflag) { + unsigned int prim=0, i0=0, i1=0, i2=0, i3=0; + char s_colors[256] = {'\0'}; + if ((err = std::fscanf(file,"%u",&prim))!=1) stopflag=true; + else switch (prim) { + case 3: { + if ((err = std::fscanf(file,"%u%u%u%255[^\n]",&i0,&i1,&i2,s_colors))<3) stopflag = true; + else { + float c0=0.5, c1=0.5, c2=0.5; + std::sscanf(s_colors,"%f%f%f",&c0,&c1,&c2); + if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2)); + else primitives.insert(CImg::vector(i0,i2,i1)); + colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + } + } break; + case 4: { + if ((err = std::fscanf(file,"%u%u%u%u%255[^\n]",&i0,&i1,&i2,&i3,s_colors))<4) stopflag = true; + else { + float c0=0.5, c1=0.5, c2=0.5; + std::sscanf(s_colors,"%f%f%f",&c0,&c1,&c2); + if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2,i3)); + else primitives.insert(CImg::vector(i0,i3,i2,i1)); + colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); + } + } break; + default: stopflag = true; + } + } + cimg::fclose(file); + cimg::warn(primitives.size!=nb_triangles, + "CImg<%s>::get_load_off() : File '%s' contained %u triangles instead of %u as claimed in the header.", + pixel_type(),filename,primitives.size,nb_triangles); + return points; + } + + //! In-place version of get_load_off() + template + CImg& load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { + return get_load_off(filename,primitives,colors,invert_faces).swap(*this); + } + + //! Save the image as a file. + /** + The used file format is defined by the file extension in the filename \p filename.\n + Parameter \p number can be used to add a 6-digit number to the filename before saving.\n + If \p normalize is true, a normalized version of the image (between [0,255]) is saved. + **/ + const CImg& save(const char *const filename, const int number=-1) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename); + if (!filename) throw CImgArgumentException("CImg<%s>::save() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + const char *ext = cimg::filename_split(filename); + char nfilename[1024]; + const char *const fn = (number>=0)?cimg::filename_number(filename,number,6,nfilename):filename; + if (!cimg::strncasecmp(ext,"asc",3)) return save_ascii(fn); + if (!cimg::strncasecmp(ext,"dlm",3) || + !cimg::strncasecmp(ext,"txt",3)) return save_dlm(fn); + if (!cimg::strncasecmp(ext,"inr",3)) return save_inr(fn); + if (!cimg::strncasecmp(ext,"hdr",3)) return save_analyze(fn); + if (!cimg::strncasecmp(ext,"dcm",3)) return save_dicom(fn); + if (!cimg::strncasecmp(ext,"pan",3)) return save_pandore(fn); + if (!cimg::strncasecmp(ext,"bmp",3)) return save_bmp(fn); + if (!cimg::strncasecmp(ext,"png",3)) return save_png(fn); + if (!cimg::strncasecmp(ext,"tif",3)) return save_tiff(fn); + if (!cimg::strncasecmp(ext,"jpg",3) || + !cimg::strncasecmp(ext,"jpeg",4)) return save_jpeg(fn); + if (!cimg::strncasecmp(ext,"rgba",4)) return save_rgba(fn); + if (!cimg::strncasecmp(ext,"rgb",3)) return save_rgb(fn); + if (!cimg::strncasecmp(ext,"raw",3)) return save_raw(fn); + if (!cimg::strncasecmp(ext,"cimg",4) || ext[0]=='\0') return save_cimg(fn); + if (!cimg::strncasecmp(ext,"pgm",3) || + !cimg::strncasecmp(ext,"ppm",3) || + !cimg::strncasecmp(ext,"pnm",3)) return save_pnm(fn); + if (!cimg::strncasecmp(ext,"yuv",3)) return save_yuv(fn,true); + return save_other(fn); + } + + //! Save the image as an ASCII file (ASCII Raw + simple header). + const CImg& save_ascii(std::FILE *const file, const char *const filename=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_ascii() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_ascii() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + std::fprintf(nfile,"%u %u %u %u\n",width,height,depth,dim); + const T* ptrs = data; + cimg_forYZV(*this,y,z,v) { + cimg_forX(*this,x) std::fprintf(nfile,"%g ",(double)*(ptrs++)); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as an ASCII file (ASCII Raw + simple header). + const CImg& save_ascii(const char *const filename) const { + return save_ascii(0,filename); + } + + //! Save the image as a DLM file. + const CImg& save_dlm(std::FILE *const file, const char *const filename=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + cimg::warn(depth>1, + "CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p) is volumetric. Pixel values along Z will be unrolled (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + cimg::warn(dim>1, + "CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p) is multispectral. Pixel values along V will be unrolled (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + const T* ptrs = data; + cimg_forYZV(*this,y,z,v) { + cimg_forX(*this,x) std::fprintf(nfile,"%g%s",(double)*(ptrs++),(x==(int)width-1)?"":","); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a DLM file. + const CImg& save_dlm(const char *const filename) const { + return save_dlm(0,filename); + } + + //! Save the image as a PNM file. + const CImg& save_pnm(std::FILE *const file, const char *const filename=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + const CImgStats st(*this,false); + cimg::warn(depth>1, + "CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + cimg::warn(dim>3, + "CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p) is multispectral. Only the three first channels will be saved (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + const double stmin = st.min, stmax = st.max; + cimg::warn(stmin<0 || stmax>65535,"CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p) has pixel values in [%g,%g]. Probable type overflow (file '%s').",pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptrR = ptr(0,0,0,0), + *ptrG = (dim>=2)?ptr(0,0,0,1):ptrR, + *ptrB = (dim>=3)?ptr(0,0,0,2):ptrR; + const unsigned int buf_size = width*height*(dim==1?1:3); + + std::fprintf(nfile,"P%c\n# CREATOR: CImg : Original size=%ux%ux%ux%u\n%u %u\n%u\n", + (dim==1?'5':'6'),width,height,depth,dim,width,height,(st.max)<256?255:65535); + + switch(dim) { + case 1: { + if ((st.max)<256) { // Binary PGM 8 bits + unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) *(xptrd++) = (unsigned char)*(ptrR++); + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } else { // Binary PGM 16 bits + unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) *(xptrd++) = (unsigned short)*(ptrR++); + if (!cimg::endian()) cimg::endian_swap(ptrd,buf_size); + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } + } break; + default: { + if ((st.max)<256) { // Binary PPM 8 bits + unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) { + *(xptrd++) = (unsigned char)*(ptrR++); + *(xptrd++) = (unsigned char)*(ptrG++); + *(xptrd++) = (unsigned char)*(ptrB++); + } + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } else { // Binary PPM 16 bits + unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) { + *(xptrd++) = (unsigned short)*(ptrR++); + *(xptrd++) = (unsigned short)*(ptrG++); + *(xptrd++) = (unsigned short)*(ptrB++); + } + if (!cimg::endian()) cimg::endian_swap(ptrd,buf_size); + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } + } break; + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a PNM file. + const CImg& save_pnm(const char *const filename) const { + return save_pnm(0,filename); + } + + //! Save an image as a Dicom file (need '(X)Medcon' : http://xmedcon.sourceforge.net ) + const CImg& save_dicom(const char *const filename) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_dicom() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename); + if (!filename) throw CImgArgumentException("CImg<%s>::save_dicom() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + static bool first_time = true; + char command[1024], filetmp[512], body[512]; + if (first_time) { std::srand((unsigned int)::time(0)); first_time = false; } + std::FILE *file; + do { + std::sprintf(filetmp,"CImg%.4d.hdr",std::rand()%10000); + if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); + } while (file); + save_analyze(filetmp); + std::sprintf(command,"%s -w -c dicom -o %s -f %s",cimg::medcon_path(),filename,filetmp); + cimg::system(command); + std::remove(filetmp); + cimg::filename_split(filetmp,body); + std::sprintf(filetmp,"%s.img",body); + std::remove(filetmp); + std::sprintf(command,"m000-%s",filename); + file = std::fopen(command,"rb"); + if (!file) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException("CImg<%s>::save_dicom() : Failed to save image '%s'.\n\n" + "Path of 'medcon' : \"%s\"\n" + "Path of temporary filename : \"%s\"", + pixel_type(),filename,cimg::medcon_path(),filetmp); + } else cimg::fclose(file); + std::rename(command,filename); + return *this; + } + + //! Save the image as an ANALYZE7.5 file. + const CImg& save_analyze(const char *const filename, const float *const voxsize=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_analyze() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename); + if (!filename) throw CImgArgumentException("CImg<%s>::save_analyze() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + std::FILE *file; + char header[348],hname[1024],iname[1024]; + const char *ext = cimg::filename_split(filename); + short datatype=-1; + std::memset(header,0,348); + if (!ext[0]) { std::sprintf(hname,"%s.hdr",filename); std::sprintf(iname,"%s.img",filename); } + if (!cimg::strncasecmp(ext,"hdr",3)) { + std::strcpy(hname,filename); std::strcpy(iname,filename); std::sprintf(iname+cimg::strlen(iname)-3,"img"); + } + if (!cimg::strncasecmp(ext,"img",3)) { + std::strcpy(hname,filename); std::strcpy(iname,filename); std::sprintf(hname+cimg::strlen(iname)-3,"hdr"); + } + ((int*)(header))[0] = 348; + std::sprintf(header+4,"CImg"); + std::sprintf(header+14," "); + ((short*)(header+36))[0] = 4096; + ((char*)(header+38))[0] = 114; + ((short*)(header+40))[0] = 4; + ((short*)(header+40))[1] = width; + ((short*)(header+40))[2] = height; + ((short*)(header+40))[3] = depth; + ((short*)(header+40))[4] = dim; + if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"unsigned long")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"long")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16; + if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64; + if (datatype<0) + throw CImgIOException("CImg<%s>::save_analyze() : Cannot save image '%s' since pixel type (%s)" + "is not handled in Analyze7.5 specifications.\n", + pixel_type(),filename,pixel_type()); + ((short*)(header+70))[0] = datatype; + ((short*)(header+72))[0] = sizeof(T); + ((float*)(header+112))[0] = 1; + ((float*)(header+76))[0] = 0; + if (voxsize) { + ((float*)(header+76))[1] = voxsize[0]; + ((float*)(header+76))[2] = voxsize[1]; + ((float*)(header+76))[3] = voxsize[2]; + } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1; + file = cimg::fopen(hname,"wb"); + cimg::fwrite(header,348,file); + cimg::fclose(file); + file = cimg::fopen(iname,"wb"); + cimg::fwrite(data,size(),file); + cimg::fclose(file); + return *this; + } + + //! Save the image as a CImg file (Binary RAW + simple header) + const CImg& save_cimg(std::FILE *const file, const char *const filename=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_cimg() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_cimg() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + CImgList tmp(1); + tmp[0].width = width; + tmp[0].height = height; + tmp[0].depth = depth; + tmp[0].dim = dim; + tmp[0].data = data; + tmp.save_cimg(file,filename); + tmp[0].width = tmp[0].height = tmp[0].depth = tmp[0].dim = 0; + tmp[0].data = 0; + return *this; + } + + //! Save the image as a CImg file (Binary RAW + simple header) + const CImg& save_cimg(const char *const filename) const { + return save_cimg(0,filename); + } + + //! Save the image as a RAW file + const CImg& save_raw(std::FILE *const file, const char *const filename=0, const bool multiplexed=false) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_raw() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_raw() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (!multiplexed) cimg::fwrite(data,size(),nfile); + else { + CImg buf(dim); + cimg_forXYZ(*this,x,y,z) { + cimg_forV(*this,k) buf[k] = (*this)(x,y,z,k); + cimg::fwrite(buf.data,dim,nfile); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a RAW file + const CImg& save_raw(const char *const filename=0, const bool multiplexed=false) const { + return save_raw(0,filename,multiplexed); + } + + //! Save the image using ImageMagick's convert. + /** Function that saves the image for other file formats that are not natively handled by CImg, + using the tool 'convert' from the ImageMagick package.\n + This is the case for all compressed image formats (GIF,PNG,JPG,TIF,...). You need to install + the ImageMagick package in order to get + this function working properly (see http://www.imagemagick.org ). + **/ + const CImg& save_imagemagick(const char *const filename, const unsigned int quality=100) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_imagemagick() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s')", + pixel_type(),width,height,depth,dim,data,filename); + if (!filename) throw CImgArgumentException("CImg<%s>::save_imagemagick() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + static bool first_time = true; + char command[512],filetmp[512]; + if (first_time) { std::srand((unsigned int)::time(0)); first_time = false; } + std::FILE *file; + do { + if (dim==1) std::sprintf(filetmp,"%s%sCImg%.4d.pgm",cimg::temporary_path(),cimg_OS==2?"\\":"/",std::rand()%10000); + else std::sprintf(filetmp,"%s%sCImg%.4d.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",std::rand()%10000); + if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); + } while (file); + save_pnm(filetmp); + std::sprintf(command,"%s -quality %u%% %s \"%s\"",cimg::imagemagick_path(),quality,filetmp,filename); + cimg::system(command); + file = std::fopen(filename,"rb"); + if (!file) throw CImgIOException("CImg<%s>::save_imagemagick() : Failed to save image '%s'.\n\n" + "Path of 'convert' : \"%s\"\n" + "Path of temporary filename : \"%s\"\n", + pixel_type(),filename,cimg::imagemagick_path(),filetmp); + if (file) cimg::fclose(file); + std::remove(filetmp); + return *this; + } + + //! Save the image using GraphicsMagick's gm. + /** Function that saves the image for other file formats that are not natively handled by CImg, + using the tool 'gm' from the GraphicsMagick package.\n + This is the case for all compressed image formats (GIF,PNG,JPG,TIF,...). You need to install + the GraphicsMagick package in order to get + this function working properly (see http://www.graphicsmagick.org ). + **/ + const CImg& save_graphicsmagick(const char *const filename, const unsigned int quality=100) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_graphicsmagick() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s')", + pixel_type(),width,height,depth,dim,data,filename); + if (!filename) throw CImgArgumentException("CImg<%s>::save_graphicsmagick() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + static bool first_time = true; + char command[512],filetmp[512]; + if (first_time) { std::srand((unsigned int)::time(0)); first_time = false; } + std::FILE *file; + do { + if (dim==1) std::sprintf(filetmp,"%s%sCImg%.4d.pgm",cimg::temporary_path(),cimg_OS==2?"\\":"/",std::rand()%10000); + else std::sprintf(filetmp,"%s%sCImg%.4d.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",std::rand()%10000); + if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); + } while (file); + save_pnm(filetmp); + std::sprintf(command,"%s -quality %u%% %s \"%s\"",cimg::graphicsmagick_path(),quality,filetmp,filename); + cimg::system(command); + file = std::fopen(filename,"rb"); + if (!file) throw CImgIOException("CImg<%s>::save_graphicsmagick() : Failed to save image '%s'.\n\n" + "Path of 'gm' : \"%s\"\n" + "Path of temporary filename : \"%s\"\n", + pixel_type(),filename,cimg::graphicsmagick_path(),filetmp); + if (file) cimg::fclose(file); + std::remove(filetmp); + return *this; + } + + const CImg& save_other(const char *const filename, const unsigned int quality=100) const { + const unsigned int odebug = cimg::exception_mode(); + bool is_saved = true; + cimg::exception_mode() = 0; + try { save_magick(filename); } + catch (CImgException&) { + try { save_imagemagick(filename,quality); } + catch (CImgException&) { + try { save_graphicsmagick(filename,quality); } + catch (CImgException&) { + is_saved = false; + } + } + } + cimg::exception_mode() = odebug; + if (!is_saved) throw CImgIOException("CImg<%s>::save_other() : File '%s' cannot be saved.\n" + "Check you have either the ImageMagick or GraphicsMagick package installed.", + pixel_type(),filename); + return *this; + } + + //! Save the image as an INRIMAGE-4 file. + const CImg& save_inr(std::FILE *const file, const char *const filename=0, const float *const voxsize=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_inr() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!filename) throw CImgArgumentException("CImg<%s>::save_inr() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + int inrpixsize=-1; + const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } + if (!cimg::strcasecmp(pixel_type(),"char")) { inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; } + if (!cimg::strcasecmp(pixel_type(),"short")) { inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; } + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; } + if (!cimg::strcasecmp(pixel_type(),"int")) { inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; } + if (!cimg::strcasecmp(pixel_type(),"float")) { inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; } + if (!cimg::strcasecmp(pixel_type(),"double")) { inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; } + if (inrpixsize<=0) throw CImgIOException("CImg<%s>::save_inr() : Don't know how to save images of '%s'",pixel_type(),pixel_type()); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + char header[257]; + int err = std::sprintf(header,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",width,height,depth,dim); + if (voxsize) err += std::sprintf(header+err,"VX=%g\nVY=%g\nVZ=%g\n",voxsize[0],voxsize[1],voxsize[2]); + err += std::sprintf(header+err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endian()?"sun":"decm"); + std::memset(header+err,'\n',252-err); + std::memcpy(header+252,"##}\n",4); + cimg::fwrite(header,256,nfile); + cimg_forXYZ(*this,x,y,z) cimg_forV(*this,k) cimg::fwrite(&((*this)(x,y,z,k)),1,nfile); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as an INRIMAGE-4 file. + const CImg& save_inr(const char *const filename, const float *const voxsize=0) const { + return save_inr(0,filename,voxsize); + } + +#define cimg_save_pandore_case(sy,sz,sv,stype,id) \ + if (!saved && (sy?(sy==height):true) && (sz?(sz==depth):true) && (sv?(sv==dim):true) && !strcmp(stype,pixel_type())) { \ + unsigned int *iheader = (unsigned int*)(header+12); \ + nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ + cimg::fwrite(header,36,nfile); \ + cimg::fwrite(dims,nbdims,nfile); \ + if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ + unsigned char *buffer = new unsigned char[size()]; \ + const T *ptrs = ptr(); \ + cimg_foroff(*this,off) *(buffer++)=(unsigned char)(*(ptrs++)); \ + buffer-=size(); \ + cimg::fwrite(buffer,size(),nfile); \ + delete[] buffer; \ + } \ + if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ + unsigned long *buffer = new unsigned long[size()]; \ + const T *ptrs = ptr(); \ + cimg_foroff(*this,off) *(buffer++)=(long)(*(ptrs++)); \ + buffer-=size(); \ + cimg::fwrite(buffer,size(),nfile); \ + delete[] buffer; \ + } \ + if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \ + float *buffer = new float[size()]; \ + const T *ptrs = ptr(); \ + cimg_foroff(*this,off) *(buffer++)=(float)(*(ptrs++)); \ + buffer-=size(); \ + cimg::fwrite(buffer,size(),nfile); \ + delete[] buffer; \ + } \ + saved = true; \ + } + + unsigned int _save_pandore_header_length(unsigned int id,unsigned int *dims,const unsigned int colorspace=0) const { + unsigned int nbdims=0; + if (id==2 || id==3 || id==4) { dims[0]=1; dims[1]=width; nbdims=2; } + if (id==5 || id==6 || id==7) { dims[0]=1; dims[1]=height; dims[2]=width; nbdims=3; } + if (id==8 || id==9 || id==10) { dims[0]=dim; dims[1]=depth; dims[2]=height; dims[3]=width; nbdims=4; } + if (id==16 || id==17 || id==18) { dims[0]=3; dims[1]=height; dims[2]=width; dims[3]=colorspace; nbdims=4; } + if (id==19 || id==20 || id==21) { dims[0]=3; dims[1]=depth; dims[2]=height; dims[3]=width; dims[4]=colorspace; nbdims=5; } + if (id==22 || id==23 || id==25) { dims[0]=dim; dims[1]=width; nbdims=2; } + if (id==26 || id==27 || id==29) { dims[0]=dim; dims[1]=height; dims[2]=width; nbdims=3; } + if (id==30 || id==31 || id==33) { dims[0]=dim; dims[1]=depth; dims[2]=height; dims[3]=width; nbdims=4; } + return nbdims; + } + + //! Save the image as a PANDORE-5 file. + const CImg& save_pandore(std::FILE *const file, const char *const filename=0, const unsigned int colorspace=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_pandore() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_pandore() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, + 0,0,0,0, + 'C','I','m','g',0,0,0,0,0, + 'N','o',' ','d','a','t','e',0,0,0, + 0 }; + unsigned int nbdims,dims[5]; + bool saved=false; + cimg_save_pandore_case(1,1,1,"unsigned char",2); + cimg_save_pandore_case(1,1,1,"char",3); + cimg_save_pandore_case(1,1,1,"short",3); + cimg_save_pandore_case(1,1,1,"unsigned short",3); + cimg_save_pandore_case(1,1,1,"unsigned int",3); + cimg_save_pandore_case(1,1,1,"int",3); + cimg_save_pandore_case(1,1,1,"unsigned long",4); + cimg_save_pandore_case(1,1,1,"long",3); + cimg_save_pandore_case(1,1,1,"float",4); + cimg_save_pandore_case(1,1,1,"double",4); + + cimg_save_pandore_case(0,1,1,"unsigned char",5); + cimg_save_pandore_case(0,1,1,"char",6); + cimg_save_pandore_case(0,1,1,"short",6); + cimg_save_pandore_case(0,1,1,"unsigned short",6); + cimg_save_pandore_case(0,1,1,"unsigned int",6); + cimg_save_pandore_case(0,1,1,"int",6); + cimg_save_pandore_case(0,1,1,"unsigned long",7); + cimg_save_pandore_case(0,1,1,"long",6); + cimg_save_pandore_case(0,1,1,"float",7); + cimg_save_pandore_case(0,1,1,"double",7); + + cimg_save_pandore_case(0,0,1,"unsigned char",8); + cimg_save_pandore_case(0,0,1,"char",9); + cimg_save_pandore_case(0,0,1,"short",9); + cimg_save_pandore_case(0,0,1,"unsigned short",9); + cimg_save_pandore_case(0,0,1,"unsigned int",9); + cimg_save_pandore_case(0,0,1,"int",9); + cimg_save_pandore_case(0,0,1,"unsigned long",10); + cimg_save_pandore_case(0,0,1,"long",9); + cimg_save_pandore_case(0,0,1,"float",10); + cimg_save_pandore_case(0,0,1,"double",10); + + cimg_save_pandore_case(0,1,3,"unsigned char",16); + cimg_save_pandore_case(0,1,3,"char",17); + cimg_save_pandore_case(0,1,3,"short",17); + cimg_save_pandore_case(0,1,3,"unsigned short",17); + cimg_save_pandore_case(0,1,3,"unsigned int",17); + cimg_save_pandore_case(0,1,3,"int",17); + cimg_save_pandore_case(0,1,3,"unsigned long",18); + cimg_save_pandore_case(0,1,3,"long",17); + cimg_save_pandore_case(0,1,3,"float",18); + cimg_save_pandore_case(0,1,3,"double",18); + + cimg_save_pandore_case(0,0,3,"unsigned char",19); + cimg_save_pandore_case(0,0,3,"char",20); + cimg_save_pandore_case(0,0,3,"short",20); + cimg_save_pandore_case(0,0,3,"unsigned short",20); + cimg_save_pandore_case(0,0,3,"unsigned int",20); + cimg_save_pandore_case(0,0,3,"int",20); + cimg_save_pandore_case(0,0,3,"unsigned long",21); + cimg_save_pandore_case(0,0,3,"long",20); + cimg_save_pandore_case(0,0,3,"float",21); + cimg_save_pandore_case(0,0,3,"double",21); + + cimg_save_pandore_case(1,1,0,"unsigned char",22); + cimg_save_pandore_case(1,1,0,"char",23); + cimg_save_pandore_case(1,1,0,"short",23); + cimg_save_pandore_case(1,1,0,"unsigned short",23); + cimg_save_pandore_case(1,1,0,"unsigned int",23); + cimg_save_pandore_case(1,1,0,"int",23); + cimg_save_pandore_case(1,1,0,"unsigned long",25); + cimg_save_pandore_case(1,1,0,"long",23); + cimg_save_pandore_case(1,1,0,"float",25); + cimg_save_pandore_case(1,1,0,"double",25); + + cimg_save_pandore_case(0,1,0,"unsigned char",26); + cimg_save_pandore_case(0,1,0,"char",27); + cimg_save_pandore_case(0,1,0,"short",27); + cimg_save_pandore_case(0,1,0,"unsigned short",27); + cimg_save_pandore_case(0,1,0,"unsigned int",27); + cimg_save_pandore_case(0,1,0,"int",27); + cimg_save_pandore_case(0,1,0,"unsigned long",29); + cimg_save_pandore_case(0,1,0,"long",27); + cimg_save_pandore_case(0,1,0,"float",29); + cimg_save_pandore_case(0,1,0,"double",29); + + cimg_save_pandore_case(0,0,0,"unsigned char",30); + cimg_save_pandore_case(0,0,0,"char",31); + cimg_save_pandore_case(0,0,0,"short",31); + cimg_save_pandore_case(0,0,0,"unsigned short",31); + cimg_save_pandore_case(0,0,0,"unsigned int",31); + cimg_save_pandore_case(0,0,0,"int",31); + cimg_save_pandore_case(0,0,0,"unsigned long",33); + cimg_save_pandore_case(0,0,0,"long",31); + cimg_save_pandore_case(0,0,0,"float",33); + cimg_save_pandore_case(0,0,0,"double",33); + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a PANDORE-5 file. + const CImg& save_pandore(const char *const filename=0, const unsigned int colorspace=0) const { + return save_pandore(0,filename,colorspace); + } + + //! Save the image as a YUV video sequence file + const CImg& save_yuv(std::FILE *const file, const char *const filename=0, const bool rgb2yuv=true) const { + CImgList(*this).save_yuv(file,filename,rgb2yuv); + return *this; + } + + //! Save the image as a YUV video sequence file + const CImg& save_yuv(const char *const filename, const bool rgb2yuv=true) const { + return save_yuv(0,filename,rgb2yuv); + } + + //! Save the image as a BMP file + const CImg& save_bmp(std::FILE *const file, const char *const filename=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + cimg::warn(depth>1, + "CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + cimg::warn(dim>3, + "CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p) is multispectral. Only the three first channels will be saved (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + unsigned char header[54]={0}, align_buf[4]={0}; + const unsigned int + align = (4-(3*width)%4)%4, + buf_size = (3*width+align)*dimy(), + file_size = 54+buf_size; + header[0] = 'B'; header[1] = 'M'; + header[0x02]=file_size&0xFF; header[0x03]=(file_size>>8)&0xFF; + header[0x04]=(file_size>>16)&0xFF; header[0x05]=(file_size>>24)&0xFF; + header[0x0A]=0x36; + header[0x0E]=0x28; + header[0x12]=width&0xFF; header[0x13]=(width>>8)&0xFF; + header[0x14]=(width>>16)&0xFF; header[0x15]=(width>>24)&0xFF; + header[0x16]=height&0xFF; header[0x17]=(height>>8)&0xFF; + header[0x18]=(height>>16)&0xFF; header[0x19]=(height>>24)&0xFF; + header[0x1A]=1; header[0x1B]=0; + header[0x1C]=24; header[0x1D]=0; + header[0x22]=buf_size&0xFF; header[0x23]=(buf_size>>8)&0xFF; + header[0x24]=(buf_size>>16)&0xFF; header[0x25]=(buf_size>>24)&0xFF; + header[0x27]=0x1; header[0x2B]=0x1; + cimg::fwrite(header,54,nfile); + + const T + *pR = ptr(0,height-1,0,0), + *pG = (dim>=2)?ptr(0,height-1,0,1):pR, + *pB = (dim>=3)?ptr(0,height-1,0,2):pR; + + cimg_forY(*this,y) { + cimg_forX(*this,x) { + std::fputc((unsigned char)(*(pB++)),nfile); + std::fputc((unsigned char)(*(pG++)),nfile); + std::fputc((unsigned char)(*(pR++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + pR-=2*width; pG-=2*width; pB-=2*width; + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a BMP file + const CImg& save_bmp(const char *const filename) const { + return save_bmp(0,filename); + } + + //! Save an image to a PNG file. + // Most of this function has been written by Eric Fausett + /** + \param filename = name of the png image file to save + \return *this + \note The png format specifies a variety of possible data formats. Grey scale, Grey + scale with Alpha, RGB color, RGB color with Alpha, and Palletized color are supported. + Per channel bit depths of 1, 2, 4, 8, and 16 are natively supported. The + type of file saved depends on the number of channels in the CImg file. If there is 4 or more + channels, the image will be saved as an RGB color with Alpha image using the bottom 4 channels. + If there are 3 channels, the saved image will be an RGB color image. If 2 channels then the + image saved will be Grey scale with Alpha, and if 1 channel will be saved as a Grey scale + image. + **/ + const CImg& save_png(std::FILE *const file, const char *const filename=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_png() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!filename) throw CImgArgumentException("CImg<%s>::save_png() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + cimg::warn(depth>1, + "CImg<%s>::save_png() : Instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); +#ifndef cimg_use_png + if (!file) return save_other(filename); + else throw CImgIOException("CImg<%s>::save_png() : Cannot save a PNG image in a *FILE output. Use libpng instead.", + pixel_type()); +#else + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + + // Setup PNG structures for write + png_voidp user_error_ptr=0; + png_error_ptr user_error_fn=0, user_warning_fn=0; + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + user_error_ptr, user_error_fn, user_warning_fn); + if(!png_ptr){ + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::save_png() : File '%s', error when initializing 'png_ptr' data structure.", + pixel_type(),filename?filename:"(unknown)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if(!info_ptr){ + png_destroy_write_struct(&png_ptr,(png_infopp)0); + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::save_png() : File '%s', error when initializing 'info_ptr' data structure.", + pixel_type(),filename?filename:"(unknown)"); + } + if (setjmp(png_jmpbuf(png_ptr))){ + png_destroy_write_struct(&png_ptr, &info_ptr); + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::save_png() : File '%s', unknown fatal error.", + pixel_type(),filename?filename:"(unknown)"); + } + + png_init_io(png_ptr, nfile); + png_uint_32 width = dimx(); + png_uint_32 height = dimy(); + const CImgStats stats(*this,false); + const float vmin = (float)stats.min, vmax = (float)stats.max; + const int bit_depth = (vmin<0 || vmax>=256)?16:8; + int color_type; + switch (dimv()) { + case 1: color_type = PNG_COLOR_TYPE_GRAY; break; + case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; + case 3: color_type = PNG_COLOR_TYPE_RGB; break; + default: color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } + const int interlace_type = PNG_INTERLACE_NONE; + const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT; + const int filter_method = PNG_FILTER_TYPE_DEFAULT; + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, + compression_type, filter_method); + png_write_info(png_ptr, info_ptr); + const int byte_depth = bit_depth>>3; + const int numChan = dimv()>4?4:dimv(); + const int pixel_bit_depth_flag = numChan * (bit_depth-1); + + // Allocate Memory for Image Save and Fill pixel data + png_bytep *imgData = new png_byte*[height]; + for(unsigned int row=0; row(imgData[y]); + cimg_forX(*this,x) *(ptrs++) = (unsigned short)*(pC0++); + } + } break; + case 30: { // Gray w/ Alpha 16-bit + const T *pC1 = ptr(0,0,0,1); + cimg_forY(*this,y){ + unsigned short *ptrs = reinterpret_cast(imgData[y]); + cimg_forX(*this,x) { + *(ptrs++) = (unsigned short)*(pC0++); + *(ptrs++) = (unsigned short)*(pC1++); + } + } + } break; + case 45: { // RGB 16-bit + const T *pC1 = ptr(0,0,0,1); + const T *pC2 = ptr(0,0,0,2); + cimg_forY(*this,y) { + unsigned short *ptrs = reinterpret_cast(imgData[y]); + cimg_forX(*this,x) { + *(ptrs++) = (unsigned short)*(pC0++); + *(ptrs++) = (unsigned short)*(pC1++); + *(ptrs++) = (unsigned short)*(pC2++); + } + } + } break; + case 60: { // RGB w/ Alpha 16-bit + const T *pC1 = ptr(0,0,0,1); + const T *pC2 = ptr(0,0,0,2); + const T *pC3 = ptr(0,0,0,3); + cimg_forY(*this,y) { + unsigned short *ptrs = reinterpret_cast(imgData[y]); + cimg_forX(*this,x) { + *(ptrs++) = (unsigned short)*(pC0++); + *(ptrs++) = (unsigned short)*(pC1++); + *(ptrs++) = (unsigned short)*(pC2++); + *(ptrs++) = (unsigned short)*(pC3++); + } + } + } break; + default: + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::save_png() : File '%s', unknown fatal error.", + pixel_type(),filename?filename:"(unknown)"); + break; + } + png_write_image(png_ptr, imgData); + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + // Deallocate Image Write Memory + for (unsigned int n=0; n::save_tiff() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename); + if (!filename) throw CImgArgumentException("CImg<%s>::save_tiff() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); +#ifdef cimg_use_tiff + uint32 rowsperstrip = (uint32) -1; + uint16 spp = dimv(), bpp = sizeof(T)*8; + uint16 photometric; + if (spp==3 || spp==4) + photometric = PHOTOMETRIC_RGB; + else + photometric = PHOTOMETRIC_MINISBLACK; + uint16 compression = COMPRESSION_NONE; + TIFF *out; + out = TIFFOpen(filename,"w"); + if (out) { + for (unsigned int dir=0; dirheight?height-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(out, row, 0); + tsize_t i = 0; + for (unsigned int rr=0; rr::get_save_tiff() : File '%s', an error occure while writing a strip.", + pixel_type(),filename?filename:"(FILE*)"); + } + } + _TIFFfree(buf); + } + TIFFWriteDirectory(out); + } + TIFFClose(out); + } + else throw CImgException("CImg<%s>::save_tiff() : File '%s', error while writing tiff file.", + pixel_type(),filename); +#else + return save_other(filename); +#endif + return *this; + } + + //! Save a file in JPEG format. + const CImg& save_jpeg(std::FILE *const file, const char *const filename=0, const unsigned int quality=100) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_jpeg() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_jpeg() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + cimg::warn(depth>1, + "CImg<%s>::save_jpeg() : Instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); +#ifndef cimg_use_jpeg + if (!file) return save_other(filename,quality); + else throw CImgIOException("CImg<%s>::save_jpeg() : Cannot save a JPEG image in a *FILE output. Use libjpeg instead.", + pixel_type()); +#else + + // Fill pixel buffer + unsigned char *buf; + unsigned int dimbuf=0; + J_COLOR_SPACE colortype=JCS_RGB; + switch (dim) { + case 1: { // Greyscale images + unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=1)]; + colortype = JCS_GRAYSCALE; + const T *ptr_g = ptr(); + cimg_forXY(*this,x,y) *(buf2++) = (unsigned char)*(ptr_g++); + } break; + case 2: + case 3: { // RGB images + unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=3)]; + const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,dim>2?2:0); + colortype = JCS_RGB; + cimg_forXY(*this,x,y) { + *(buf2++) = (unsigned char)*(ptr_r++); + *(buf2++) = (unsigned char)*(ptr_g++); + *(buf2++) = (unsigned char)*(ptr_b++); + } + } break; + default: { // YCMYK images + unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=4)]; + const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2), *ptr_a = ptr(0,0,0,3); + colortype = JCS_CMYK; + cimg_forXY(*this,x,y) { + *(buf2++) = (unsigned char)*(ptr_r++); + *(buf2++) = (unsigned char)*(ptr_g++); + *(buf2++) = (unsigned char)*(ptr_b++); + *(buf2++) = (unsigned char)*(ptr_a++); + } + } break; + } + + // Call libjpeg functions + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + jpeg_stdio_dest(&cinfo,nfile); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = dimbuf; + cinfo.in_color_space = colortype; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE); + jpeg_start_compress(&cinfo,TRUE); + + const unsigned int row_stride = width*dimbuf; + JSAMPROW row_pointer[1]; + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = &buf[cinfo.next_scanline*row_stride]; + jpeg_write_scanlines(&cinfo,row_pointer,1); + } + jpeg_finish_compress(&cinfo); + + delete[] buf; + if (!file) cimg::fclose(nfile); + jpeg_destroy_compress(&cinfo); + return *this; +#endif + } + + //! Save a file in JPEG format. + const CImg& save_jpeg(const char *const filename, const unsigned int quality=100) const { + return save_jpeg(0,filename,quality); + } + + //! Save the image using built-in ImageMagick++ library + const CImg& save_magick(const char *const filename) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_magick() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data); + if (!filename) throw CImgArgumentException("CImg<%s>::save_magick() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); +#ifdef cimg_use_magick + Magick::Image image(Magick::Geometry(width,height),"black"); + image.type(Magick::TrueColorType); + const T *rdata = ptr(0,0,0,0), *gdata = dim>1?ptr(0,0,0,1):rdata, *bdata = dim>2?ptr(0,0,0,2):gdata; + cimg_forXY(*this,x,y) image.pixelColor(x,y,Magick::ColorRGB(*(rdata++)/255.0,*(gdata++)/255.0,*(bdata++)/255.0)); + image.syncPixels(); + image.write(filename); +#else + throw CImgIOException("CImg<%s>::save_magick() : File '%s', Magick++ library has not been linked.", + pixel_type(),filename); +#endif + return *this; + } + + //! Save the image as a RGBA file + const CImg& save_rgba(std::FILE *const file, const char *const filename=0) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_rgba() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_rgba() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + cimg::warn(dim!=4, + "CImg<%s>::save_rgba() : Instance image (%u,%u,%u,%u,%p) has not exactly 4 channels (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const unsigned int wh = width*height; + unsigned char *buffer = new unsigned char[4*wh], *nbuffer=buffer; + const T + *ptr1 = ptr(0,0,0,0), + *ptr2 = dim>1?ptr(0,0,0,1):ptr1, + *ptr3 = dim>2?ptr(0,0,0,2):ptr1, + *ptr4 = dim>3?ptr(0,0,0,3):0; + for (unsigned int k=0; k::save_rgb() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_rgb() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + cimg::warn(dim!=3, + "CImg<%s>::save_rgb() : Instance image (%u,%u,%u,%u,%p) has not exactly 3 channels (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const unsigned int wh = width*height; + unsigned char *buffer = new unsigned char[3*wh], *nbuffer=buffer; + const T + *ptr1 = ptr(0,0,0,0), + *ptr2 = dim>1?ptr(0,0,0,1):ptr1, + *ptr3 = dim>2?ptr(0,0,0,2):ptr1; + for (unsigned int k=0; k res(40,38,1,3); + if (first_time) { + const unsigned char *ptrs = cimg::logo40x38; + T *ptr1 = res.ptr(0,0,0,0), *ptr2 = res.ptr(0,0,0,1), *ptr3 = res.ptr(0,0,0,2); + for (unsigned int off = 0; off + const CImg& save_off(std::FILE *const file, const char *const filename, + const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { + if (is_empty()) throw CImgInstanceException("CImg<%s>::save_off() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", + pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_off() : Specified filename is (null).",pixel_type()); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + std::fprintf(nfile,"OFF\n%u %u %u\n",width,primitives.size,3*primitives.size); + cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2))); + cimglist_for(primitives,l) { + const unsigned int prim = primitives[l].size(); + switch (prim) { + case 3: { + if (invert_faces) + std::fprintf(nfile,"3 %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),(unsigned int)primitives(l,2), + (float)(colors(l,0)/255.0f),(float)(colors(l,1)/255.0f),(float)(colors(l,2)/255.0f)); + else + std::fprintf(nfile,"3 %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),(unsigned int)primitives(l,1), + (float)(colors(l,0)/255.0f),(float)(colors(l,1)/255.0f),(float)(colors(l,2)/255.0f)); + } break; + case 4: { + if (invert_faces) + std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),(unsigned int)primitives(l,2),(unsigned int)primitives(l,3), + (float)(colors(l,0)/255.0f),(float)(colors(l,1)/255.0f),(float)(colors(l,2)/255.0f)); + else + std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),(unsigned int)primitives(l,2),(unsigned int)primitives(l,1), + (float)(colors(l,0)/255.0f),(float)(colors(l,1)/255.0f),(float)(colors(l,2)/255.0f)); + } break; + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save OFF files (GeomView 3D object files) + template + const CImg& save_off(const char *const filename, + const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { + return save_off(filename,primitives,colors,invert_faces); + } + + }; + + + /* + #----------------------------------------- + # + # + # + # Definition of the CImgList<> structure + # + # + # + #------------------------------------------ + */ + + //! Class representing list of images CImg. + template struct CImgList { + + //! Size of the list (number of elements inside) + unsigned int size; + + //! Allocation size of the list + unsigned int allocsize; + + //! Pointer to the first list element + CImg *data; + + //! Define a CImgList::iterator + typedef CImg* iterator; + + //! Define a CImgList::const_iterator + typedef const CImg* const_iterator; + + //! Get value type + typedef T value_type; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimglist_plugin +#include cimglist_plugin +#endif + //@} + + //------------------------------------------ + // + //! \name Constructors - Destructor - Copy + //@{ + //------------------------------------------ + + //! Default constructor + CImgList(): + size(0),allocsize(0),data(0) {} + + //! Destructor + ~CImgList() { + if (data) delete[] data; + } + + //! In-place version of the default constructor and default destructor + CImgList& assign() { + if (data) delete[] data; + size = allocsize = 0; + data = 0; + return *this; + } + + //! Equivalent to assign() (STL-compliant name) + CImgList& clear() { + return assign(); + } + + //! Copy constructor + template CImgList(const CImgList& list): + size(0),allocsize(0),data(0) { + assign(list); + } + + CImgList(const CImgList& list): + size(0),allocsize(0),data(0) { + assign(list); + } + + //! Copy constructor that create a shared object + template CImgList(const CImgList& list, const bool shared): + size(0),allocsize(0),data(0) { + assign(list,shared); + } + + CImgList(const CImgList& list, const bool shared): + size(0),allocsize(0),data(0) { + assign(list,shared); + } + + //! In-place version of the copy constructor + template CImgList& assign(const CImgList& list, const bool shared=false) { + assign(list.size); + cimglist_for(*this,l) (*this)[l].assign(list[l],shared); + return *this; + } + + //! Construct an image list containing n empty images + explicit CImgList(const unsigned int n): + size(n) { + data = new CImg[allocsize=cimg::nearest_pow2(n)]; + } + + //! In-place version of the previous constructor + CImgList& assign(const unsigned int n) { + if (n) { + if (allocsize(n<<2)) { + if (data) delete[] data; + data = new CImg[allocsize=cimg::nearest_pow2(n)]; + } + size = n; + } else return assign(); + return *this; + } + + //! Construct an image list containing n images with specified size + CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int dim=1): + size(0),allocsize(0),data(0) { + assign(n,width,height,depth,dim); + } + + //! In-place version of the previous constructor + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int dim=1) { + const unsigned int siz = width*height*depth*dim; + if (n && siz) { assign(n); cimglist_for(*this,l) data[l].assign(width,height,depth,dim); } + else return assign(); + return *this; + } + + //! Construct an image list containing n images with specified size, filled with val + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int dim, const T& val): + size(0),allocsize(0),data(0) { + assign(n,width,height,depth,dim,val); + } + + //! In-place version of the previous constructor + CImgList& assign(const unsigned int n,const unsigned int width,const unsigned int height, + const unsigned int depth, const unsigned int dim,const T& val) { + assign(n,width,height,depth,dim); + cimglist_for(*this,l) data[l].fill(val); + return *this; + } + + //! Construct a list containing n copies of the image img + template CImgList(const unsigned int n, const CImg& img, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(n,img,shared); + } + + //! In-place version of the previous constructor + template CImgList& assign(const unsigned int n, const CImg& img, const bool shared=false) { + assign(n); + cimglist_for(*this,l) data[l].assign(img,shared); + return *this; + } + + //! Construct an image list from one image + template explicit CImgList(const CImg& img, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img,shared); + } + + //! In-place version of the previous constructor + template CImgList& assign(const CImg& img, const bool shared=false) { + return assign(1,img,shared); + } + + //! Construct an image list from two images + template CImgList(const CImg& img1, const CImg& img2, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img1,img2,shared); + } + + //! In-place version of the previous constructor + template CImgList& assign(const CImg& img1, const CImg& img2, const bool shared=false) { + assign(2); + data[0].assign(img1,shared); data[1].assign(img2,shared); + return *this; + } + + //! Construct an image list from three images + template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img1,img2,img3,shared); + } + + //! In-place version of the previous constructor + template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared=false) { + assign(3); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); + return *this; + } + + //! Construct an image list from four images + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img1,img2,img3,img4,shared); + } + + //! In-place version of the previous constructor + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const bool shared=false) { + assign(4); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + return *this; + } + + //! Construct an image list from five images + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img1,img2,img3,img4,img5,shared); + } + + //! In-place version of the previous constructor + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const bool shared=false) { + assign(5); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); + return *this; + } + + //! Construct an image list from six images + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img1,img2,img3,img4,img5,img6,shared); + } + + //! In-place version of the previous constructor + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const bool shared=false) { + assign(6); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); + return *this; + } + + //! Construct an image list from seven images + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const CImg& img7, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img1,img2,img3,img4,img5,img6,img7,shared); + } + + //! In-place version of the previous constructor + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const CImg& img7, const bool shared=false) { + assign(7); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); + return *this; + } + + //! Construct an image list from eight images + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const CImg& img7, const CImg& img8, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img1,img2,img3,img4,img5,img6,img7,img8,shared); + } + + //! In-place version of the previous constructor + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const CImg& img7, const CImg& img8, const bool shared=false) { + assign(8); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); data[7].assign(img8,shared); + return *this; + } + + //! Construct an image list from nine images + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const CImg& img7, const CImg& img8, const CImg& img9, const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img1,img2,img3,img4,img5,img6,img7,img8,img9,shared); + } + + //! In-place version of the previous constructor + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const CImg& img7, const CImg& img8, const CImg& img9, const bool shared=false) { + assign(9); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); data[7].assign(img8,shared); + data[8].assign(img9,shared); + return *this; + } + + //! Construct an image list from ten images + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const CImg& img7, const CImg& img8, const CImg& img9, const CImg& img10, + const bool shared=false): + size(0),allocsize(0),data(0) { + assign(img1,img2,img3,img4,img5,img6,img7,img8,img9,img10,shared); + } + + //! In-place version of the previous constructor + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, + const CImg& img6, const CImg& img7, const CImg& img8, const CImg& img9, const CImg& img10, + const bool shared=false) { + assign(10); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); data[7].assign(img8,shared); + data[8].assign(img9,shared); data[9].assign(img10,shared); + return *this; + } + + //! Construct an image list from a filename + CImgList(const char *const filename): + size(0),allocsize(0),data(0) { + assign(filename); + } + + //! In-place version of the previous constructor + CImgList& assign(const char *const filename) { + return load(filename); + } + + //! Return a string describing the type of the image pixels in the list (template parameter \p T). + static const char* pixel_type() { + return cimg::type::id(); + } + + //! Return \p true if list is empty + bool is_empty() const { + return (!data || !size); + } + + //! Return \c true if the list contains an image with indice k + bool contains(const int k) const { + return data && k CImgList& operator=(const CImgList& list) { + return assign(list); + } + + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Assignement operator. + template CImgList& operator=(const CImg& img) { + cimglist_for(*this,l) data[l]=img; + return *this; + } + + //! Assignement operator. + CImgList& operator=(const T& val) { + cimglist_for(*this,l) data[l].fill(val); + return *this; + } + + //! Operator+ + CImgList operator+() const { + return CImgList(*this); + } + + //! Operator+= +#ifdef cimg_use_visualcpp6 + CImgList& operator+=(const T& val) { +#else + template CImgList& operator+=(const t& val) { +#endif + cimglist_for(*this,l) (*this)[l]+=val; + return *this; + } + + //! Operator+= + template CImgList& operator+=(const CImgList& list) { + const unsigned int sizemax = min(size,list.size); + for (unsigned int l=0; l res(size); + cimglist_for(res,l) res[l].assign(-data[l]); + return res; + } + + //! Operator-=. +#ifdef cimg_use_visualcpp6 + CImgList& operator-=(const T& val) { +#else + template CImgList& operator-=(const t& val) { +#endif + cimglist_for(*this,l) (*this)[l]-=val; + return *this; + } + + //! Operator-=. + template CImgList& operator-=(const CImgList& list) { + const unsigned int sizemax = min(size,list.size); + for (unsigned int l=0; l CImgList& operator*=(const t& val) { +#endif + cimglist_for(*this,l) (*this)[l]*=val; + return *this; + } + + //! Operator*=. + template CImgList& operator*=(const CImgList& list) { + const unsigned int N = cimg::min(size,list.size); + for (unsigned int l=0; l CImgList& operator/=(const t& val) { +#endif + cimglist_for(*this,l) (*this)[l]/=val; + return *this; + } + + //! Operator/=. + template CImgList& operator/=(const CImgList& list) { + const unsigned int N = cimg::min(size,list.size); + for (unsigned int l=0; l& operator[](const unsigned int pos) { +#if cimg_debug>=3 + if (pos>=size) { + cimg::warn(true,"CImgList<%s>::operator[] : bad list position %u, in a list of %u images",pixel_type(),pos,size); + return *data; + } +#endif + return data[pos]; + } + + const CImg& operator[](const unsigned int pos) const { +#if cimg_debug>=3 + if (pos>=size) { + cimg::warn(true,"CImgList<%s>::operator[] : bad list position %u, in a list of %u images",pixel_type(),pos,size); + return *data; + } +#endif + return data[pos]; + } + + //! Equivalent to CImgList::operator[] + CImg& operator()(const unsigned int pos) { return (*this)[pos]; } + const CImg& operator()(const unsigned int pos) const { return (*this)[pos]; } + + //! Return a reference to (x,y,z,v) pixel of the pos-th image of the list + T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int v=0) { + return (*this)[pos](x,y,z,v); + } + const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int v=0) const { + return (*this)[pos](x,y,z,v); + } + + //! Equivalent to CImgList::operator[], with boundary checking + CImg& at(const unsigned int pos) { + if (pos>=size) + throw CImgArgumentException("CImgList<%s>::at() : bad list position %u, in a list of %u images", + pixel_type(),pos,size); + return data[pos]; + } + + const CImg& at(const unsigned int pos) const { + if (pos>=size) + throw CImgArgumentException("CImgList<%s>::at() : bad list position %u, in a list of %u images", + pixel_type(),pos,size); + return data[pos]; + } + + //! Returns a reference to last element + CImg& back() { + return (*this)(size-1); + } + + const CImg& back() const { + return (*this)(size-1); + } + + //! Returns a reference to the first element + CImg& front() { + return *data; + } + + const CImg& front() const { + return *data; + } + + //! Returns an iterator to the beginning of the vector. + iterator begin() { + return data; + } + + const_iterator begin() const { + return data; + } + + //! Returns an iterator just past the last element. + iterator end() { + return data + size; + } + + const_iterator end() const { + return data + size; + } + + //! Insert a copy of the image \p img into the current image list, at position \p pos. + template CImgList& insert(const CImg& img, const unsigned int pos, const bool shared) { + if (pos>size) + throw CImgArgumentException("CImgList<%s>::insert() : Cannot insert at position %u into a list with %u elements", + pixel_type(),pos,size); + if (shared) + throw CImgArgumentException("CImgList<%s>::insert(): Cannot insert a shared image CImg<%s> into a CImgList<%s>", + pixel_type(),img.pixel_type(),pixel_type()); + CImg *new_data = (++size>allocsize)?new CImg[allocsize?(allocsize<<=1):(allocsize=1)]:0; + if (!size || !data) { data = new_data; *data = img; } else { + if (new_data) { + if (pos) std::memcpy(new_data,data,sizeof(CImg)*pos); + if (pos!=size-1) std::memcpy(new_data+pos+1,data+pos,sizeof(CImg)*(size-1-pos)); + std::memset(data,0,sizeof(CImg)*(size-1)); + delete[] data; + data = new_data; + } + else if (pos!=size-1) memmove(data+pos+1,data+pos,sizeof(CImg)*(size-1-pos)); + data[pos].width = data[pos].height = data[pos].depth = data[pos].dim = 0; data[pos].data = 0; + data[pos] = img; + } + return *this; + } + + CImgList& insert(const CImg& img, const unsigned int pos, const bool shared) { + if (pos>size) + throw CImgArgumentException("CImgList<%s>::insert() : Can't insert at position %u into a list with %u elements", + pixel_type(),pos,size); + CImg *new_data = (++size>allocsize)?new CImg[allocsize?(allocsize<<=1):(allocsize=1)]:0; + if (!size || !data) { + data = new_data; + if (shared && !img.is_empty()) { + data->width = img.width; data->height = img.height; data->depth = img.depth; data->dim = img.dim; + data->is_shared = true; data->data = img.data; + } else *data = img; + } + else { + if (new_data) { + if (pos) std::memcpy(new_data,data,sizeof(CImg)*pos); + if (pos!=size-1) std::memcpy(new_data+pos+1,data+pos,sizeof(CImg)*(size-1-pos)); + std::memset(data,0,sizeof(CImg)*(size-1)); + delete[] data; + data = new_data; + } + else if (pos!=size-1) memmove(data+pos+1,data+pos,sizeof(CImg)*(size-1-pos)); + if (shared && !img.is_empty()) { + data[pos].width = img.width; data[pos].height = img.height; data[pos].depth = img.depth; data[pos].dim = img.dim; + data[pos].is_shared = true; data[pos].data = img.data; + } else { + data[pos].width = data[pos].height = data[pos].depth = data[pos].dim = 0; data[pos].data = 0; + data[pos] = img; + } + } + return *this; + } + + template CImgList& insert(const CImg& img, const unsigned int pos) { + return insert(img,pos,false); + } + + template CImgList::type> get_insert(const CImg& img, const unsigned int pos, const bool shared=false) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).insert(img,pos,shared); + } + + //! Insert a copy of the image \p img at the current image list. + template CImgList& insert(const CImg& img) { + return insert(img,size); + } + + template CImgList::type> get_insert(const CImg& img) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).insert(img); + } + + //! Insert a copy of the image \p img at the current image list. + CImgList& operator<<(const CImg& img) { + return insert(img); + } + + //! Insert n copies of the image \p img into the current image list, at position \p pos. + template CImgList& insert(const unsigned int n, const CImg& img, const unsigned int pos) { + for (unsigned int i=0; i CImgList::type> get_insert(const unsigned int n, const CImg& img, const unsigned int pos) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).insert(n,img,pos); + } + + + //! Insert n copies of the image \p img at the end of the list. + template CImgList& insert(const unsigned int n, const CImg& img) { + for (unsigned int i=0; i CImgList::type> get_insert(const unsigned int n, const CImg& img) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).insert(n,img); + } + + //! Insert a copy of the image list \p list into the current image list, starting from position \p pos. + template CImgList& insert(const CImgList& list, const unsigned int pos) { + cimglist_for(list,l) insert(list[l],pos+l); + return *this; + } + + CImgList& insert(const CImgList& list, const unsigned int pos) { + if (this!=&list) cimglist_for(list,l) insert(list[l],pos+l); + else insert(CImgList(list),pos); + return *this; + } + + template CImgList::type> get_insert(const CImgList& list, const unsigned int pos) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).insert(list,pos); + } + + //! Append a copy of the image list \p list at the current image list. + template CImgList& insert(const CImgList& list) { + return insert(list,size); + } + + template CImgList::type> get_insert(const CImgList& list) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).insert(list); + } + + //! Insert a copy of the image list \p list at the current image list. + CImgList& operator<<(const CImgList& list) { + return insert(list); + } + + //! Insert n copies of the list \p list at position \p pos of the current list. + template CImgList& insert(const unsigned int n, const CImgList& list, const unsigned int pos) { + for (unsigned int i=0; i CImgList::type> get_insert(const unsigned int n, const CImgList& list, const unsigned int pos) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).insert(n,list,pos); + } + + //! Insert n copies of the list at the end of the current list + template CImgList& insert(const unsigned int n, const CImgList& list) { + for (unsigned int i=0; i CImgList::type> get_insert(const unsigned int n, const CImgList& list) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).insert(n,list); + } + + //! Insert image \p img at the end of the list. + template CImgList& push_back(const CImg& img) { + return insert(img); + } + + //! Insert image \p img at the front of the list. + template CImgList& push_front(const CImg& img) { + return insert(img,0); + } + + //! Insert list \p list at the end of the current list. + template CImgList& push_back(const CImgList& list) { + return insert(list); + } + + //! Insert list \p list at the front of the current list. + template CImgList& push_front(const CImgList& list) { + return insert(list,0); + } + + //! Insert a shared copy of the image \p img into the current image list, at position \p pos. + template CImgList& insert_shared(const CImg& img, const unsigned int pos) { + return insert(img,pos,true); + } + + template CImgList get_insert_shared(const CImg& img, const unsigned int pos) const { + return CImgList(*this).insert_shared(img,pos); + } + + //! Insert a shared copy of the image \p img at the current image list. + template CImgList& insert_shared(const CImg& img) { + return insert_shared(img,size); + } + + template CImgList get_insert_shared(const CImg& img) const { + return CImgList(*this).insert_shared(img); + } + + //! Insert n shared copies of the image \p img into the current image list, at position \p pos. + template CImgList& insert_shared(const unsigned int n, const CImg& img, const unsigned int pos) { + for (unsigned int i=0; i CImgList get_insert_shared(const unsigned int n, const CImg& img, const unsigned int pos) const { + return CImgList(*this).insert_shared(n,img,pos); + } + + //! Insert n shared copies of the image \p img at the end of the list. + template CImgList& insert_shared(const unsigned int n, const CImg& img) { + for (unsigned int i=0; i CImgList get_insert_shared(const unsigned int n, const CImg& img) const { + return CImgList(*this).insert_shared(n,img); + } + + //! Insert a shared copy of all image of the list \p list into the current image list, starting from position \p pos. + template CImgList& insert_shared(const CImgList& list, const unsigned int pos) { + if (this!=&list) cimglist_for(list,l) insert_shared(list[l],pos+l); + else insert_shared(CImgList(list),pos); + return *this; + } + + template CImgList get_insert_shared(const CImgList& list, const unsigned int pos) const { + return CImgList(*this).insert_shared(list,pos); + } + + //! Append a shared copy of the image list \p list at the current image list. + template CImgList& insert_shared(const CImgList& list) { + return insert_shared(list,size); + } + + template CImgList get_insert_shared(const CImgList& list) const { + return CImgList(*this).insert_shared(list); + } + + //! Insert n shared copies of the list \p list at position \p pos of the current list. + template CImgList& insert_shared(const unsigned int n, const CImgList& list, const unsigned int pos) { + for (unsigned int i=0; i CImgList get_insert_shared(const unsigned int n, const CImgList& list, const unsigned int pos) const { + return CImgList(*this).insert_shared(n,list,pos); + } + + //! Insert n shared copies of the list \p list at the end of the list + template CImgList& insert_shared(const unsigned int n, const CImgList& list) { + return insert_shared(n,list,size); + } + + template CImgList get_insert_shared(const unsigned int n, const CImgList& list) const { + return CImgList(*this).insert_shared(n,list); + } + + //! Remove the image at position \p pos from the image list. + CImgList& remove(const unsigned int pos) { + if (pos>=size) + cimg::warn(true,"CImgList<%s>::remove() : Cannot remove an image from a list (%p,%u), at position %u.", + pixel_type(),data,size,pos); + else { + data[pos].assign(); + if (!(--size)) return assign(); + if (size<8 || size>(allocsize>>2)) { // Removing item without reallocation. + if (pos!=size) { + std::memmove(data+pos,data+pos+1,sizeof(CImg)*(size-pos)); + CImg &tmp = data[size]; + tmp.width = tmp.height = tmp.depth = tmp.dim = 0; tmp.data = 0; + } + } else { // Removing item with reallocation. + allocsize>>=2; + CImg *new_data = new CImg[allocsize]; + if (pos) std::memcpy(new_data,data,sizeof(CImg)*pos); + if (pos!=size) std::memcpy(new_data+pos,data+pos+1,sizeof(CImg)*(size-pos)); + std::memset(data,0,sizeof(CImg)*(size+1)); + delete[] data; + data = new_data; + } + } + return *this; + } + + CImgList get_remove(const unsigned int pos) const { + return CImgList(*this).remove(pos); + } + + //! Remove last element of the list; + CImgList& pop_back() { + return remove(size-1); + } + + //! Remove last element of the list; + CImgList& operator>>(CImg& img) { + if (size) { img.swap((*this)[size-1]); return remove(size-1); } + cimg::warn(true,"CImgl<%s>::operator>>() : List is empty",pixel_type()); + img.assign(); + return *this; + } + + //! Remove first element of the list; + CImgList& pop_front() { + return remove(0); + } + + //! Remove the element pointed by iterator \p iter; + CImgList& erase(const iterator iter) { + return remove(iter-data); + } + + //! Remove the last image from the image list. + CImgList& remove() { + if (size) return remove(size-1); + else cimg::warn(true,"CImgList<%s>::remove() : List is empty",pixel_type()); + return *this; + } + + CImgList get_remove() const { + return CImgList(*this).remove(); + } + + //! Reverse list order + CImgList& reverse() { + for (unsigned int l=0; l(*this).reverse(); + } + + //! Get a sub-list + const CImgList get_crop(const unsigned int i0, const unsigned int i1, const bool shared=false) const { + if (i0>i1 || i1>=size) + throw CImgArgumentException("CImgList<%s>::get_crop() : Cannot get a sub-list (%u->%u) from a list of %u images", + pixel_type(),i0,i1,size); + CImgList res(i1-i0+1); + cimglist_for(res,l) res[l].assign((*this)[i0+l],shared); + return res; + } + + //! Replace a list by its sublist + CImgList& crop(const unsigned int i0, const unsigned int i1, const bool shared=false) { + return get_crop(i0,i1,shared).swap(*this); + } + + //@} + //---------------------------- + // + //! \name Fourier Transforms + //@{ + //---------------------------- + + //! Compute the Fast Fourier Transform (along the specified axis). + CImgList& FFT(const char axe, const bool inverse=false) { + if (is_empty()) throw CImgInstanceException("CImgList<%s>::FFT() : Instance list (%u,%p) is empty",pixel_type(),size,data); + if (data[0].is_empty()) throw CImgInstanceException("CImgList<%s>::FFT() : Real part (%u,%u,%u,%u,%p) is empty", + pixel_type,data[0].width,data[0].height,data[0].depth,data[0].dim,data[0].data); + cimg::warn(size>2,"CImgList<%s>::FFT() : Instance list (%u,%p) have more than 2 images",pixel_type(),size,data); + if (size==1) insert(CImg(data[0].width,data[0].height,data[0].depth,data[0].dim,0)); + CImg &Ir = data[0], &Ii = data[1]; + if (Ir.width!=Ii.width || Ir.height!=Ii.height || Ir.depth!=Ii.depth || Ir.dim!=Ii.dim) + throw CImgInstanceException("CImgList<%s>::FFT() : Real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p)" + "have different dimensions",pixel_type(), + Ir.width,Ir.height,Ir.depth,Ir.dim,Ir.data,Ii.width,Ii.height,Ii.depth,Ii.dim,Ii.data); + +#ifdef cimg_use_fftw3 + fftw_complex *data_in; + fftw_plan data_plan; + + switch (cimg::uncase(axe)) { + case 'x': { + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.width); + data_plan = fftw_plan_dft_1d(Ir.width, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); + cimg_forYZV(Ir,y,z,k) { + T *ptrr = Ir.ptr(0,y,z,k), *ptri = Ii.ptr(0,y,z,k); + double *ptrd = (double*)data_in; + cimg_forX(Ir,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); } + fftw_execute(data_plan); + cimg_forX(Ir,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); } + } + } break; + + case 'y': { + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.height); + data_plan = fftw_plan_dft_1d(Ir.height, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); + const unsigned int off = Ir.width; + cimg_forXZV(Ir,x,z,k) { + T *ptrr = Ir.ptr(x,0,z,k), *ptri = Ii.ptr(x,0,z,k); + double *ptrd = (double*)data_in; + cimg_forY(Ir,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } + fftw_execute(data_plan); + cimg_forY(Ir,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } + } + } break; + + case 'z': { + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.depth); + data_plan = fftw_plan_dft_1d(Ir.depth, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); + const unsigned int off = Ir.width*Ir.height; + cimg_forXYV(Ir,x,y,k) { + T *ptrr = Ir.ptr(x,y,0,k), *ptri = Ii.ptr(x,y,0,k); + double *ptrd = (double*)data_in; + cimg_forZ(Ir,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } + fftw_execute(data_plan); + cimg_forZ(Ir,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } + } + } break; + + case 'v': { + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.dim); + data_plan = fftw_plan_dft_1d(Ir.dim, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); + const unsigned int off = Ir.width*Ir.height*Ir.depth; + cimg_forXYZ(Ir,x,y,z) { + T *ptrr = Ir.ptr(x,y,z,0), *ptri = Ii.ptr(x,y,z,0); + double *ptrd = (double*)data_in; + cimg_forV(Ir,k) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } + fftw_execute(data_plan); + cimg_forV(Ir,k) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } + } + } break; + } + + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#else + switch (cimg::uncase(axe)) { + case 'x': { // Fourier along X + const unsigned int N = Ir.width, N2 = (N>>1); + if (((N-1)&N) && N!=1) throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image along 'x' is %d != 2^N", + pixel_type(),N); + for (unsigned int i=0,j=0; ii) cimg_forYZV(Ir,y,z,v) { cimg::swap(Ir(i,y,z,v),Ir(j,y,z,v)); cimg::swap(Ii(i,y,z,v),Ii(j,y,z,v)); + if (j=m; j-=m, m=n, n>>=1); + } + for (unsigned int delta=2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i=0; i>1); + if (((N-1)&N) && N!=1) throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image(s) along 'y' is %d != 2^N", + pixel_type(),N); + for (unsigned int i=0,j=0; ii) cimg_forXZV(Ir,x,z,v) { cimg::swap(Ir(x,i,z,v),Ir(x,j,z,v)); cimg::swap(Ii(x,i,z,v),Ii(x,j,z,v)); + if (j=m; j-=m, m=n, n>>=1); + } + for (unsigned int delta=2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i=0; i>1); + if (((N-1)&N) && N!=1) throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image(s) along 'z' is %d != 2^N", + pixel_type(),N); + for (unsigned int i=0,j=0; ii) cimg_forXYV(Ir,x,y,v) { cimg::swap(Ir(x,y,i,v),Ir(x,y,j,v)); cimg::swap(Ii(x,y,i,v),Ii(x,y,j,v)); + if (j=m; j-=m, m=n, n>>=1); + } + for (unsigned int delta=2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i=0; i::FFT() : unknown axe '%c', must be 'x','y' or 'z'"); + } +#endif + return *this; + } + + //! Return the Fast Fourier Transform of a complex image (along a specified axis). + CImgList::type> get_FFT(const char axe,const bool inverse=false) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).FFT(axe,inverse); + } + + //! Compute the Fast Fourier Transform of a complex image. + CImgList& FFT(const bool inverse=false) { + if (is_empty()) throw CImgInstanceException("CImgList<%s>::FFT() : Instance list (%u,%p) is empty",pixel_type(),size,data); + cimg::warn(size>2,"CImgList<%s>::FFT() : Instance list (%u,%p) have more than 2 images",pixel_type(),size,data); + if (size==1) insert(CImg(data[0].width,data[0].height,data[0].depth,data[0].dim,0)); + CImg &Ir = data[0]; + +#ifdef cimg_use_fftw3 + CImg &Ii = data[1]; + fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.width*Ir.height*Ir.depth); + fftw_plan data_plan; + const unsigned int w = Ir.width, wh = w*Ir.height, whd = wh*Ir.depth; + data_plan = fftw_plan_dft_3d(Ir.width, Ir.height, Ir.depth, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); + cimg_forV(Ir,k) { + T *ptrr = Ir.ptr(0,0,0,k), *ptri = Ii.ptr(0,0,0,k); + double *ptrd = (double*)data_in; + cimg_forX(Ir,x) { cimg_forY(Ir,y) { cimg_forZ(Ir,z) + { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=wh; ptri+=wh; } + ptrr-=whd-w; ptri-=whd-w; } + ptrr-=wh-1; ptri-=wh-1; } + fftw_execute(data_plan); + ptrd = (double*)data_in; + ptrr = Ir.ptr(0,0,0,k), ptri = Ii.ptr(0,0,0,k); + cimg_forX(Ir,x) { cimg_forY(Ir,y) { cimg_forZ(Ir,z) + { *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++); ptrr+=wh; ptri+=wh; } + ptrr-=whd-w; ptri-=whd-w; } + ptrr-=wh-1; ptri-=wh-1; } + } + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#else + if (Ir.depth>1) FFT('z',inverse); + if (Ir.height>1) FFT('y',inverse); + if (Ir.width>1) FFT('x',inverse); +#endif + return *this; + } + + //! Return the Fast Fourier Transform of a complex image + CImgList::type> get_FFT(const bool inverse=false) const { + typedef typename cimg::largest::type restype; + return CImgList(*this).FFT(inverse); + } + + //@} + //---------------------------------- + // + //! \name Input-Output and Display + //@{ + //---------------------------------- + + //! Print informations about the list on the standard output. + const CImgList& print(const char* title=0, const unsigned int print_flag=1) const { + char tmp[1024]; + std::fprintf(stderr,"%-8s(this=%p) : { size=%u, data=%p }\n",title?title:"CImgList", + (void*)this,size,(void*)data); + if (print_flag>0) cimglist_for(*this,l) { + std::sprintf(tmp,"%s[%d]",title?title:"CImgList",l); + data[l].print(tmp,print_flag); + } + return *this; + } + + //! Display informations about the list on the standart output. + const CImgList& print(const unsigned int print_flag) const { + return print(0,print_flag); + } + + //! Load an image list from a file. + static CImgList get_load(const char *const filename) { + const char *ext = cimg::filename_split(filename); + if (!cimg::strncasecmp(ext,"cimg",4) || !ext[0]) return get_load_cimg(filename); + if (!cimg::strncasecmp(ext,"rec",3) || + !cimg::strncasecmp(ext,"par",3)) return get_load_parrec(filename); + CImgList res(1); + res[0].load(filename); + return res; + } + + //! In-place version of load(). + CImgList& load(const char *const filename) { + return get_load(filename).swap(*this); + } + +#define cimg_load_cimg_case(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,tmp2)) for (unsigned int l=0; l0) { \ + Tss *buf = new Tss[w*h*z*k]; cimg::fread(buf,w*h*z*k,nfile); \ + if (endian) cimg::endian_swap(buf,w*h*z*k); \ + CImg idest(w,h,z,k); \ + cimg_foroff(idest,off) idest[off] = (T)(buf[off]); idest.swap(res[l]); \ + delete[] buf; \ + } \ + loaded = true; \ + } + + //! Load an image list from a file (.raw format). + static CImgList get_load_cimg(std::FILE *const file, const char *const filename=0) { + typedef unsigned char uchar; + typedef unsigned short ushort; + typedef unsigned int uint; + typedef unsigned long ulong; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + char tmp[256],tmp2[256]; + int i; + bool loaded = false; + unsigned int n,j,w,h,z,k,err; + j=0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++]=i; tmp[j]='\0'; + err=std::sscanf(tmp,"%u%*c%255[A-Za-z ]",&n,tmp2); + if (err!=2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::get_load_cimg() : File '%s', Unknow CImg RAW header.", + pixel_type(),filename?filename:"(FILE*)"); + } + CImgList res(n); + cimg_load_cimg_case("bool",bool); + cimg_load_cimg_case("unsigned char",uchar); + cimg_load_cimg_case("uchar",uchar); + cimg_load_cimg_case("char",char); + cimg_load_cimg_case("unsigned short",ushort); + cimg_load_cimg_case("ushort",ushort); + cimg_load_cimg_case("short",short); + cimg_load_cimg_case("unsigned int",uint); + cimg_load_cimg_case("uint",uint); + cimg_load_cimg_case("int",int); + cimg_load_cimg_case("unsigned long",ulong); + cimg_load_cimg_case("ulong",ulong); + cimg_load_cimg_case("long",long); + cimg_load_cimg_case("float",float); + cimg_load_cimg_case("double",double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::get_load_cimg() : File '%s', cannot read images of pixels coded as '%s'.", + pixel_type(),filename?filename:"(FILE*)",tmp2); + } + if (!file) cimg::fclose(nfile); + return res; + } + + //! Load an image list from a file (.raw format). + static CImgList get_load_cimg(const char *const filename) { + return get_load_cimg(0,filename); + } + + //! In-place version of get_load_cimg(). + CImgList& load_cimg(std::FILE *const file, const char *const filename=0) { + return get_load_cimg(file,filename).swap(*this); + } + + //! In-place version of get_load_cimg(). + CImgList& load_cimg(const char *const filename) { + return get_load_cimg(filename).swap(*this); + } + + //! Load PAR-REC (Philips) image file + static CImgList get_load_parrec(const char *const filename) { + char body[1024], filenamepar[1024], filenamerec[1024]; + const char *ext = cimg::filename_split(filename,body); + if (!cimg::strncmp(ext,"par",3)) { std::strcpy(filenamepar,filename); std::sprintf(filenamerec,"%s.rec",body); } + if (!cimg::strncmp(ext,"PAR",3)) { std::strcpy(filenamepar,filename); std::sprintf(filenamerec,"%s.REC",body); } + if (!cimg::strncmp(ext,"rec",3)) { std::strcpy(filenamerec,filename); std::sprintf(filenamepar,"%s.par",body); } + if (!cimg::strncmp(ext,"REC",3)) { std::strcpy(filenamerec,filename); std::sprintf(filenamepar,"%s.PAR",body); } + std::FILE *file = cimg::fopen(filenamepar,"r"); + + // Parse header file + CImgList st_slices; + CImgList st_global; + int err; + char line[256]={0}; + do { err=std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (line[0]=='#' || line[0]=='.')); + do { + unsigned int sn,sizex,sizey,pixsize; + float rs,ri,ss; + err=std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&sizex,&sizey,&ri,&rs,&ss); + if (err==7) { + st_slices.insert(CImg::vector((float)sn,(float)pixsize,(float)sizex,(float)sizey,ri,rs,ss,0)); + unsigned int i; for (i=0; i::vector(sizex,sizey,sn)); + else { + CImg &vec = st_global[i]; + if (sizex>vec[0]) vec[0] = sizex; + if (sizey>vec[1]) vec[1] = sizey; + vec[2] = sn; + } + st_slices[st_slices.size-1][7] = (float)i; + } + } while (err==7); + + // Read data + std::FILE *file2 = cimg::fopen(filenamerec,"rb"); + CImgList dest; + { cimglist_for(st_global,l) { + const CImg& vec = st_global[l]; + dest.insert(CImg(vec[0],vec[1],vec[2])); + }} + + cimglist_for(st_slices,l) { + const CImg& vec = st_slices[l]; + const unsigned int + sn = (unsigned int)vec[0]-1, + pixsize = (unsigned int)vec[1], + sizex = (unsigned int)vec[2], + sizey = (unsigned int)vec[3], + imn = (unsigned int)vec[7]; + const float ri = vec[4], rs = vec[5], ss = vec[6]; + switch (pixsize) { + case 8: { + CImg buf(sizex,sizey); + cimg::fread(buf.data,sizex*sizey,file2); + if (cimg::endian()) cimg::endian_swap(buf.data,sizex*sizey); + CImg& img = dest[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 16: { + CImg buf(sizex,sizey); + cimg::fread(buf.data,sizex*sizey,file2); + if (cimg::endian()) cimg::endian_swap(buf.data,sizex*sizey); + CImg& img = dest[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 32: { + CImg buf(sizex,sizey); + cimg::fread(buf.data,sizex*sizey,file2); + if (cimg::endian()) cimg::endian_swap(buf.data,sizex*sizey); + CImg& img = dest[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + default: + cimg::fclose(file); + cimg::fclose(file2); + throw CImgIOException("CImg<%s>::get_load_parrec() : File '%s', cannot handle image with pixsize = %d bits.", + pixel_type(),filename,pixsize); + break; + } + } + cimg::fclose(file); + cimg::fclose(file2); + if (!dest.size) + throw CImgIOException("CImg<%s>::get_load_parrec() : File '%s' does not appear to be a valid PAR-REC file.", + pixel_type(),filename); + return dest; + } + + //! In-place version of get_load_parrec(). + CImgList& load_parrec(const char *const filename) { + return get_load_parrec(filename).swap(*this); + } + + //! Load YUV image sequence. + static CImgList get_load_yuv(std::FILE *const file, const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const int last_frame=-1, + const bool yuv2rgb=false) { + if (sizex%2 || sizey%2) + throw CImgArgumentException("CImgList<%s>::get_load_yuv() : File '%s', image dimensions along X and Y must be even numbers (given are %ux%u)\n", + pixel_type(),filename?filename:"(unknown)",sizex,sizey); + if (!sizex || !sizey) + throw CImgArgumentException("CImgList<%s>::get_load_yuv() : File '%s', given image sequence size (%u,%u) is invalid", + pixel_type(),filename?filename:"(unknown)",sizex,sizey); + if (last_frame>0 && first_frame>(unsigned int)last_frame) + throw CImgArgumentException("CImgList<%s>::get_load_yuv() : File '%s', given first frame %u is posterior to last frame %d.", + pixel_type(),filename?filename:"(unknown)",first_frame,last_frame); + if (!sizex || !sizey) + throw CImgArgumentException("CImgList<%s>::get_load_yuv() : File '%s', given frame size (%u,%u) is invalid.", + pixel_type(),filename?filename:"(unknown)",sizex,sizey); + CImgList res; + CImg tmp(sizex,sizey,1,3), UV(sizex/2,sizey/2,1,2); + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool stopflag = false; + int err; + if (first_frame) { + err = std::fseek(nfile,first_frame*(sizex*sizey + sizex*sizey/2),SEEK_CUR); + if (err) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::get_load_yuv() : File '%s' doesn't contain frame number %u " + "(out of range error).",pixel_type(),filename?filename:"(FILE*)",first_frame); + } + } + unsigned int frame; + for (frame = first_frame; !stopflag && (last_frame<0 || frame<=(unsigned int)last_frame); frame++) { + tmp.fill(0); + // TRY to read the luminance, don't replace by cimg::fread ! + err = (int)std::fread((void*)(tmp.ptr()),1,(size_t)(tmp.width*tmp.height),nfile); + if (err!=(int)(tmp.width*tmp.height)) { + stopflag = true; + cimg::warn(err>0,"CImgList<%s>::get_load_yuv() : File '%s' contains incomplete data," + " or given image dimensions (%u,%u) are incorrect.", + pixel_type(),filename?filename:"(unknown)",sizex,sizey); + } else { + UV.fill(0); + // TRY to read the luminance, don't replace by cimg::fread ! + err = (int)std::fread((void*)(UV.ptr()),1,(size_t)(UV.size()),nfile); + if (err!=(int)(UV.size())) { + stopflag = true; + cimg::warn(err>0,"CImgList<%s>::get_load_yuv() : File '%s' contains incomplete data," + " or given image dimensions (%u,%u) are incorrect.", + pixel_type(),filename?filename:"(unknown)",sizex,sizey); + } else { + cimg_forXY(UV,x,y) { + const int x2=2*x, y2=2*y; + tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0); + tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1); + } + if (yuv2rgb) tmp.YCbCrtoRGB(); + res.insert(tmp); + } + } + } + cimg::warn(stopflag && last_frame>=0 && frame!=(unsigned int)last_frame, + "CImgList<%s>::get_load_yuv() : File '%s', frame %d not reached since only %u frames were found in the file.", + pixel_type(),filename?filename:"(unknown)",last_frame,frame-1,filename); + if (!file) cimg::fclose(nfile); + return res; + } + + //! Load YUV image sequence. + static CImgList get_load_yuv(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const int last_frame=-1, + const bool yuv2rgb=false) { + return get_load_yuv(0,filename,sizex,sizey,first_frame,last_frame,yuv2rgb); + } + + //! In-place version of get_load_yuv(). + CImgList& load_yuv(std::FILE *const file, const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const int last_frame=-1, + const bool yuv2rgb=false) { + return get_load_yuv(file,filename,sizex,sizey,first_frame,last_frame,yuv2rgb).swap(*this); + } + + //! In-place version of get_load_yuv(). + CImgList& load_yuv(const char *const filename, + const unsigned int sizex, const unsigned int sizey, + const unsigned int first_frame=0, const int last_frame=-1, + const bool yuv2rgb=false) { + return get_load_yuv(filename,sizex,sizey,first_frame,last_frame,yuv2rgb).swap(*this); + } + + //! Load from OFF file format + template + static CImgList get_load_off(std::FILE *const file, const char *const filename, + CImgList& primitives, CImgList& colors, + const bool invert_faces=false) { + return CImg::get_load_off(file,filename,primitives,colors,invert_faces).get_split('x'); + } + + //! Load from OFF file format + template + static CImgList get_load_off(const char *const filename, + CImgList& primitives, CImgList& colors, + const bool invert_faces=false) { + return get_load_off(0,filename,primitives,colors,invert_faces); + } + + //! In-place version of get_load_off() + template + CImgList& load_off(std::FILE *const file, const char *const filename, CImgList& primitives, CImgList& colors, + const bool invert_faces=false) { + return get_load_off(file,filename,primitives,colors,invert_faces).swap(*this); + } + + //! In-place version of get_load_off() + template + CImgList& load_off(const char *const filename, CImgList& primitives, CImgList& colors, + const bool invert_faces=false) { + return get_load_off(filename,primitives,colors,invert_faces).swap(*this); + } + + //! Save an image list into a file. + /** + Depending on the extension of the given filename, a file format is chosen for the output file. + **/ + const CImgList& save(const char *const filename) const { + if (is_empty()) throw CImgInstanceException("CImgList<%s>::save() : Instance list (%u,%p) is empty (file '%s').", + pixel_type(),size,data,filename); + if (!filename) throw CImgArgumentException("CImg<%s>::save() : Instance list (%u,%p), specified filename is (null).", + pixel_type(),size,data); + const char *ext = cimg::filename_split(filename); + if (!cimg::strncasecmp(ext,"cimg",4) || !ext[0]) return save_cimg(filename); + if (!cimg::strncasecmp(ext,"yuv",3)) return save_yuv(filename,true); + if (size==1) data[0].save(filename,-1); + else cimglist_for(*this,l) data[l].save(filename,l); + return *this; + } + + //! Save an image sequence into a YUV file + const CImgList& save_yuv(std::FILE *const file, const char *const filename=0, const bool rgb2yuv=true) const { + if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_yuv() : Instance list (%u,%p) is empty (file '%s').", + pixel_type(),size,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_yuv() : Instance list (%u,%p), specified file is (null).", + pixel_type(),size,data); + if ((*this)[0].dimx()%2 || (*this)[0].dimy()%2) + throw CImgInstanceException("CImgList<%s>::save_yuv() : Image dimensions must be even numbers (current are %ux%u, file '%s').", + pixel_type(),(*this)[0].dimx(),(*this)[0].dimy(),filename?filename:"(unknown)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + cimglist_for(*this,l) { + CImg YCbCr((*this)[l]); + if (rgb2yuv) YCbCr.RGBtoYCbCr(); + cimg::fwrite(YCbCr.ptr(),YCbCr.width*YCbCr.height,nfile); + cimg::fwrite(YCbCr.get_resize(YCbCr.width/2, YCbCr.height/2,1,3,3).ptr(0,0,0,1), + YCbCr.width*YCbCr.height/2,nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save an image sequence into a YUV file + const CImgList& save_yuv(const char *const filename=0, const bool rgb2yuv=true) const { + return save_yuv(0,filename,rgb2yuv); + } + + //! Save an image list into a CImg file (RAW binary file + simple header) + /** + A CImg RAW file is a simple uncompressed binary file that may be used to save list of CImg images. + \param filename : name of the output file. + \return A reference to the current CImgList instance is returned. + **/ + const CImgList& save_cimg(std::FILE *const file, const char *const filename=0) const { + if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_cimg() : Instance list (%u,%p) is empty (file '%s').", + pixel_type(),size,data,filename?filename:"(unknown)"); + if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_cimg() : Instance list (%u,%p), specified file is (null).", + pixel_type(),size,data); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + std::fprintf(nfile,"%u %s\n",size,pixel_type()); + cimglist_for(*this,l) { + const CImg& img = data[l]; + std::fprintf(nfile,"%u %u %u %u\n",img.width,img.height,img.depth,img.dim); + if (img.data) { + if (cimg::endian()) { + CImg tmp(img); + cimg::endian_swap(tmp.data,tmp.size()); + cimg::fwrite(tmp.data,img.width*img.height*img.depth*img.dim,nfile); + } else cimg::fwrite(img.data,img.width*img.height*img.depth*img.dim,nfile); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save an image list into a CImg file (RAW binary file + simple header) + const CImgList& save_cimg(const char *const filename) const { + return save_cimg(0,filename); + } + + //! Save an image list into a OFF file. + template + const CImgList& save_off(std::FILE *const file, const char *const filename, + const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { + get_append('x').save_off(file,filename,primitives,colors,invert_faces); + return *this; + } + + //! Save an image list into a OFF file. + template + const CImgList& save_off(const char *const filename, + const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { + return save_off(filename,primitives,colors,invert_faces); + } + + //! Return a single image which is the concatenation of all images of the current CImgList instance. + /** + \param axe : specify the axe for image concatenation. Can be 'x','y','z' or 'v'. + \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). + \return A CImg image corresponding to the concatenation is returned. + **/ + CImg get_append(const char axe='x',const char align='c') const { + if (is_empty()) return CImg(); + unsigned int dx=0,dy=0,dz=0,dv=0,pos=0; + CImg res; + switch(cimg::uncase(axe)) { + case 'x': { + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + dx += img.width; + dy = cimg::max(dy,img.height); + dz = cimg::max(dz,img.depth); + dv = cimg::max(dv,img.dim); + } + res.assign(dx,dy,dz,dv,0); + switch (cimg::uncase(align)) { + case 'p' : { cimglist_for(*this,ll) { res.draw_image((*this)[ll],pos,0,0,0); pos+=(*this)[ll].width; }} break; + case 'n' : { cimglist_for(*this,ll) { + res.draw_image((*this)[ll],pos,dy-(*this)[ll].height,dz-(*this)[ll].depth,dv-(*this)[ll].dim); pos+=(*this)[ll].width; + }} break; + default : { cimglist_for(*this,ll) { + res.draw_image((*this)[ll],pos,(dy-(*this)[ll].height)/2,(dz-(*this)[ll].depth)/2,(dv-(*this)[ll].dim)/2); + pos+=(*this)[ll].width; + }} break; + } + } break; + case 'y': { + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + dx = cimg::max(dx,img.width); + dy += img.height; + dz = cimg::max(dz,img.depth); + dv = cimg::max(dv,img.dim); + } + res.assign(dx,dy,dz,dv,0); + switch (cimg::uncase(align)) { + case 'p': { cimglist_for(*this,ll) { res.draw_image((*this)[ll],0,pos,0,0); pos+=(*this)[ll].height; }} break; + case 'n': { cimglist_for(*this,ll) { + res.draw_image((*this)[ll],dx-(*this)[ll].width,pos,dz-(*this)[ll].depth,dv-(*this)[ll].dim); pos+=(*this)[ll].height; + }} break; + default : { cimglist_for(*this,ll) { + res.draw_image((*this)[ll],(dx-(*this)[ll].width)/2,pos,(dz-(*this)[ll].depth)/2,(dv-(*this)[ll].dim)/2); + pos+=(*this)[ll].height; + }} break; + } + } break; + case 'z': { + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + dx = cimg::max(dx,img.width); + dy = cimg::max(dy,img.height); + dz += img.depth; + dv = cimg::max(dv,img.dim); + } + res.assign(dx,dy,dz,dv,0); + switch (cimg::uncase(align)) { + case 'p': { cimglist_for(*this,ll) { res.draw_image((*this)[ll],0,0,pos,0); pos+=(*this)[ll].depth; }} break; + case 'n': { cimglist_for(*this,ll) { + res.draw_image((*this)[ll],dx-(*this)[ll].width,dy-(*this)[ll].height,pos,dv-(*this)[ll].dim); pos+=(*this)[ll].depth; + }} break; + case 'c': { cimglist_for(*this,ll) { + res.draw_image((*this)[ll],(dx-(*this)[ll].width)/2,(dy-(*this)[ll].height)/2,pos,(dv-(*this)[ll].dim)/2); + pos+=(*this)[ll].depth; + }} break; + } + } break; + case 'v': { + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + dx = cimg::max(dx,img.width); + dy = cimg::max(dy,img.height); + dz = cimg::max(dz,img.depth); + dv += img.dim; + } + res.assign(dx,dy,dz,dv,0); + switch (cimg::uncase(align)) { + case 'p': { cimglist_for(*this,ll) { res.draw_image((*this)[ll],0,0,0,pos); pos+=(*this)[ll].dim; }} break; + case 'n': { cimglist_for(*this,ll) { + res.draw_image((*this)[ll],dx-(*this)[ll].width,dy-(*this)[ll].height,dz-(*this)[ll].depth,pos); pos+=(*this)[ll].dim; + }} break; + case 'c': { cimglist_for(*this,ll) { + res.draw_image((*this)[ll],(dx-(*this)[ll].width)/2,(dy-(*this)[ll].height)/2,(dz-(*this)[ll].depth)/2,pos); + pos+=(*this)[ll].dim; + }} break; + } + } break; + default: throw CImgArgumentException("CImg<%s>::get_append() : unknow axe '%c', must be 'x','y','z' or 'v'",pixel_type(),axe); + } + return res; + } + + // Create an auto-cropped font (along the X axis) from a input font \p font. + CImgList get_crop_font() const { + CImgList res; + cimglist_for(*this,l) { + const CImg& letter = (*this)[l]; + int xmin = letter.width, xmax = 0; + cimg_forXY(letter,x,y) if (letter(x,y)) { if (xxmax) xmax=x; } + if (xmin>xmax) res.insert(CImg(letter.width,letter.height,1,letter.dim,0)); + else res.insert(letter.get_crop(xmin,0,xmax,letter.height-1)); + } + res[' '].resize(res['f'].width); + res[' '+256].resize(res['f'].width); + return res; + } + + CImgList& crop_font() { + return get_crop_font().swap(*this); + } + + static CImgList get_font(const unsigned int *const font,const unsigned int w,const unsigned int h, + const unsigned int paddingx, const unsigned int paddingy, const bool variable_size=true) { + CImgList res = CImgList(256,w,h,1,3).insert(CImgList(256,w,h,1,1)); + const unsigned int *ptr = font; + unsigned int m = 0, val = 0; + for (unsigned int y=0; y>=1; if (!m) { m=0x80000000; val = *(ptr++); } + CImg& img = res[x/w], &mask = res[x/w+256]; + unsigned int xm = x%w; + img(xm,y,0) = img(xm,y,1) = img(xm,y,2) = mask(xm,y,0) = (T)((val&m)?1:0); + } + if (variable_size) res.crop_font(); + if (paddingx || paddingy) cimglist_for(res,l) res[l].resize(res[l].dimx()+paddingx, res[l].dimy()+paddingy,1,-100,0); + return res; + } + + //! Return a CImg pre-defined font with desired size + /** + \param font_height = height of the desired font (can be 11,13,24,38 or 57) + \param fixed_size = tell if the font has a fixed or variable width. + **/ + static CImgList get_font(const unsigned int font_width, const bool variable_size=true) { + if (font_width<=11) { + static CImgList font7x11, nfont7x11; + if (!variable_size && font7x11.is_empty()) font7x11 = get_font(cimg::font7x11,7,11,1,0,false); + if (variable_size && nfont7x11.is_empty()) nfont7x11 = get_font(cimg::font7x11,7,11,1,0,true); + return variable_size?nfont7x11:font7x11; + } + if (font_width<=13) { + static CImgList font10x13, nfont10x13; + if (!variable_size && font10x13.is_empty()) font10x13 = get_font(cimg::font10x13,10,13,1,0,false); + if (variable_size && nfont10x13.is_empty()) nfont10x13 = get_font(cimg::font10x13,10,13,1,0,true); + return variable_size?nfont10x13:font10x13; + } + if (font_width<=17) { + static CImgList font8x17, nfont8x17; + if (!variable_size && font8x17.is_empty()) font8x17 = get_font(cimg::font8x17,8,17,1,0,false); + if (variable_size && nfont8x17.is_empty()) nfont8x17 = get_font(cimg::font8x17,8,17,1,0,true); + return variable_size?nfont8x17:font8x17; + } + if (font_width<=19) { + static CImgList font10x19, nfont10x19; + if (!variable_size && font10x19.is_empty()) font10x19 = get_font(cimg::font10x19,10,19,2,0,false); + if (variable_size && nfont10x19.is_empty()) nfont10x19 = get_font(cimg::font10x19,10,19,2,0,true); + return variable_size?nfont10x19:font10x19; + } + if (font_width<=24) { + static CImgList font12x24, nfont12x24; + if (!variable_size && font12x24.is_empty()) font12x24 = get_font(cimg::font12x24,12,24,2,0,false); + if (variable_size && nfont12x24.is_empty()) nfont12x24 = get_font(cimg::font12x24,12,24,2,0,true); + return variable_size?nfont12x24:font12x24; + } + if (font_width<=32) { + static CImgList font16x32, nfont16x32; + if (!variable_size && font16x32.is_empty()) font16x32 = get_font(cimg::font16x32,16,32,2,0,false); + if (variable_size && nfont16x32.is_empty()) nfont16x32 = get_font(cimg::font16x32,16,32,2,0,true); + return variable_size?nfont16x32:font16x32; + } + if (font_width<=38) { + static CImgList font19x38, nfont19x38; + if (!variable_size && font19x38.is_empty()) font19x38 = get_font(cimg::font19x38,19,38,3,0,false); + if (variable_size && nfont19x38.is_empty()) nfont19x38 = get_font(cimg::font19x38,19,38,3,0,true); + return variable_size?nfont19x38:font19x38; + } + static CImgList font29x57, nfont29x57; + if (!variable_size && font29x57.is_empty()) font29x57 = get_font(cimg::font29x57,29,57,5,0,false); + if (variable_size && nfont29x57.is_empty()) nfont29x57 = get_font(cimg::font29x57,29,57,5,0,true); + return variable_size?nfont29x57:font29x57; + } + + //! Display the current CImgList instance in an existing CImgDisplay window (by reference). + /** + This function displays the list images of the current CImgList instance into an existing CImgDisplay window. + Images of the list are concatenated in a single temporarly image for visualization purposes. + The function returns immediately. + \param disp : reference to an existing CImgDisplay instance, where the current image list will be displayed. + \param axe : specify the axe for image concatenation. Can be 'x','y','z' or 'v'. + \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). + \return A reference to the current CImgList instance is returned. + **/ + const CImgList& display(CImgDisplay& disp, const char axe='x', const char align='c') const { + get_append(axe,align).display(disp); + return *this; + } + + //! Display the current CImgList instance in a new display window. + /** + This function opens a new window with a specific title and displays the list images of the current CImgList instance into it. + Images of the list are concatenated in a single temporarly image for visualization purposes. + The function returns when a key is pressed or the display window is closed by the user. + \param title : specify the title of the opening display window. + \param axe : specify the axe for image concatenation. Can be 'x','y','z' or 'v'. + \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). + \param min_size : specify the minimum size of the opening display window. Images having dimensions below this + size will be upscaled. + \param max_size : specify the maximum size of the opening display window. Images having dimensions above this + size will be downscaled. + \return A reference to the current CImgList instance is returned. + **/ + const CImgList& display(const char* title,const char axe='x',const char align='c', + const int min_size=128,const int max_size=1024) const { + get_append(axe,align).display(title,min_size,max_size); + return *this; + } + + //! Display the current CImgList instance in a new display window. + /** + This function opens a new window and displays the list images of the current CImgList instance into it. + Images of the list are concatenated in a single temporarly image for visualization purposes. + The function returns when a key is pressed or the display window is closed by the user. + \param axe : specify the axe for image concatenation. Can be 'x','y','z' or 'v'. + \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). + \param min_size : specify the minimum size of the opening display window. Images having dimensions below this + size will be upscaled. + \param max_size : specify the maximum size of the opening display window. Images having dimensions above this + size will be downscaled. + \return A reference to the current CImgList instance is returned. + **/ + const CImgList& display(const char axe='x',const char align='c', + const int min_size=128,const int max_size=1024) const { + return display(" ",axe,align,min_size,max_size); + } + + //! Rescale and center 3D object + CImgList& resize_object3d(const float siz=100, const bool centering=true) { + float xm = (float)((*this)(0,0)), ym = (float)((*this)(0,1)), zm = (float)((*this)(0,2)), xM = xm, yM = ym, zM = zm; + for (unsigned int p=1; pxM) xM = x; + if (y>yM) yM = y; + if (z>zM) zM = z; + } + const float + cx = 0.5f*(xm+xM), + cy = 0.5f*(ym+yM), + cz = 0.5f*(zm+zM), + delta = cimg::max(xM-xm,yM-ym,zM-zm), + ratio = (siz>=0)?siz/delta:-siz/100; + if (centering) cimglist_for(*this,l) { + T &x = (*this)(l,0), &y = (*this)(l,1), &z = (*this)(l,2); + x = (T)((x-cx)*ratio); + y = (T)((y-cy)*ratio); + z = (T)((z-cz)*ratio); + } else cimglist_for(*this,l) { + T &x = (*this)(l,0), &y = (*this)(l,1), &z = (*this)(l,2); + x = (T)(cx+(x-cx)*ratio); + y = (T)(cy+(y-cy)*ratio); + z = (T)(cz+(z-cz)*ratio); + } + return *this; + } + + //! Get a rescaled and centered version of the 3D object + CImgList get_resize_object3d(const float siz=100, const bool centering=true) const { + return CImgList(*this).resize_object3d(siz,centering); + } + + // Swap fields of two CImgList instances. + CImgList& swap(CImgList& list) { + cimg::swap(size,list.size); + cimg::swap(allocsize,list.allocsize); + cimg::swap(data,list.data); + return list; + } + + }; + + /* + #----------------------------------------- + # + # + # + # Complete previously defined functions + # + # + # + #------------------------------------------ + */ + +#ifdef cimg_use_visualcpp6 + template inline CImg operator+(const CImg& img, const t& val) { + return CImg(img,false)+=val; + } +#else + template inline CImg::type> operator+(const CImg& img, const t2& val) { + typedef typename cimg::largest::type restype; + return CImg(img,false)+=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImg operator+(const t& val, const CImg& img) { + return img+val; + } +#else + template inline CImg::type> operator+(const t1& val, const CImg& img) { + return img+val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImgList operator+(const CImgList& list, const t& val) { + return CImgList(list)+=val; + } +#else + template inline CImgList::type> operator+(const CImgList& list, const t2& val) { + typedef typename cimg::largest::type restype; + return CImgList(list)+=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImgList operator+(const t& val, const CImgList& list) { + return list+val; + } +#else + template inline CImgList::type> operator+(const t1& val, const CImgList& list) { + return list+val; + } +#endif + + template inline CImg::type> operator+(const CImg& img1, const CImg& img2) { + typedef typename cimg::largest::type restype; + return CImg(img1,false)+=img2; + } + + template inline CImgList::type> operator+(const CImg& img, const CImgList& list) { + typedef typename cimg::largest::type restype; + return CImgList(list)+=img; + } + + template inline CImgList::type> operator+(const CImgList& list, const CImg& img) { + return img+list; + } + + template inline CImgList::type> operator+(const CImgList& list1, const CImgList& list2) { + typedef typename cimg::largest::type restype; + return CImgList(list1)+=list2; + } + +#ifdef cimg_use_visualcpp6 + template inline CImg operator-(const CImg& img, const t& val) { + return CImg(img,false)-=val; + } +#else + template inline CImg::type> operator-(const CImg& img, const t2& val) { + typedef typename cimg::largest::type restype; + return CImg(img,false)-=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImg operator-(const t& val, const CImg& img) { + return CImg(img.width,img.height,img.depth,img.dim,val)-=img; + } +#else + template inline CImg::type> operator-(const t1& val, const CImg& img) { + typedef typename cimg::largest::type restype; + return CImg(img.width,img.height,img.depth,img.dim,(restype)val)-=img; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImgList operator-(const CImgList& list, const t& val) { + return CImgList(list)-=val; + } +#else + template inline CImgList::type> operator-(const CImgList& list, const t2& val) { + typedef typename cimg::largest::type restype; + return CImgList(list)-=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImgList operator-(const t& val, const CImgList& list) { + CImgList res(list.size); + cimglist_for(res,l) res[l] = val-list[l]; + return res; + } +#else + template inline CImgList::type> operator-(const t1& val, const CImgList& list) { + typedef typename cimg::largest::type restype; + CImgList res(list.size); + cimglist_for(res,l) res[l] = val-list[l]; + return res; + } +#endif + + template inline CImg::type> operator-(const CImg& img1, const CImg& img2) { + typedef typename cimg::largest::type restype; + return CImg(img1,false)-=img2; + } + + template inline CImgList::type> operator-(const CImg& img, const CImgList& list) { + typedef typename cimg::largest::type restype; + CImgList res(list.size); + cimglist_for(res,l) res[l] = img-list[l]; + return res; + } + + template inline CImgList::type> operator-(const CImgList& list, const CImg& img) { + typedef typename cimg::largest::type restype; + return CImgList(list)-=img; + } + + template inline CImgList::type> operator-(const CImgList& list1, const CImgList& list2) { + typedef typename cimg::largest::type restype; + return CImgList(list1)-=list2; + } + +#ifdef cimg_use_visualcpp6 + template inline CImg operator*(const CImg& img, const double val) { + return CImg(img,false)*=val; + } +#else + template inline CImg::type> operator*(const CImg& img, const t2& val) { + typedef typename cimg::largest::type restype; + return CImg(img,false)*=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImg operator*(const double val, const CImg& img) { + return img*val; + } +#else + template inline CImg::type> operator*(const t1& val, const CImg& img) { + return img*val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImgList operator*(const CImgList& list, const double val) { + return CImgList(list)*=val; + } +#else + template inline CImgList::type> operator*(const CImgList& list, const t2& val) { + typedef typename cimg::largest::type restype; + return CImgList(list)*=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImgList operator*(const double val, const CImgList& list) { + return list*val; + } +#else + template inline CImgList::type> operator*(const t1& val, const CImgList& list) { + return list*val; + } +#endif + + template inline CImg::type> operator*(const CImg& img1, const CImg& img2) { + typedef typename cimg::largest::type restype; + if (img1.width!=img2.height) + throw CImgArgumentException("operator*() : can't multiply a matrix (%ux%u) by a matrix (%ux%u)", + img1.width,img1.height,img2.width,img2.height); + CImg res(img2.width,img1.height); + restype val; + cimg_forXY(res,i,j) { val=0; cimg_forX(img1,k) val+=img1(k,j)*img2(i,k); res(i,j) = val; } + return res; + } + + template inline CImgList::type> operator*(const CImg& img, const CImgList& list) { + typedef typename cimg::largest::type restype; + CImgList res(list.size); + cimglist_for(res,l) res[l] = img*list[l]; + return res; + } + + template inline CImgList::type> operator*(const CImgList& list, const CImg& img) { + typedef typename cimg::largest::type restype; + CImgList res(list.size); + cimglist_for(res,l) res[l] = list[l]*img; + return res; + } + + template inline CImgList::type> operator*(const CImgList& list1, const CImgList& list2) { + typedef typename cimg::largest::type restype; + CImgList res(cimg::min(list1.size,list2.size)); + cimglist_for(res,l) res[l] = list1[l]*list2[l]; + return res; + } + +#ifdef cimg_use_visualcpp6 + template inline CImg operator/(const CImg& img, const double val) { + return CImg(img,false)/=val; + } +#else + template inline CImg::type> operator/(const CImg& img, const t2& val) { + typedef typename cimg::largest::type restype; + return CImg(img,false)/=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImg operator/(const double val, CImg& img) { + return val*img.get_inverse(); + } +#else + template inline CImg::type> operator/(const t1& val, CImg& img) { + return val*img.get_inverse(); + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImgList operator/(const CImgList& list, const double val) { + return CImgList(list)/=val; + } +#else + template inline CImgList::type> operator/(const CImgList& list, const t2& val) { + typedef typename cimg::largest::type restype; + return CImgList(list)/=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template inline CImgList operator/(const double val, const CImgList& list) { + CImgList res(list.size); + cimglist_for(res,l) res[l] = val/list[l]; + return res; + } +#else + template inline CImgList::type> operator/(const t1& val, const CImgList& list) { + typedef typename cimg::largest::type restype; + CImgList res(list.size); + cimglist_for(res,l) res[l] = val/list[l]; + return res; + } +#endif + + template inline CImg::type> operator/(const CImg& img1, const CImg& img2) { + typedef typename cimg::largest::type restype; + return CImg(img1,false)*=img2.get_inverse(); + } + + template inline CImg::type> operator/(const CImg& img, const CImgList& list) { + typedef typename cimg::largest::type restype; + CImgList res(list.size); + cimglist_for(res,l) res[l] = img/list[l]; + return res; + } + + template inline CImgList::type> operator/(const CImgList& list, const CImg& img) { + typedef typename cimg::largest::type restype; + return CImgList(list)/=img; + } + + template inline CImgList::type> operator/(const CImgList& list1, const CImgList& list2) { + typedef typename cimg::largest::type restype; + return CImgList(list1)/=list2; + } + +namespace cimg { + + //! Display a dialog box, where a user can click standard buttons. + /** + Up to 6 buttons can be defined in the dialog window. + This function returns when a user clicked one of the button or closed the dialog window. + \param title = Title of the dialog window. + \param msg = Main message displayed inside the dialog window. + \param button1_txt = Label of the 1st button. + \param button2_txt = Label of the 2nd button. + \param button3_txt = Label of the 3rd button. + \param button4_txt = Label of the 4th button. + \param button5_txt = Label of the 5th button. + \param button6_txt = Label of the 6th button. + \param logo = Logo image displayed at the left of the main message. This parameter is optional. + \param centering = Tell to center the dialog window on the screen. + \return The button number (from 0 to 5), or -1 if the dialog window has been closed by the user. + \note If a button text is set to 0, then the corresponding button (and the followings) won't appear in + the dialog box. At least one button is necessary. + **/ + + template + inline int dialog(const char *title,const char *msg, + const char *button1_txt,const char *button2_txt, + const char *button3_txt,const char *button4_txt, + const char *button5_txt,const char *button6_txt, + const CImg& logo, const bool centering = false) { +#if cimg_display_type!=0 + const unsigned char + black[3]={0,0,0}, white[3]={255,255,255}, gray[3]={200,200,200}, gray2[3]={150,150,150}; + + // Create buttons and canvas graphics + CImgList buttons, cbuttons, sbuttons; + if (button1_txt) { buttons.insert(CImg().draw_text(button1_txt,0,0,black,gray,13)); + if (button2_txt) { buttons.insert(CImg().draw_text(button2_txt,0,0,black,gray,13)); + if (button3_txt) { buttons.insert(CImg().draw_text(button3_txt,0,0,black,gray,13)); + if (button4_txt) { buttons.insert(CImg().draw_text(button4_txt,0,0,black,gray,13)); + if (button5_txt) { buttons.insert(CImg().draw_text(button5_txt,0,0,black,gray,13)); + if (button6_txt) { buttons.insert(CImg().draw_text(button6_txt,0,0,black,gray,13)); + }}}}}} + if (!buttons.size) throw CImgArgumentException("cimg::dialog() : No buttons have been defined. At least one is necessary"); + + unsigned int bw=0, bh=0; + cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l].width); bh = cimg::max(bh,buttons[l].height); } + bw+=8; bh+=8; + if (bw<64) bw=64; + if (bw>128) bw=128; + if (bh<24) bh=24; + if (bh>48) bh=48; + + CImg button = CImg(bw,bh,1,3). + draw_rectangle(0,0,bw-1,bh-1,gray). + draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white). + draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black). + draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2); + CImg sbutton = CImg(bw,bh,1,3). + draw_rectangle(0,0,bw-1,bh-1,gray). + draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black). + draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black). + draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white). + draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black). + draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2). + draw_line(4,4,bw-5,4,black,0xAAAAAAAA).draw_line(bw-5,4,bw-5,bh-5,black,0xAAAAAAAA). + draw_line(bw-5,bh-5,4,bh-5,black,0xAAAAAAAA).draw_line(4,bh-5,4,4,black,0xAAAAAAAA); + CImg cbutton = CImg(bw,bh,1,3). + draw_rectangle(0,0,bw-1,bh-1,black).draw_rectangle(1,1,bw-2,bh-2,gray2).draw_rectangle(2,2,bw-3,bh-3,gray). + draw_line(4,4,bw-5,4,black,0xAAAAAAAA).draw_line(bw-5,4,bw-5,bh-5,black,0xAAAAAAAA). + draw_line(bw-5,bh-5,4,bh-5,black,0xAAAAAAAA).draw_line(4,bh-5,4,4,black,0xAAAAAAAA); + + cimglist_for(buttons,ll) { + cbuttons.insert(CImg(cbutton).draw_image(buttons[ll],1+(bw-buttons[ll].dimx())/2,1+(bh-buttons[ll].dimy())/2)); + sbuttons.insert(CImg(sbutton).draw_image(buttons[ll],(bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2)); + buttons[ll] = CImg(button).draw_image(buttons[ll],(bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2); + } + + CImg canvas; + if (msg) canvas = CImg().draw_text(msg,0,0,black,gray,13); + const unsigned int + bwall = (buttons.size-1)*(12+bw) + bw, + w = cimg::max(196U,36+logo.width+canvas.width, 24+bwall), + h = cimg::max(96U,36+canvas.height+bh,36+logo.height+bh), + lx = 12 + (canvas.data?0:((w-24-logo.width)/2)), + ly = (h-12-bh-logo.height)/2, + tx = lx+logo.width+12, + ty = (h-12-bh-canvas.height)/2, + bx = (w-bwall)/2, + by = h-12-bh; + + if (canvas.data) + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w-1,h-1,gray). + draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). + draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black). + draw_image(canvas,tx,ty); + else + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w-1,h-1,gray). + draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). + draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black); + if (logo.data) canvas.draw_image(logo,lx,ly); + + unsigned int xbuttons[6]; + cimglist_for(buttons,lll) { xbuttons[lll] = bx+(bw+12)*lll; canvas.draw_image(buttons[lll],xbuttons[lll],by); } + + // Open window and enter events loop + CImgDisplay disp(canvas,title?title:" ",0,3,false,centering?true:false); + if (centering) disp.move((CImgDisplay::screen_dimx()-disp.dimx())/2, + (CImgDisplay::screen_dimy()-disp.dimy())/2); + bool stopflag = false, refresh = false; + int oselected = -1, oclicked = -1, selected = -1, clicked = -1; + while (!disp.is_closed && !stopflag) { + if (refresh) { + if (clicked>=0) CImg(canvas).draw_image(cbuttons[clicked],xbuttons[clicked],by).display(disp); + else { + if (selected>=0) CImg(canvas).draw_image(sbuttons[selected],xbuttons[selected],by).display(disp); + else canvas.display(disp); + } + refresh = false; + } + disp.wait(15); + if (disp.is_resized) disp.resize(disp); + + if (disp.button&1) { + oclicked = clicked; + clicked = -1; + cimglist_for(buttons,l) + if (disp.mouse_y>=(int)by && disp.mouse_y<(int)(by+bh) && + disp.mouse_x>=(int)xbuttons[l] && disp.mouse_x<(int)(xbuttons[l]+bw)) { + clicked = selected = l; + refresh = true; + } + if (clicked!=oclicked) refresh = true; + } else if (clicked>=0) stopflag = true; + + if (disp.key) { + oselected = selected; + switch (disp.key) { + case cimg::keyESC: selected=-1; stopflag=true; break; + case cimg::keyENTER: if (selected<0) selected=0; stopflag = true; break; + case cimg::keyTAB: + case cimg::keyARROWRIGHT: + case cimg::keyARROWDOWN: selected = (selected+1)%buttons.size; break; + case cimg::keyARROWLEFT: + case cimg::keyARROWUP: selected = (selected+buttons.size-1)%buttons.size; break; + } + disp.key = 0; + if (selected!=oselected) refresh = true; + } + } + if (disp.is_closed) selected = -1; + return selected; +#else + std::fprintf(stderr,"<%s>\n\n%s\n\n",title,msg); + return -1+0*(int)(button1_txt-button2_txt+button3_txt-button4_txt+button5_txt-button6_txt+logo.width+(int)centering); +#endif + } + + inline int dialog(const char *title,const char *msg, + const char *button1_txt,const char *button2_txt,const char *button3_txt, + const char *button4_txt,const char *button5_txt,const char *button6_txt, + const bool centering) { + return dialog(title,msg,button1_txt,button2_txt,button3_txt,button4_txt,button5_txt,button6_txt, + CImg::get_logo40x38(),centering); + } + + + // Inner routine used by the Marching cube algorithm + template inline int _marching_cubes_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) { + switch (edge) { + case 0: return indices1(x,y,0); + case 1: return indices1(nx,y,1); + case 2: return indices1(x,ny,0); + case 3: return indices1(x,y,1); + case 4: return indices2(x,y,0); + case 5: return indices2(nx,y,1); + case 6: return indices2(x,ny,0); + case 7: return indices2(x,y,1); + case 8: return indices1(x,y,2); + case 9: return indices1(nx,y,2); + case 10: return indices1(nx,ny,2); + case 11: return indices1(x,ny,2); + } + return 0; + } + + //! Polygonize an implicit function + // This function uses the Marching Cubes Tables published on the web page : + // http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/ + template + inline void marching_cubes(const tfunc& func, const float isovalue, + const float x0,const float y0,const float z0, + const float x1,const float y1,const float z1, + const float resx,const float resy,const float resz, + CImgList& points, CImgList& primitives, + const bool invert_faces) { + + static unsigned int edges[256]={ + 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 }; + + static int triangles[256][16] = + {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; + + const unsigned int + nx = (unsigned int)((x1-x0+1)/resx), nxm1 = nx-1, + ny = (unsigned int)((y1-y0+1)/resy), nym1 = ny-1, + nz = (unsigned int)((z1-z0+1)/resz), nzm1 = nz-1; + + if (!nxm1 || !nym1 || !nzm1) return; + + CImg indices1(nx,ny,1,3,-1), indices2(indices1); + CImg values1(nx,ny), values2(nx,ny); + float X=0, Y=0, Z=0, nX=0, nY=0, nZ=0; + + // Fill the first plane with function values + Y=y0; + cimg_forY(values1,y) { + X = x0; + cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=resx; } + Y+=resy; + } + + // Run Marching Cubes algorithm + Z = z0; nZ = Z + resz; + for (unsigned int zi=0; zi::vector(Xi,Y,Z)); + } + if ((edge&2) && indices1(nxi,yi,1)<0) { + const float Yi = Y + (isovalue-val1)*resy/(val2-val1); + indices1(nxi,yi,1) = points.size; + points.insert(CImg::vector(nX,Yi,Z)); + } + if ((edge&4) && indices1(xi,nyi,0)<0) { + const float Xi = X + (isovalue-val3)*resx/(val2-val3); + indices1(xi,nyi,0) = points.size; + points.insert(CImg::vector(Xi,nY,Z)); + } + if ((edge&8) && indices1(xi,yi,1)<0) { + const float Yi = Y + (isovalue-val0)*resy/(val3-val0); + indices1(xi,yi,1) = points.size; + points.insert(CImg::vector(X,Yi,Z)); + } + if ((edge&16) && indices2(xi,yi,0)<0) { + const float Xi = X + (isovalue-val4)*resx/(val5-val4); + indices2(xi,yi,0) = points.size; + points.insert(CImg::vector(Xi,Y,nZ)); + } + if ((edge&32) && indices2(nxi,yi,1)<0) { + const float Yi = Y + (isovalue-val5)*resy/(val6-val5); + indices2(nxi,yi,1) = points.size; + points.insert(CImg::vector(nX,Yi,nZ)); + } + if ((edge&64) && indices2(xi,nyi,0)<0) { + const float Xi = X + (isovalue-val7)*resx/(val6-val7); + indices2(xi,nyi,0) = points.size; + points.insert(CImg::vector(Xi,nY,nZ)); + } + if ((edge&128) && indices2(xi,yi,1)<0) { + const float Yi = Y + (isovalue-val4)*resy/(val7-val4); + indices2(xi,yi,1) = points.size; + points.insert(CImg::vector(X,Yi,nZ)); + } + if ((edge&256) && indices1(xi,yi,2)<0) { + const float Zi = Z+ (isovalue-val0)*resz/(val4-val0); + indices1(xi,yi,2) = points.size; + points.insert(CImg::vector(X,Y,Zi)); + } + if ((edge&512) && indices1(nxi,yi,2)<0) { + const float Zi = Z + (isovalue-val1)*resz/(val5-val1); + indices1(nxi,yi,2) = points.size; + points.insert(CImg::vector(nX,Y,Zi)); + } + if ((edge&1024) && indices1(nxi,nyi,2)<0) { + const float Zi = Z + (isovalue-val2)*resz/(val6-val2); + indices1(nxi,nyi,2) = points.size; + points.insert(CImg::vector(nX,nY,Zi)); + } + if ((edge&2048) && indices1(xi,nyi,2)<0) { + const float Zi = Z + (isovalue-val3)*resz/(val7-val3); + indices1(xi,nyi,2) = points.size; + points.insert(CImg::vector(X,nY,Zi)); + } + + // Create triangles + for (int *triangle=triangles[configuration]; *triangle!=-1; ) { + const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++); + const tf + i0 = (tf)(_marching_cubes_indice(p0,indices1,indices2,xi,yi,nxi,nyi)), + i1 = (tf)(_marching_cubes_indice(p1,indices1,indices2,xi,yi,nxi,nyi)), + i2 = (tf)(_marching_cubes_indice(p2,indices1,indices2,xi,yi,nxi,nyi)); + if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2)); + else primitives.insert(CImg::vector(i0,i2,i1)); + } + } + } + } + cimg::swap(values1,values2); + cimg::swap(indices1,indices2); + } + } + + // Inner routine used by the Marching square algorithm + template inline int _marching_squares_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int nx) { + switch (edge) { + case 0: return (int)indices1(x,0); + case 1: return (int)indices1(nx,1); + case 2: return (int)indices2(x,0); + case 3: return (int)indices1(x,1); + } + return 0; + } + + //! Polygonize an implicit 2D function by the marching squares algorithm + template + inline void marching_squares(const tfunc& func, const float isovalue, + const float x0,const float y0, + const float x1,const float y1, + const float resx,const float resy, + CImgList& points, CImgList& primitives) { + + static unsigned int edges[16]={ 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; + static int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 }, + { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 }, + { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 }, + { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } }; + const unsigned int + nx = (unsigned int)((x1-x0+1)/resx), nxm1 = nx-1, + ny = (unsigned int)((y1-y0+1)/resy), nym1 = ny-1; + + if (!nxm1 || !nym1) return; + + CImg indices1(nx,1,1,2,-1), indices2(nx,1,1,2); + CImg values1(nx), values2(nx); + float X = 0, Y = 0, nX = 0, nY = 0; + + // Fill first line with values + cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=resx; } + + // Run the marching squares algorithm + Y = y0; nY = Y + resy; + for (unsigned int yi=0, nyi=1; yi::vector(Xi,Y)); + } + if ((edge&2) && indices1(nxi,1)<0) { + const float Yi = Y + (isovalue-val1)*resy/(val2-val1); + indices1(nxi,1) = points.size; + points.insert(CImg::vector(nX,Yi)); + } + if ((edge&4) && indices2(xi,0)<0) { + const float Xi = X + (isovalue-val3)*resx/(val2-val3); + indices2(xi,0) = points.size; + points.insert(CImg::vector(Xi,nY)); + } + if ((edge&8) && indices1(xi,1)<0) { + const float Yi = Y + (isovalue-val0)*resy/(val3-val0); + indices1(xi,1) = points.size; + points.insert(CImg::vector(X,Yi)); + } + + // Create segments + for (int *segment=segments[configuration]; *segment!=-1; ) { + const unsigned int p0 = *(segment++), p1 = *(segment++); + const tf + i0 = (tf)(_marching_squares_indice(p0,indices1,indices2,xi,nxi)), + i1 = (tf)(_marching_squares_indice(p1,indices1,indices2,xi,nxi)); + primitives.insert(CImg::vector(i0,i1)); + } + } + } + values1.swap(values2); + indices1.swap(indices2); + } + } + + // End of cimg:: namespace +} + + + // End of cimg_library:: namespace +} + +#ifdef std +#undef std +#endif + +#endif + +// Local Variables: +// mode: c++ +// End: diff --git a/libvips/cimg/Makefile.am b/libvips/cimg/Makefile.am new file mode 100644 index 00000000..254620e3 --- /dev/null +++ b/libvips/cimg/Makefile.am @@ -0,0 +1,24 @@ +noinst_LTLIBRARIES = libcimg.la + +libcimg_la_SOURCES = \ + CImg.h \ + cimg_dispatch.c \ + cimg.cpp + +# various cimg settings as well +# we need to change these a bit for win32 +if OS_WIN32 +AM_CPPFLAGS = \ + -Dcimg_strict \ + -Dcimg_OS=0 \ + -Dcimg_display_type=0 \ + -DLOCALEDIR=\""$(LOCALEDIR)"\" +else +AM_CPPFLAGS = \ + -Dcimg_strict \ + -Dcimg_OS=1 \ + -Dcimg_display_type=0 \ + -DLOCALEDIR=\""$(LOCALEDIR)"\" +endif + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/cimg/cimg.cpp b/libvips/cimg/cimg.cpp new file mode 100644 index 00000000..12fd2771 --- /dev/null +++ b/libvips/cimg/cimg.cpp @@ -0,0 +1,288 @@ +/* @(#) Pass VIPS images through CImg + * + * JC, 15/10/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 + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +/* CImg needs to call pthread directly, this is the preproc magic they + * prefer. + */ +#if defined(sun) || defined(__sun) || defined(linux) || defined(__linux) \ + || defined(__linux__) || defined(__CYGWIN__) || defined(BSD) || defined(__FreeBSD__) \ + || defined(__OPENBSD__) || defined(__MACOSX__) || defined(__APPLE__) || defined(sgi) \ + || defined(__sgi) + #include +#endif + +#include "CImg.h" +using namespace cimg_library; + +/* Save params here. + */ +struct Greyc { + IMAGE *in; + IMAGE *out; + IMAGE *mask; + IMAGE **arry; + + int iterations; + float amplitude; + float sharpness; + float anisotropy; + float alpha; + float sigma; + float dl; + float da; + float gauss_prec; + int interpolation; + bool fast_approx; +}; + +// copy part of a vips region into a cimg +template static CImg * +vips_to_cimg( REGION *in, Rect *area ) +{ + IMAGE *im = in->im; + CImg *img = new CImg( area->width, area->height, 1, im->Bands ); + + for( int y = 0; y < area->height; y++ ) { + T *p = (T *) IM_REGION_ADDR( in, area->left, area->top + y ); + + for( int x = 0; x < area->width; x++ ) { + for( int z = 0; z < im->Bands; z++ ) + (*img)( x, y, z ) = p[z]; + + p += im->Bands; + } + } + + return( img ); +} + +// write a CImg to a vips region +// fill out->valid, img has pixels in img_rect +template static void +cimg_to_vips( CImg *img, Rect *img_rect, REGION *out ) +{ + IMAGE *im = out->im; + Rect *valid = &out->valid; + + g_assert( im_rect_includesrect( img_rect, valid ) ); + + int x_off = valid->left - img_rect->left; + int y_off = valid->top - img_rect->top; + + for( int y = 0; y < valid->height; y++ ) { + T *p = (T *) IM_REGION_ADDR( out, valid->left, valid->top + y ); + + for( int x = 0; x < valid->width; x++ ) { + for( int z = 0; z < im->Bands; z++ ) + p[z] = static_cast( (*img)( + x + x_off, y + y_off, z ) ); + + p += im->Bands; + } + } +} + +template static int +greyc_gen( REGION *out, REGION **in, IMAGE **arry, Greyc *greyc ) +{ + static const float gfact = (sizeof( T ) == 2) ? 1.0 / 256 : 1.0; + static const int tile_border = 4; + + Rect *ir = &out->valid; + Rect need; + Rect image; + + CImg *img; + CImg *msk; + + need = *ir; + im_rect_marginadjust( &need, tile_border ); + image.left = 0; + image.top = 0; + image.width = in[0]->im->Xsize; + image.height = in[0]->im->Ysize; + im_rect_intersectrect( &need, &image, &need ); + if( im_prepare( in[0], &need ) ) + return( -1 ); + if( in[1] && im_prepare( in[1], &need ) ) + return( -1 ); + + img = NULL; + msk = NULL; + + try { + img = vips_to_cimg( in[0], &need ); + if( in[1] ) + msk = vips_to_cimg( in[1], &need ); + else + // empty mask + msk = new CImg(); + + for( int i = 0; i < greyc->iterations; i++ ) + img->blur_anisotropic( *msk, + greyc->amplitude, greyc->sharpness, + greyc->anisotropy, + greyc->alpha, greyc->sigma, greyc->dl, + greyc->da, greyc->gauss_prec, + greyc->interpolation, greyc->fast_approx, + gfact ); + + cimg_to_vips( img, &need, out ); + } + catch( CImgException e ) { + if( img ) + delete( img ); + if( msk ) + delete( msk ); + + im_error( "GREYCstoration", "%s", e.message ); + + return( -1 ); + } + + if( img ) + delete( img ); + if( msk ) + delete( msk ); + + return( 0 ); +} + +// Hmm, strange double-cast needed +typedef int (*generate_fn)( REGION *out, REGION **in, + IMAGE **im, Greyc *greyc ); + +// as a plain C function +int +im_greyc_mask( IMAGE *in, IMAGE *out, IMAGE *mask, + int iterations, + float amplitude, float sharpness, float anisotropy, + float alpha, float sigma, + float dl, float da, float gauss_prec, + int interpolation, int fast_approx ) +{ + IMAGE **arry; + Greyc *greyc; + + if( im_piocheck( in, out ) ) + return( -1 ); + if( in->Coding != IM_CODING_NONE ) { + im_error( "GREYCstoration", "%s", _( "uncoded only" ) ); + return( -1 ); + } + if( mask ) { + if( mask->Coding != IM_CODING_NONE ) { + im_error( "GREYCstoration", "%s", _( "uncoded only" ) ); + return( -1 ); + } + if( mask->Xsize != in->Xsize || + mask->Ysize != in->Ysize ) { + im_error( "GREYCstoration", + "%s", _( "mask size does not match input" ) ); + return( -1 ); + } + if( mask->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "GREYCstoration", + "%s", _( "mask must be uchar" ) ); + return( -1 ); + } + } + im_cp_desc( out, in ); + if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) + return( -1 ); + if( !(arry = im_allocate_input_array( out, in, mask, NULL )) ) + return( -1 ); + + if( !(greyc = IM_NEW( out, Greyc )) ) + return( -1 ); + greyc->in = in; + greyc->out = out; + greyc->mask = mask; + greyc->arry = arry; + greyc->iterations = iterations; + greyc->amplitude = amplitude; + greyc->sharpness = sharpness; + greyc->anisotropy = anisotropy; + greyc->alpha = alpha; + greyc->sigma = sigma; + greyc->dl = dl; + greyc->da = da; + greyc->gauss_prec = gauss_prec; + greyc->interpolation = interpolation; + greyc->fast_approx = fast_approx; + + switch( in->BandFmt ) { + case IM_BANDFMT_UCHAR: + if( im_generate( out, + im_start_many, + // double-cast to give g++ enough context to expand the + // template correctly + (im_generate_fn) ( + (generate_fn) greyc_gen), + im_stop_many, arry, greyc ) ) + return( -1 ); + break; + + case IM_BANDFMT_USHORT: + if( im_generate( out, + im_start_many, + (im_generate_fn) ( + (generate_fn) greyc_gen), + im_stop_many, arry, greyc ) ) + return( -1 ); + break; + + case IM_BANDFMT_FLOAT: + if( im_generate( out, + im_start_many, + (im_generate_fn) ( + (generate_fn) greyc_gen), + im_stop_many, arry, greyc ) ) + return( -1 ); + break; + + default: + im_error( "GREYCstoration", + "%s", _( "unsupported type: " + "uchar, ushort and float only" ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libvips/cimg/cimg_dispatch.c b/libvips/cimg/cimg_dispatch.c new file mode 100644 index 00000000..30397c2b --- /dev/null +++ b/libvips/cimg/cimg_dispatch.c @@ -0,0 +1,164 @@ +/* Function dispatch tables for cimg wrappers. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 +greyc_vec( im_object *argv ) +{ + IMAGE *src = (IMAGE *) argv[0]; + IMAGE *dst = (IMAGE *) argv[1]; + + int iterations = *((int *) argv[2]); + double amplitude = *((double *) argv[3]); + double sharpness = *((double *) argv[4]); + double anisotropy = *((double *) argv[5]); + double alpha = *((double *) argv[6]); + double sigma = *((double *) argv[7]); + double dl = *((double *) argv[8]); + double da = *((double *) argv[9]); + double gauss_prec = *((double *) argv[10]); + int interpolation = *((int *) argv[11]); + int fast_approx = *((int *) argv[12]); + + if( im_greyc_mask( src, dst, NULL, + iterations, + amplitude, sharpness, anisotropy, + alpha, sigma, + dl, da, gauss_prec, + interpolation, fast_approx ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc greyc_arg_types[] = { + IM_INPUT_IMAGE( "src" ), + IM_OUTPUT_IMAGE( "dst" ), + IM_INPUT_INT( "iterations" ), + IM_INPUT_DOUBLE( "amplitude" ), + IM_INPUT_DOUBLE( "sharpness" ), + IM_INPUT_DOUBLE( "anisotropy" ), + IM_INPUT_DOUBLE( "alpha" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "dl" ), + IM_INPUT_DOUBLE( "da" ), + IM_INPUT_DOUBLE( "gauss_prec" ), + IM_INPUT_INT( "interpolation" ), + IM_INPUT_INT( "fast_approx" ) +}; + +static im_function greyc_desc = { + "im_greyc", /* Name */ + "noise-removing filter", /* Description */ + (im_fn_flags) (IM_FN_TRANSFORM | IM_FN_PIO),/* Flags */ + greyc_vec, /* Dispatch function */ + IM_NUMBER( greyc_arg_types ), /* Size of arg list */ + greyc_arg_types /* Arg list */ +}; + +static int +greyc_mask_vec( im_object *argv ) +{ + IMAGE *src = (IMAGE *) argv[0]; + IMAGE *dst = (IMAGE *) argv[1]; + IMAGE *mask = (IMAGE *) argv[2]; + + int iterations = *((int *) argv[3]); + double amplitude = *((double *) argv[4]); + double sharpness = *((double *) argv[5]); + double anisotropy = *((double *) argv[6]); + double alpha = *((double *) argv[7]); + double sigma = *((double *) argv[8]); + double dl = *((double *) argv[9]); + double da = *((double *) argv[10]); + double gauss_prec = *((double *) argv[11]); + int interpolation = *((int *) argv[12]); + int fast_approx = *((int *) argv[13]); + + if( im_greyc_mask( src, dst, mask, + iterations, + amplitude, sharpness, anisotropy, + alpha, sigma, + dl, da, gauss_prec, + interpolation, fast_approx ) ) + return( -1 ); + + return( 0 ); +} + +static im_arg_desc greyc_mask_arg_types[] = { + IM_INPUT_IMAGE( "src" ), + IM_OUTPUT_IMAGE( "dst" ), + IM_INPUT_IMAGE( "mask" ), + IM_INPUT_INT( "iterations" ), + IM_INPUT_DOUBLE( "amplitude" ), + IM_INPUT_DOUBLE( "sharpness" ), + IM_INPUT_DOUBLE( "anisotropy" ), + IM_INPUT_DOUBLE( "alpha" ), + IM_INPUT_DOUBLE( "sigma" ), + IM_INPUT_DOUBLE( "dl" ), + IM_INPUT_DOUBLE( "da" ), + IM_INPUT_DOUBLE( "gauss_prec" ), + IM_INPUT_INT( "interpolation" ), + IM_INPUT_INT( "fast_approx" ) +}; + +static im_function greyc_mask_desc = { + "im_greyc_mask", /* Name */ + "noise-removing filter, with a mask", /* Description */ + (im_fn_flags) (IM_FN_TRANSFORM | IM_FN_PIO),/* Flags */ + greyc_mask_vec, /* Dispatch function */ + IM_NUMBER( greyc_mask_arg_types ),/* Size of arg list */ + greyc_mask_arg_types /* Arg list */ +}; + +static im_function *function_list[] = { + &greyc_desc, + &greyc_mask_desc +}; + +/* Package of functions. + */ +im_package im__cimg = { + "cimg", + IM_NUMBER( function_list ), + function_list +}; diff --git a/libvips/colour/Makefile.am b/libvips/colour/Makefile.am new file mode 100644 index 00000000..193b91a1 --- /dev/null +++ b/libvips/colour/Makefile.am @@ -0,0 +1,32 @@ +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_float2rad.c \ + im_Yxy2XYZ.c \ + im_XYZ2disp.c \ + im_dE00_fromLab.c \ + im_dECMC_fromLab.c \ + im_dE_fromLab.c \ + im_disp2XYZ.c \ + im_rad2float.c + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/colour/colour.c b/libvips/colour/colour.c new file mode 100644 index 00000000..1675e787 --- /dev/null +++ b/libvips/colour/colour.c @@ -0,0 +1,1125 @@ +/* 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 + * 14/3/08 + * - more tiny cond jump valgrind fixes + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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; + float C; + float 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++ ) { + int j; + + for( j = 0; j < 3001 && Cl[j] <= i / 10.0; 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) % 360] - 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/libvips/colour/colour_dispatch.c b/libvips/colour/colour_dispatch.c new file mode 100644 index 00000000..f77b4b34 --- /dev/null +++ b/libvips/colour/colour_dispatch.c @@ -0,0 +1,1052 @@ +/* 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_rad2float() via arg vector. + */ +static int +rad2float_vec( im_object *argv ) +{ + return( im_rad2float( argv[0], argv[1] ) ); +} + +/* Description of im_rad2float. + */ +static im_function rad2float_desc = { + "im_rad2float", /* Name */ + "convert Radiance packed to float", /* Description */ + IM_FN_PIO, /* Flags */ + rad2float_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + +/* Call im_float2rad() via arg vector. + */ +static int +float2rad_vec( im_object *argv ) +{ + return( im_float2rad( argv[0], argv[1] ) ); +} + +/* Description of im_float2rad + */ +static im_function float2rad_desc = { + "im_float2rad", /* Name */ + "convert float to Radiance packed", /* Description */ + IM_FN_PIO, /* Flags */ + float2rad_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, + &float2rad_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, + &rad2float_desc, + &sRGB2XYZ_desc +}; + +/* Package of functions. + */ +im_package im__colour = { + "colour", + IM_NUMBER( colour_list ), + colour_list +}; diff --git a/libvips/colour/derived.c b/libvips/colour/derived.c new file mode 100644 index 00000000..92109672 --- /dev/null +++ b/libvips/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/libvips/colour/im_LCh2Lab.c b/libvips/colour/im_LCh2Lab.c new file mode 100644 index 00000000..4e7b1c2b --- /dev/null +++ b/libvips/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/libvips/colour/im_LCh2UCS.c b/libvips/colour/im_LCh2UCS.c new file mode 100644 index 00000000..a826ff47 --- /dev/null +++ b/libvips/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/libvips/colour/im_Lab2LCh.c b/libvips/colour/im_Lab2LCh.c new file mode 100644 index 00000000..8cbdf471 --- /dev/null +++ b/libvips/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/libvips/colour/im_Lab2LabQ.c b/libvips/colour/im_Lab2LabQ.c new file mode 100644 index 00000000..70dc4254 --- /dev/null +++ b/libvips/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/libvips/colour/im_Lab2LabS.c b/libvips/colour/im_Lab2LabS.c new file mode 100644 index 00000000..d83ba931 --- /dev/null +++ b/libvips/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/libvips/colour/im_Lab2XYZ.c b/libvips/colour/im_Lab2XYZ.c new file mode 100644 index 00000000..9749248c --- /dev/null +++ b/libvips/colour/im_Lab2XYZ.c @@ -0,0 +1,147 @@ +/* @(#) 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, a, b; + float X, Y, Z; + double cby, tmp; + + L = p[0]; + a = p[1]; + b = p[2]; + 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; + + /* Check input image. + */ + if( !(temp = IM_NEW( out, im_colour_temperature )) ) + return( -1 ); + if( in->Bands != 3 || + in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_error( "im_Lab2XYZ", "%s", _( "not 3-band uncoded float" ) ); + 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/libvips/colour/im_LabQ2Lab.c b/libvips/colour/im_LabQ2Lab.c new file mode 100644 index 00000000..327ec18d --- /dev/null +++ b/libvips/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/libvips/colour/im_LabQ2LabS.c b/libvips/colour/im_LabQ2LabS.c new file mode 100644 index 00000000..f2fe3a0d --- /dev/null +++ b/libvips/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/libvips/colour/im_LabQ2disp.c b/libvips/colour/im_LabQ2disp.c new file mode 100644 index 00000000..88fac6ec --- /dev/null +++ b/libvips/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/libvips/colour/im_LabS2Lab.c b/libvips/colour/im_LabS2Lab.c new file mode 100644 index 00000000..58c8b431 --- /dev/null +++ b/libvips/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/libvips/colour/im_LabS2LabQ.c b/libvips/colour/im_LabS2LabQ.c new file mode 100644 index 00000000..5e47764a --- /dev/null +++ b/libvips/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/libvips/colour/im_UCS2LCh.c b/libvips/colour/im_UCS2LCh.c new file mode 100644 index 00000000..84085e02 --- /dev/null +++ b/libvips/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/libvips/colour/im_XYZ2Lab.c b/libvips/colour/im_XYZ2Lab.c new file mode 100644 index 00000000..51c33cf5 --- /dev/null +++ b/libvips/colour/im_XYZ2Lab.c @@ -0,0 +1,190 @@ +/* @(#) 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 +#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 was_built; + int i; + + g_mutex_lock( im__global_lock ); + was_built = built_tables; + built_tables = 1; + g_mutex_unlock( im__global_lock ); + if( was_built ) + 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 ); + } +} + +/* 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; + + /* Check input image. + */ + if( !(temp = IM_NEW( out, im_colour_temperature )) ) + return( -1 ); + if( in->Bands != 3 || + in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_error( "im_XYZ2Lab", "%s", _( "not 3-band uncoded float" ) ); + 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/libvips/colour/im_XYZ2Yxy.c b/libvips/colour/im_XYZ2Yxy.c new file mode 100644 index 00000000..9c8b520d --- /dev/null +++ b/libvips/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/libvips/colour/im_XYZ2disp.c b/libvips/colour/im_XYZ2disp.c new file mode 100644 index 00000000..335ff1c7 --- /dev/null +++ b/libvips/colour/im_XYZ2disp.c @@ -0,0 +1,165 @@ +/* @(#) 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_error( "im_XYZ2disp", + "%s", _( "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/libvips/colour/im_Yxy2XYZ.c b/libvips/colour/im_Yxy2XYZ.c new file mode 100644 index 00000000..6dd1ea09 --- /dev/null +++ b/libvips/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/libvips/colour/im_dE00_fromLab.c b/libvips/colour/im_dE00_fromLab.c new file mode 100644 index 00000000..a01b3fdb --- /dev/null +++ b/libvips/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/libvips/colour/im_dECMC_fromLab.c b/libvips/colour/im_dECMC_fromLab.c new file mode 100644 index 00000000..70a6761b --- /dev/null +++ b/libvips/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/libvips/colour/im_dE_fromLab.c b/libvips/colour/im_dE_fromLab.c new file mode 100644 index 00000000..27e07574 --- /dev/null +++ b/libvips/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/libvips/colour/im_disp2XYZ.c b/libvips/colour/im_disp2XYZ.c new file mode 100644 index 00000000..5e692d8d --- /dev/null +++ b/libvips/colour/im_disp2XYZ.c @@ -0,0 +1,116 @@ +/* @(#) 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_error( "im_disp2XYZ", + "%s", _( "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/libvips/colour/im_float2rad.c b/libvips/colour/im_float2rad.c new file mode 100644 index 00000000..aa06a9f5 --- /dev/null +++ b/libvips/colour/im_float2rad.c @@ -0,0 +1,207 @@ +/* Convert float to Radiance 32bit packed format + * 23/3/09 + * - from im_rad2float and Radiance sources + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + + Sections of this file from Greg Ward and Radiance with kind + permission. The Radience copyright notice appears below. + + */ + +/* ==================================================================== + * The Radiance Software License, Version 1.0 + * + * Copyright (c) 1990 - 2009 The Regents of the University of California, + * through Lawrence Berkeley National Laboratory. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes Radiance software + * (http://radsite.lbl.gov/) + * developed by the Lawrence Berkeley National Laboratory + * (http://www.lbl.gov/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Radiance," "Lawrence Berkeley National Laboratory" + * and "The Regents of the University of California" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact radiance@radsite.lbl.gov. + * + * 5. Products derived from this software may not be called "Radiance", + * nor may "Radiance" appear in their name, without prior written + * permission of Lawrence Berkeley National Laboratory. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Lawrence Berkeley National Laboratory OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of Lawrence Berkeley National Laboratory. For more + * information on Lawrence Berkeley National Laboratory, please see + * . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Begin copy-paste from Radiance sources. + */ + +#define RED 0 +#define GRN 1 +#define BLU 2 +#define CIEX 0 /* or, if input is XYZ... */ +#define CIEY 1 +#define CIEZ 2 +#define EXP 3 /* exponent same for either format */ +#define COLXS 128 /* excess used for exponent */ +#define WHT 3 /* used for RGBPRIMS type */ + +#undef BYTE +#define BYTE unsigned char /* 8-bit unsigned integer */ + +typedef BYTE COLR[4]; /* red, green, blue (or X,Y,Z), exponent */ + +typedef float COLORV; +typedef COLORV COLOR[3]; /* red, green, blue (or X,Y,Z) */ + +#define copycolor(c1,c2) ((c1)[0]=(c2)[0],(c1)[1]=(c2)[1],(c1)[2]=(c2)[2]) + +static void +setcolr( COLR clr, double r, double g, double b ) /* assign a short color value */ +{ + double d; + int e; + + d = r > g ? r : g; + if (b > d) d = b; + + if (d <= 1e-32) { + clr[RED] = clr[GRN] = clr[BLU] = 0; + clr[EXP] = 0; + return; + } + + d = frexp(d, &e) * 255.9999 / d; + + if (r > 0.0) + clr[RED] = r * d; + else + clr[RED] = 0; + if (g > 0.0) + clr[GRN] = g * d; + else + clr[GRN] = 0; + if (b > 0.0) + clr[BLU] = b * d; + else + clr[BLU] = 0; + + clr[EXP] = e + COLXS; +} + + + + +/* End copy-paste from Radiance sources. + */ + + +static void +float2rad( COLOR *inp, COLR *outbuf, int n ) +{ + while (n-- > 0) { + setcolr( outbuf[0], inp[0][RED], inp[0][GRN], inp[0][BLU] ); + inp++; + outbuf++; + } +} + +int +im_float2rad( IMAGE *in, IMAGE *out ) +{ + /* Must be 3-band float. + */ + if( in->Bands != 3 || in->BandFmt != IM_BANDFMT_FLOAT || + in->Coding != IM_CODING_NONE ) { + im_error( "im_float2rad", "%s", + _( "3-band float uncoded only" ) ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = 4; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = im_bits_of_fmt( out->BandFmt ); + out->Coding = IM_CODING_RAD; + + if( im_wrapone( in, out, + (im_wrapone_fn) float2rad, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/colour/im_icc_transform.c b/libvips/colour/im_icc_transform.c new file mode 100644 index 00000000..8aa2cb30 --- /dev/null +++ b/libvips/colour/im_icc_transform.c @@ -0,0 +1,821 @@ +/* @(#) 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 + * 6/4/09 + * - catch lcms error messages + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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", "%s", + _( "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", "%s", + _( "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", "%s", + _( "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", "%s", + _( "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", "%s", + _( "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", "%s", + _( "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; + +/* Error from lcms. + */ +static int +icc_error( int code, const char *text ) +{ + if( code == LCMS_ERRC_WARNING ) + im_warn( "im_icc", "%s", text ); + else + im_error( "im_icc", "%s", text ); + + return( 0 ); +} + +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 ); + cmsSetErrorHandler( icc_error ); + + 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", + "%s", _( "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", + "%s", _( "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; + } +} + +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", "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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_typeof( in, IM_META_ICC_NAME ) == 0 ) { + im_error( "im_icc_import_embedded", + "%s", _( "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; + } + + /* Do IM_CODING_RAD. + */ + if( in->Coding == IM_CODING_RAD ) { + IMAGE *t1 = im_open_local( out, "im_icc_export:1", "p" ); + + if( !t1 || im_rad2float( 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", + "%s", _( "3-band uncoded Lab float only" ) ); + return( -1 ); + } + + if( depth != 8 && depth != 16 ) { + im_error( "im_icc_export", "%s", _( "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", "%s", _( "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; + } + + /* Do IM_CODING_RAD. + */ + if( in->Coding == IM_CODING_RAD ) { + IMAGE *t1 = im_open_local( out, "im_icc_export:1", "p" ); + + if( !t1 || im_rad2float( 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/libvips/colour/im_lab_morph.c b/libvips/colour/im_lab_morph.c new file mode 100644 index 00000000..e4ac277c --- /dev/null +++ b/libvips/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/libvips/colour/im_rad2float.c b/libvips/colour/im_rad2float.c new file mode 100644 index 00000000..c3df75fe --- /dev/null +++ b/libvips/colour/im_rad2float.c @@ -0,0 +1,194 @@ +/* Convert Radiance 32bit packed format to float. + * 3/3/09 + * - from LabQ2Lab and Radiance sources + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + + Sections of this file from Greg Ward and Radiance with kind + permission. The Radience copyright notice appears below. + + */ + +/* ==================================================================== + * The Radiance Software License, Version 1.0 + * + * Copyright (c) 1990 - 2009 The Regents of the University of California, + * through Lawrence Berkeley National Laboratory. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes Radiance software + * (http://radsite.lbl.gov/) + * developed by the Lawrence Berkeley National Laboratory + * (http://www.lbl.gov/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Radiance," "Lawrence Berkeley National Laboratory" + * and "The Regents of the University of California" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact radiance@radsite.lbl.gov. + * + * 5. Products derived from this software may not be called "Radiance", + * nor may "Radiance" appear in their name, without prior written + * permission of Lawrence Berkeley National Laboratory. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Lawrence Berkeley National Laboratory OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of Lawrence Berkeley National Laboratory. For more + * information on Lawrence Berkeley National Laboratory, please see + * . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Begin copy-paste from Radiance sources. + */ + +#define RED 0 +#define GRN 1 +#define BLU 2 +#define CIEX 0 /* or, if input is XYZ... */ +#define CIEY 1 +#define CIEZ 2 +#define EXP 3 /* exponent same for either format */ +#define COLXS 128 /* excess used for exponent */ +#define WHT 3 /* used for RGBPRIMS type */ + +#undef BYTE +#define BYTE unsigned char /* 8-bit unsigned integer */ + +typedef BYTE COLR[4]; /* red, green, blue (or X,Y,Z), exponent */ + +typedef float COLORV; +typedef COLORV COLOR[3]; /* red, green, blue (or X,Y,Z) */ + +#define copycolor(c1,c2) ((c1)[0]=(c2)[0],(c1)[1]=(c2)[1],(c1)[2]=(c2)[2]) + +static void +colr_color(col, clr) /* convert short to float color */ +register COLOR col; +register COLR clr; +{ + double f; + + if (clr[EXP] == 0) + col[RED] = col[GRN] = col[BLU] = 0.0; + else { + f = ldexp(1.0, (int)clr[EXP]-(COLXS+8)); + col[RED] = (clr[RED] + 0.5)*f; + col[GRN] = (clr[GRN] + 0.5)*f; + col[BLU] = (clr[BLU] + 0.5)*f; + } +} + +/* End copy-paste from Radiance sources. + */ + + +static void +rad2float( COLR *inp, COLOR *outbuf, int n ) +{ + colr_color(outbuf[0], inp[0]); + while (--n > 0) { + outbuf++; inp++; + if (inp[0][RED] == inp[-1][RED] && + inp[0][GRN] == inp[-1][GRN] && + inp[0][BLU] == inp[-1][BLU] && + inp[0][EXP] == inp[-1][EXP]) + copycolor(outbuf[0], outbuf[-1]); + else + colr_color(outbuf[0], inp[0]); + } +} + +int +im_rad2float( IMAGE *in, IMAGE *out ) +{ + /* Must be 4-band uchar. + */ + if( in->Bands != 4 || in->BandFmt != IM_BANDFMT_UCHAR || + in->Coding != IM_CODING_RAD ) { + im_error( "im_rad2float", "%s", + _( "4-band uchar Radiance-coded images only" ) ); + return( -1 ); + } + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Bands = 3; + out->BandFmt = IM_BANDFMT_FLOAT; + out->Bbits = im_bits_of_fmt( out->BandFmt ); + out->Coding = IM_CODING_NONE; + + if( im_wrapone( in, out, + (im_wrapone_fn) rad2float, NULL, NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am new file mode 100644 index 00000000..d7857fd9 --- /dev/null +++ b/libvips/conversion/Makefile.am @@ -0,0 +1,45 @@ +noinst_LTLIBRARIES = libconversion.la + +libconversion_la_SOURCES = \ + im_bernd.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_mask2vips.c \ + im_msb.c \ + im_recomb.c \ + im_replicate.c \ + im_grid.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_text.c \ + im_thresh.c \ + im_vips2mask.c \ + im_wrap.c \ + im_zoom.c + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/conversion/conver_dispatch.c b/libvips/conversion/conver_dispatch.c new file mode 100644 index 00000000..2847425c --- /dev/null +++ b/libvips/conversion/conver_dispatch.c @@ -0,0 +1,1631 @@ +/* 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 */ +}; + +/* 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 */ +}; + +/* Args to im_wrap. + */ +static im_arg_desc wrap_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INT( "x" ), + IM_INPUT_INT( "y" ) +}; + +/* Call im_wrap via arg vector. + */ +static int +wrap_vec (im_object * argv) +{ + return im_wrap( (IMAGE*)argv[0], (IMAGE*)argv[1], *(int*)argv[2], *(int*)argv[3] ); +} + +/* Description of im_wrap. + */ +static im_function wrap_desc = { + "im_wrap", /* Name */ + "shift image origin, wrapping at sides", + IM_FN_PIO | IM_FN_TRANSFORM, /* Flags */ + wrap_vec, /* Dispatch function */ + IM_NUMBER (wrap_args), /* Size of arg list */ + wrap_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, + &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, + &lrjoin_desc, + &mask2vips_desc, + &msb_desc, + &msb_band_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, + &vips2mask_desc, + &wrap_desc, + &zoom_desc +}; + +/* Package of functions. + */ +im_package im__conversion = { + "conversion", + IM_NUMBER( conv_list ), + conv_list +}; diff --git a/libvips/conversion/im_bandjoin.c b/libvips/conversion/im_bandjoin.c new file mode 100644 index 00000000..2edec590 --- /dev/null +++ b/libvips/conversion/im_bandjoin.c @@ -0,0 +1,162 @@ +/* @(#) 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, void *seq, void *a, void *b ) +{ + REGION **ir = (REGION **) seq; + 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 ); + 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/libvips/conversion/im_bernd.c b/libvips/conversion/im_bernd.c new file mode 100644 index 00000000..5ac7f653 --- /dev/null +++ b/libvips/conversion/im_bernd.c @@ -0,0 +1,95 @@ +/* @(#) 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 + * 12/5/09 + * - fix signed/unsigned warning + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 ) +{ + IMAGE *t1; + int len; + char *buf; + + if( !(t1 = im_open_local( in, "im_bernd:2", "p" )) || + im_extract_area( in, t1, x, y, w, h ) || + im_vips2bufjpeg( t1, in, 75, &buf, &len ) ) + return( -1 ); + + if( fwrite( buf, sizeof( char ), len, stdout ) != (size_t) len ) { + im_error( "im_bernd", "%s", _( "error writing output" ) ); + return( -1 ); + } + fflush( stdout ); + + 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/libvips/conversion/im_black.c b/libvips/conversion/im_black.c new file mode 100644 index 00000000..87a783f0 --- /dev/null +++ b/libvips/conversion/im_black.c @@ -0,0 +1,118 @@ +/* @(#) 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 +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Generate function --- just black out the region. + */ +static int +black_gen( REGION *or, void *seq, void *a, void *b ) +{ + im__black_region( or ); + + 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/libvips/conversion/im_c2amph.c b/libvips/conversion/im_c2amph.c new file mode 100644 index 00000000..112ce044 --- /dev/null +++ b/libvips/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/libvips/conversion/im_c2imag.c b/libvips/conversion/im_c2imag.c new file mode 100644 index 00000000..504d1853 --- /dev/null +++ b/libvips/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/libvips/conversion/im_c2ps.c b/libvips/conversion/im_c2ps.c new file mode 100644 index 00000000..75303191 --- /dev/null +++ b/libvips/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/libvips/conversion/im_c2real.c b/libvips/conversion/im_c2real.c new file mode 100644 index 00000000..0a14a30b --- /dev/null +++ b/libvips/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/libvips/conversion/im_c2rect.c b/libvips/conversion/im_c2rect.c new file mode 100644 index 00000000..a4c1a13f --- /dev/null +++ b/libvips/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/libvips/conversion/im_clip.c b/libvips/conversion/im_clip.c new file mode 100644 index 00000000..283647c6 --- /dev/null +++ b/libvips/conversion/im_clip.c @@ -0,0 +1,531 @@ +/* @(#) 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 + * 7/11/07 + * - use new evalstart/evalend system + * 26/8/08 + * - oops, complex->complex conversion 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 +#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_evalstart( Clip *clip ) +{ + /* Reset counts. + */ + clip->overflow = 0; + clip->underflow = 0; + + return( 0 ); +} + +static int +clip_evalend( 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_evalstart_callback( out, + (im_callback_fn) clip_evalstart, clip, NULL ) || + im_add_evalend_callback( out, + (im_callback_fn) clip_evalend, 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 +clip_stop( void *vseq, void *a, void *b ) +{ + ClipSequence *seq = (ClipSequence *) vseq; + Clip *clip = (Clip *) b; + + /* 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 * +clip_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + ClipSequence *seq; + + if( !(seq = IM_NEW( out, ClipSequence )) ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->overflow = 0; + seq->underflow = 0; + + if( !(seq->ir = im_region_create( in )) ) + return( NULL ); + + return( 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 non-complex to a complex type ... set imaginary to zero. + */ +#define IM_CLIP_REAL_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;\ + }\ +} + +/* Clip any complex to a complex type. + */ +#define IM_CLIP_COMPLEX_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[0];\ + q[1] = p[1];\ + p += 2;\ + 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, void *vseq, void *a, void *b ) +{ + ClipSequence *seq = (ClipSequence *) vseq; + Clip *clip = (Clip *) b; + 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_REAL_COMPLEX ); + break; + case IM_BANDFMT_CHAR: + BAND_SWITCH_INNER( signed char, + IM_CLIP_INT_INT, + IM_CLIP_REAL_FLOAT, + IM_CLIP_REAL_COMPLEX ); + break; + case IM_BANDFMT_USHORT: + BAND_SWITCH_INNER( unsigned short, + IM_CLIP_INT_INT, + IM_CLIP_REAL_FLOAT, + IM_CLIP_REAL_COMPLEX ); + break; + case IM_BANDFMT_SHORT: + BAND_SWITCH_INNER( signed short, + IM_CLIP_INT_INT, + IM_CLIP_REAL_FLOAT, + IM_CLIP_REAL_COMPLEX ); + break; + case IM_BANDFMT_UINT: + BAND_SWITCH_INNER( unsigned int, + IM_CLIP_INT_INT, + IM_CLIP_REAL_FLOAT, + IM_CLIP_REAL_COMPLEX ); + break; + case IM_BANDFMT_INT: + BAND_SWITCH_INNER( signed int, + IM_CLIP_INT_INT, + IM_CLIP_REAL_FLOAT, + IM_CLIP_REAL_COMPLEX ); + break; + case IM_BANDFMT_FLOAT: + BAND_SWITCH_INNER( float, + IM_CLIP_FLOAT_INT, + IM_CLIP_REAL_FLOAT, + IM_CLIP_REAL_COMPLEX ); + break; + case IM_BANDFMT_DOUBLE: + BAND_SWITCH_INNER( double, + IM_CLIP_FLOAT_INT, + IM_CLIP_REAL_FLOAT, + IM_CLIP_REAL_COMPLEX ); + break; + case IM_BANDFMT_COMPLEX: + BAND_SWITCH_INNER( float, + IM_CLIP_COMPLEX_INT, + IM_CLIP_COMPLEX_FLOAT, + IM_CLIP_COMPLEX_COMPLEX ); + break; + case IM_BANDFMT_DPCOMPLEX: + BAND_SWITCH_INNER( double, + IM_CLIP_COMPLEX_INT, + IM_CLIP_COMPLEX_FLOAT, + IM_CLIP_COMPLEX_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", "%s", _( "in must be uncoded" ) ); + return( -1 ); + } + if( ofmt < 0 || ofmt > IM_BANDFMT_DPCOMPLEX ) { + im_error( "im_clip2fmt", "%s", _( "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, clip_start, clip_gen, clip_stop, 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/libvips/conversion/im_copy.c b/libvips/conversion/im_copy.c new file mode 100644 index 00000000..8ebd81bb --- /dev/null +++ b/libvips/conversion/im_copy.c @@ -0,0 +1,558 @@ +/* @(#) 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) + * 15/2/08 + * - added im__saveable_t ... so we can have CMYK JPEG write + * 24/3/09 + * - added IM_CODING_RAD 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Copy a small area. + */ +static int +copy_gen( REGION *or, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + 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 && + in->Coding != IM_CODING_RAD ) { + im_error( "im_copy", "%s", + _( "in must be NONE, LABQ or RAD" ) ); + return( -1 ); + } + if( Coding != IM_CODING_NONE && + Coding != IM_CODING_LABQ && + Coding != IM_CODING_RAD ) { + im_error( "im_copy", "%s", + _( "Coding must be NONE, LABQ or RAD" ) ); + 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", "%s", _( "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", "%s", _( "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", "%s", _( "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 a saveable format. im__saveable_t gives the general type of image + * we make: vanilla 1/3 bands (PPM), with an optional alpha (like PNG), or + * with CMYK as an option (like JPEG). Need to im_close() the return IMAGE. + */ +IMAGE * +im__convert_saveable( IMAGE *in, im__saveable_t saveable ) +{ + IMAGE *out; + + if( !(out = im_open( "convert-for-save", "p" )) ) + return( NULL ); + + /* If this is an 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; + } + + /* If this is an IM_CODING_RAD, we go to float RGB or XYZ. We should + * probably un-gamma-correct the RGB :( + */ + if( in->Coding == IM_CODING_RAD ) { + IMAGE *t; + + if( !(t = im_open_local( out, "conv:1", "p" )) || + im_rad2float( in, t ) ) { + im_close( out ); + return( NULL ); + } + + in = t; + } + + /* Get the bands right. + */ + if( in->Coding == IM_CODING_NONE ) { + if( in->Bands == 2 && saveable != IM__RGBA ) { + 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 && saveable == IM__RGB ) { + 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 && + (saveable == IM__RGB_CMYK || saveable == IM__RGBA) ) { + 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/libvips/conversion/im_extract.c b/libvips/conversion/im_extract.c new file mode 100644 index 00000000..71ba1573 --- /dev/null +++ b/libvips/conversion/im_extract.c @@ -0,0 +1,269 @@ +/* @(#) 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 + * 24/3/09 + * - added IM_CODING_RAD 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 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE_BOX *box = (IMAGE_BOX *) b; + 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 ); + Rect iarea; + 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE_BOX *box = (IMAGE_BOX *) b; + 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", + "%s", _( "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", + "%s", _( "bad extract area" ) ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE ) { + if( in->Coding != IM_CODING_LABQ && + in->Coding != IM_CODING_RAD ) { + im_error( "im_extract_areabands", + "%s", _( "unknown coding" ) ); + return( -1 ); + } + + /* We only do area extract for coding == labq. + */ + if( band != 0 || nbands != in->Bands ) { + im_error( "im_extract_areabands", "%s", + _( "only extract areas from LABQ and RAD" ) ); + 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/libvips/conversion/im_falsecolour.c b/libvips/conversion/im_falsecolour.c new file mode 100644 index 00000000..a8e8a818 --- /dev/null +++ b/libvips/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", + "%s", _( "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/libvips/conversion/im_fliphor.c b/libvips/conversion/im_fliphor.c new file mode 100644 index 00000000..d4f14587 --- /dev/null +++ b/libvips/conversion/im_fliphor.c @@ -0,0 +1,156 @@ +/* @(#) 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 + * 24/3/09 + * - added IM_CODING_RAD 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 WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Flip a small area. + */ +static int +flip_gen( REGION *or, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + 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 && + in->Coding != IM_CODING_RAD ) { + im_error( "im_fliphor", "%s", + _( "Coding must be NONE, LABQ or RAD" ) ); + 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/libvips/conversion/im_flipver.c b/libvips/conversion/im_flipver.c new file mode 100644 index 00000000..66e065b2 --- /dev/null +++ b/libvips/conversion/im_flipver.c @@ -0,0 +1,147 @@ +/* @(#) 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 + * 24/3/09 + * - added IM_CODING_RAD 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Flip a small area. + */ +static int +flip_gen( REGION *or, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + 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 && + in->Coding != IM_CODING_RAD ) { + im_error( "im_flipver", "%s", + _( "Coding must be NONE, LABQ or RAD" ) ); + 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/libvips/conversion/im_gbandjoin.c b/libvips/conversion/im_gbandjoin.c new file mode 100644 index 00000000..374baab4 --- /dev/null +++ b/libvips/conversion/im_gbandjoin.c @@ -0,0 +1,231 @@ +/* @(#) 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, void *seq, void *a, void *b ) +{ + REGION **ir = (REGION **) seq; + Join *jn = (Join *) b; + 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", "%s", _( "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", + "%s", _( "uncoded input only" ) ); + return( -1 ); + } + + if( in[0]->BandFmt != in[i]->BandFmt ) { + im_error( "im_gbandjoin", + "%s", _( "input images differ in format" ) ); + return( -1 ); + } + if( in[0]->Xsize != in[i]->Xsize || + in[0]->Ysize != in[i]->Ysize ) { + im_error( "im_gbandjoin", + "%s", _( "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/libvips/conversion/im_grid.c b/libvips/conversion/im_grid.c new file mode 100644 index 00000000..edb9befb --- /dev/null +++ b/libvips/conversion/im_grid.c @@ -0,0 +1,182 @@ +/* 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + Grid *grid = (Grid *) b; + 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", "%s", _( "bad parameters" ) ); + return( -1 ); + } + if( in->Ysize % tile_height != 0 || + in->Ysize / tile_height != across * down ) { + im_error( "im_grid", "%s", _( "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/libvips/conversion/im_insert.c b/libvips/conversion/im_insert.c new file mode 100644 index 00000000..0d993f94 --- /dev/null +++ b/libvips/conversion/im_insert.c @@ -0,0 +1,387 @@ +/* @(#) 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 + * 24/3/09 + * - added IM_CODING_RAD 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 + +#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, void *seq, void *a, void *b ) +{ + REGION **ir = (REGION **) seq; + InsertState *ins = (InsertState *) b; + 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", "%s", _( "inputs differ in format" ) ); + return( -1 ); + } + if( main->Coding != IM_CODING_NONE && + main->Coding != IM_CODING_RAD && + main->Coding != IM_CODING_LABQ ) { + im_error( "im_insert", "%s", + _( "Coding should be NONE, LABQ or RAD" ) ); + return( -1 ); + } + if( x > RANGE || x < -RANGE || y > RANGE || y < -RANGE ) { + im_error( "im_insert", "%s", _( "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", + "%s", _( "inputs differ in format" ) ); + return( -1 ); + } + if( main->Coding != IM_CODING_NONE && + main->Coding != IM_CODING_LABQ && + main->Coding != IM_CODING_RAD ) { + im_error( "im_insert_noexpand", "%s", + _( "Coding should be NONE, LABQ or RAD" ) ); + return( -1 ); + } + if( x > RANGE || x < -RANGE || y > RANGE || y < -RANGE ) { + im_error( "im_insert", "%s", _( "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/libvips/conversion/im_lrjoin.c b/libvips/conversion/im_lrjoin.c new file mode 100644 index 00000000..c4e11fcf --- /dev/null +++ b/libvips/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/libvips/conversion/im_mask2vips.c b/libvips/conversion/im_mask2vips.c new file mode 100644 index 00000000..8f864821 --- /dev/null +++ b/libvips/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/libvips/conversion/im_msb.c b/libvips/conversion/im_msb.c new file mode 100644 index 00000000..99d57f50 --- /dev/null +++ b/libvips/conversion/im_msb.c @@ -0,0 +1,346 @@ +/* @(#) 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, "%s", _("char, short or int only")); + return -1; + } + + params = IM_ARRAY (out, 3, size_t); + + if (!params) + return -1; + + width = SIZEOF_BAND (in->BandFmt); + +#if G_BYTE_ORDER == G_BIG_ENDIAN + 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, "%s", _("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, "%s", _("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, "%s", _("char, short or int only")); + return -1; + } + + if (band >= in->Bands) + { + im_error (FUNCTION_NAME, + "%s", _("image does not have that many bands")); + return -1; + } + + width = SIZEOF_BAND (in->BandFmt); + +#if G_BYTE_ORDER == G_BIG_ENDIAN + 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, + "%s", _("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, "%s", _("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/libvips/conversion/im_print.c b/libvips/conversion/im_print.c new file mode 100644 index 00000000..563bf0ed --- /dev/null +++ b/libvips/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/libvips/conversion/im_recomb.c b/libvips/conversion/im_recomb.c new file mode 100644 index 00000000..009e1772 --- /dev/null +++ b/libvips/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/libvips/conversion/im_replicate.c b/libvips/conversion/im_replicate.c new file mode 100644 index 00000000..a669c3bd --- /dev/null +++ b/libvips/conversion/im_replicate.c @@ -0,0 +1,158 @@ +/* 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE *in = (IMAGE *) a; + 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/libvips/conversion/im_ri2c.c b/libvips/conversion/im_ri2c.c new file mode 100644 index 00000000..3d5d2dfd --- /dev/null +++ b/libvips/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/libvips/conversion/im_rightshift_size.c b/libvips/conversion/im_rightshift_size.c new file mode 100644 index 00000000..48129e59 --- /dev/null +++ b/libvips/conversion/im_rightshift_size.c @@ -0,0 +1,300 @@ +/* @(#) 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, void *seq, void *a, void *b ); + +/* 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, "%s", _( "bad arguments" ) ); + return -1; + } + if( ! xshift && ! yshift ){ + im_warn( FUNCTION_NAME, "%s", _( "shift by zero: falling back to im_copy" ) ); + return im_copy( in, out ); + } + if( ! in-> Xsize >> xshift || ! in-> Ysize >> yshift ){ + im_error( FUNCTION_NAME, "%s", _( "would result in zero size output image" ) ); + return -1; + } + if( ! im_isint( in ) ){ + im_error( FUNCTION_NAME, "%s", _( "integer type images only" ) ); + return -1; + } + if( IM_CODING_NONE != in->Coding ){ + im_error( FUNCTION_NAME, "%s", _( "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, "%s", _( "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, "%s", _( "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, void *seq, void *a, void *b ){ \ + \ + REGION *make_from = (REGION *) seq; \ + int *params = (int *) b; \ + int xshift= params[0]; \ + int yshift= params[1]; \ + int G_GNUC_UNUSED preshift= params[2]; \ + int G_GNUC_UNUSED 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/libvips/conversion/im_rot180.c b/libvips/conversion/im_rot180.c new file mode 100644 index 00000000..924e5361 --- /dev/null +++ b/libvips/conversion/im_rot180.c @@ -0,0 +1,166 @@ +/* @(#) 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 + * 24/3/09 + * - added IM_CODING_RAD 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Rotate a small piece. + */ +static int +rot180_gen( REGION *or, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE *in = (IMAGE *) a; + + /* 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 && + in->Coding != IM_CODING_RAD ) { + im_error( "im_rot180", "%s", + _( "Coding should be NONE, LABQ or RAD" ) ); + 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/libvips/conversion/im_rot270.c b/libvips/conversion/im_rot270.c new file mode 100644 index 00000000..a1487748 --- /dev/null +++ b/libvips/conversion/im_rot270.c @@ -0,0 +1,170 @@ +/* @(#) 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 + * 24/3/09 + * - added IM_CODING_RAD 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Rotate a small piece. + */ +static int +rot270_gen( REGION *or, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE *in = (IMAGE *) a; + + /* 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 && + in->Coding != IM_CODING_RAD ) { + im_error( "im_rot270", "%s", + _( "Coding should be NONE, LABQ or RAD" ) ); + 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/libvips/conversion/im_rot90.c b/libvips/conversion/im_rot90.c new file mode 100644 index 00000000..0a441ba4 --- /dev/null +++ b/libvips/conversion/im_rot90.c @@ -0,0 +1,170 @@ +/* @(#) 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 + * 24/3/09 + * - added IM_CODING_RAD 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Rotate a small piece. + */ +static int +rot90_gen( REGION *or, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE *in = (IMAGE *) a; + + /* 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 && + in->Coding != IM_CODING_RAD ) { + im_error( "im_rot90", "%s", + _( "Coding should be NONE, LABQ or RAD" ) ); + 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/libvips/conversion/im_scale.c b/libvips/conversion/im_scale.c new file mode 100644 index 00000000..39427986 --- /dev/null +++ b/libvips/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/libvips/conversion/im_scaleps.c b/libvips/conversion/im_scaleps.c new file mode 100644 index 00000000..5a0436d3 --- /dev/null +++ b/libvips/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/libvips/conversion/im_slice.c b/libvips/conversion/im_slice.c new file mode 100644 index 00000000..2e1077aa --- /dev/null +++ b/libvips/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/libvips/conversion/im_subsample.c b/libvips/conversion/im_subsample.c new file mode 100644 index 00000000..a3d00cf5 --- /dev/null +++ b/libvips/conversion/im_subsample.c @@ -0,0 +1,249 @@ +/* @(#) 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 + * 21/4/08 + * - don't fall back to pixel-wise shrinks for smalltile, it kills + * performance, just bring IM_MAX_WIDTH down 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Maximum width of input we ask for. + */ +#define IM_MAX_WIDTH (100) + +/* 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE *in = (IMAGE *) a; + SubsampleInfo *st = (SubsampleInfo *) b; + 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, k; + + /* 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( k = 0; k < ps; k++ ) + q[k] = p[k]; + + q += ps; + p += ps * st->xshrink; + } + } + } + + return( 0 ); +} + +/* Fetch one pixel at a time ... good for very large shrinks. + */ +static int +point_shrink_gen( REGION *or, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE *in = (IMAGE *) a; + SubsampleInfo *st = (SubsampleInfo *) b; + 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 k; + + /* 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( k = 0; k < ps; k++ ) + q[k] = p[k]; + 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_error( "im_subsample", + "%s", _( "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_error( "im_subsample", + "%s", _( "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( 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/libvips/conversion/im_system.c b/libvips/conversion/im_system.c new file mode 100644 index 00000000..419379bb --- /dev/null +++ b/libvips/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/libvips/conversion/im_tbjoin.c b/libvips/conversion/im_tbjoin.c new file mode 100644 index 00000000..52831b8b --- /dev/null +++ b/libvips/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/libvips/conversion/im_text.c b/libvips/conversion/im_text.c new file mode 100644 index 00000000..f381def7 --- /dev/null +++ b/libvips/conversion/im_text.c @@ -0,0 +1,240 @@ +/* @(#) 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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "pangoft2 support disabled" ) ); + + return( -1 ); +} + +#endif /*HAVE_PANGOFT2*/ diff --git a/libvips/conversion/im_thresh.c b/libvips/conversion/im_thresh.c new file mode 100644 index 00000000..3662fece --- /dev/null +++ b/libvips/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/libvips/conversion/im_vips2mask.c b/libvips/conversion/im_vips2mask.c new file mode 100644 index 00000000..7d92b6ac --- /dev/null +++ b/libvips/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", "%s", _( "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", + "%s", _( "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/libvips/conversion/im_wrap.c b/libvips/conversion/im_wrap.c new file mode 100644 index 00000000..7c7c4def --- /dev/null +++ b/libvips/conversion/im_wrap.c @@ -0,0 +1,127 @@ +/* Wrap an image so that what was the origin is at (x,y). + * + * int im_wrap( IMAGE *in, IMAGE *out, int x, int y ); + * + * All functions return 0 on success and -1 on error + * + * Copyright: 2008, Nottingham Trent University + * Author: Tom Vajzovic + * Written on: 2008-01-15 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 wrap( REGION *out, void *seq, void *a, void *b ){ +#define IM ((IMAGE*)a) +#define X (((int*)b)[0]) +#define Y (((int*)b)[1]) + int left= out-> valid. left - X; + int top= out-> valid. top - Y; + int right= left + out-> valid. width; + int bot= top + out-> valid. height; + Rect source_a= { + left: IM-> Xsize + left, + top: IM-> Ysize + top, + width: 0 < right ? -left : out-> valid. width, + height: 0 < bot ? -top : out-> valid. height + }; + Rect source_b= { + left: source_a. left, + top: 0 > top ? 0 : top, + width: source_a. width, + height: 0 > top ? bot : out-> valid. height + }; + Rect source_c= { + left: 0 > left ? 0 : left, + top: source_a. top, + width: 0 > left ? right : out-> valid. width, + height: source_a. height + }; + Rect source_d= { + left: source_c. left, + top: source_b. top, + width: source_c. width, + height: source_b. height + }; + if( 0 > left ){ + if( 0 > top && im_prepare_to( (REGION*)seq, out, & source_a, + out-> valid. left, out-> valid. top )) + return -1; + + if( 0 < bot && im_prepare_to( (REGION*)seq, out, & source_b, + out-> valid. left, 0 > top ? Y : out-> valid. top )) + return -1; + } + if( 0 < right ){ + if( 0 > top && im_prepare_to( (REGION*)seq, out, & source_c, + 0 > left ? X : out-> valid. left, out-> valid. top )) + return -1; + + if( 0 < bot && im_prepare_to( (REGION*)seq, out, & source_d, + 0 > left ? X : out-> valid. left, 0 > top ? Y : out-> valid. top )) + return -1; + } + return 0; +#undef IM +#undef X +#undef Y +} + +int im_wrap( IMAGE *in, IMAGE *out, int x, int y ){ + if( im_piocheck( in, out )) + return -1; + { + int *params= IM_ARRAY( out, 2, int ); + if( ! params ) + return -1; + + params[ 0 ]= x % in-> Xsize; + params[ 1 ]= y % in-> Ysize; + if( 0 > x ) + params[ 0 ]+= in-> Xsize; + if( 0 > y ) + params[ 1 ]+= in-> Ysize; + + return im_cp_desc( out, in ) + || im_demand_hint( out, IM_THINSTRIP, in, NULL ) + || im_generate( out, im_start_one, wrap, im_stop_one, (void*)in, (void*)params ); + } +} diff --git a/libvips/conversion/im_zoom.c b/libvips/conversion/im_zoom.c new file mode 100644 index 00000000..1c6fb4fa --- /dev/null +++ b/libvips/conversion/im_zoom.c @@ -0,0 +1,378 @@ +/* @(#) 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 + * 24/3/09 + * - added IM_CODING_RAD 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 + + */ + +/* + * 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 quicker 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! + */ + g_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. + */ + g_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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + ZoomInfo *zm = (ZoomInfo *) b; + + /* 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 && + in->Coding != IM_CODING_RAD ) { + im_error( "im_zoom", "%s", _( "unknown coding type" ) ); + return( -1 ); + } + if( xfac <= 0 || yfac <= 0 ) { + im_error( "im_zoom", "%s", _( "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", "%s", _( "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() 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/libvips/convolution/Makefile.am b/libvips/convolution/Makefile.am new file mode 100644 index 00000000..d876fe3f --- /dev/null +++ b/libvips/convolution/Makefile.am @@ -0,0 +1,32 @@ +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_phasecor_fft.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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/convolution/convol_dispatch.c b/libvips/convolution/convol_dispatch.c new file mode 100644 index 00000000..a5b16d90 --- /dev/null +++ b/libvips/convolution/convol_dispatch.c @@ -0,0 +1,1415 @@ +/* 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 */ +}; + +/* Call im_phasecor_fft via arg vector. + */ +static int +phasecor_fft_vec( im_object *argv ) +{ + return( im_phasecor_fft( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_phasecor_fft. + */ +static im_function phasecor_fft_desc = { + "im_phasecor_fft", /* Name */ + "non-normalised correlation of gradient of in2 within in1", + IM_FN_TRANSFORM, /* Flags */ + phasecor_fft_vec, /* Dispatch function */ + IM_NUMBER( two_in_one_out ), /* Size of arg list */ + two_in_one_out /* 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 */ +}; + +/* Call im_gauss_imask_sep via arg vector. + */ +static int +gauss_imask_sep_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_sep( mo->name, sigma, min_amp )) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_gauss_imask_sep. + */ +static im_function gauss_imask_sep_desc = { + "im_gauss_imask_sep", /* Name */ + "generate separable gaussian INTMASK", + 0, /* Flags */ + gauss_imask_sep_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 */ + "horizontal difference 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 */ + "vertical difference 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 */ +}; + +/* 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, + &gauss_imask_sep_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, + &phasecor_fft_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, + &stretch3_desc, + &zerox_desc +}; + +/* Package of functions. + */ +im_package im__convolution = { + "convolution", + IM_NUMBER( convol_list ), + convol_list +}; diff --git a/libvips/convolution/im_addgnoise.c b/libvips/convolution/im_addgnoise.c new file mode 100644 index 00000000..87de342a --- /dev/null +++ b/libvips/convolution/im_addgnoise.c @@ -0,0 +1,90 @@ +/* @(#) 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 + * + * 2008-01-28 tcv: + * - now works (was broken) + * - no limit on 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +int +im_addgnoise( IMAGE *in, IMAGE *out, double sigma ){ +#define FUNCTION_NAME "im_addgnoise" + + if( im_piocheck( in, out )) + return -1; + { + int i; + IMAGE **temps= IM_ARRAY( out, in-> Bands, IMAGE* ); + IMAGE *joined_temps= im_open_local( out, FUNCTION_NAME ": joined_temps", "p" ); + + if( ! temps || ! joined_temps || im_open_local_array( out, temps, in-> Bands, FUNCTION_NAME ": temps", "p" )) + return -1; + + for( i= 0; i < in-> Bands; ++i ) + if( im_gaussnoise( temps[i], in-> Xsize, in-> Ysize, 0.0, sigma )) + return -1; + + return im_gbandjoin( temps, joined_temps, in-> Bands ) || im_add( in, joined_temps, out ); + } +#undef FUNCTION_NAME +} diff --git a/libvips/convolution/im_compass.c b/libvips/convolution/im_compass.c new file mode 100644 index 00000000..7b357da7 --- /dev/null +++ b/libvips/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/libvips/convolution/im_contrast_surface.c b/libvips/convolution/im_contrast_surface.c new file mode 100644 index 00000000..14c291c1 --- /dev/null +++ b/libvips/convolution/im_contrast_surface.c @@ -0,0 +1,276 @@ +/* @(#) 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, void *seq, + void *a, void * b); + +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, "%s", _("one band uncoded uchar only")); + return -1; + } + + if (half_win_size < 1 || spacing < 1) + { + im_error (FUNCTION_NAME, "%s", _("bad parameters")); + return -1; + } + + if (DOUBLE (half_win_size) >= LESSER (in->Xsize, in->Ysize)) + { + im_error (FUNCTION_NAME, + "%s", _("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, void * seq, void *unrequired, void * b) +{ + /* I don't need *in, but I will recieve it anyway since im_start_one() needs it */ + + REGION * make_from = (REGION *) seq; + cont_surf_params_t * params = (cont_surf_params_t *) b; + + 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/libvips/convolution/im_conv.c b/libvips/convolution/im_conv.c new file mode 100644 index 00000000..479184d8 --- /dev/null +++ b/libvips/convolution/im_conv.c @@ -0,0 +1,587 @@ +/* @(#) 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 + * 7/11/07 + * - new evalstart/end callbacks + * 12/5/08 + * - int rounding was +1 too much, argh + * - only rebuild the buffer offsets if bpl changes + * 5/4/09 + * - tiny speedups and cleanups + * - add restrict, though it doesn't seem to help gcc + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_close( Conv *conv ) +{ + IM_FREEF( im_free_imask, conv->mask ); + + return( 0 ); +} + +static int +conv_evalstart( Conv *conv ) +{ + /* Reset underflow/overflow count. + */ + conv->overflow = 0; + conv->underflow = 0; + + return( 0 ); +} + +static int +conv_evalend( Conv *conv ) +{ + /* Print underflow/overflow count. + */ + if( conv->overflow || conv->underflow ) + im_warn( "im_conv", + _( "%d overflows and %d underflows detected" ), + conv->overflow, conv->underflow ); + + 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_close, conv, NULL ) || + im_add_close_callback( out, + (im_callback_fn) conv_evalstart, conv, NULL ) || + im_add_close_callback( out, + (im_callback_fn) conv_evalend, 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 pointers */ + + int underflow; /* Underflow/overflow counts */ + int overflow; + + int last_bpl; /* Avoid recalcing offsets, if we can */ +} ConvSequence; + +/* Free a sequence value. + */ +static int +conv_stop( void *vseq, void *a, void *b ) +{ + ConvSequence *seq = (ConvSequence *) vseq; + Conv *conv = (Conv *) b; + + /* 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 * +conv_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + Conv *conv = (Conv *) b; + ConvSequence *seq; + + if( !(seq = IM_NEW( out, ConvSequence )) ) + return( NULL ); + + /* Init! + */ + seq->conv = conv; + seq->ir = NULL; + seq->pts = NULL; + seq->underflow = 0; + seq->overflow = 0; + seq->last_bpl = -1; + + /* 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 ) { + conv_stop( seq, in, conv ); + return( NULL ); + } + + return( seq ); +} + +#define INNER { \ + sum += t[i] * p[i][x]; \ + i += 1; \ +} + +/* INT and FLOAT inner loops. + */ +#define CONV_INT( TYPE, IM_CLIP ) { \ + TYPE ** restrict p = (TYPE **) seq->pts; \ + TYPE * restrict q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + int sum; \ + int i; \ + \ + sum = 0; \ + i = 0; \ + IM_UNROLL( conv->nnz, INNER ); \ + \ + sum = ((sum + rounding) / mask->scale) + mask->offset; \ + \ + IM_CLIP; \ + \ + q[x] = sum; \ + } \ +} + +#define CONV_FLOAT( TYPE ) { \ + TYPE ** restrict p = (TYPE **) seq->pts; \ + TYPE * restrict q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + double sum; \ + int i; \ + \ + sum = 0; \ + i = 0; \ + IM_UNROLL( conv->nnz, INNER ); \ + \ + sum = (sum / mask->scale) + mask->offset; \ + \ + q[x] = sum; \ + } \ +} + +/* Convolve! + */ +static int +conv_gen( REGION *or, void *vseq, void *a, void *b ) +{ + ConvSequence *seq = (ConvSequence *) vseq; + IMAGE *in = (IMAGE *) a; + Conv *conv = (Conv *) b; + REGION *ir = seq->ir; + INTMASK *mask = conv->mask; + int * restrict t = conv->coeff; + + /* You might think this should be (scale+1)/2, but then we'd be adding + * one for scale == 1. + */ + int rounding = mask->scale / 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. Only do this if the bpl has changed since the + * previous im_prepare(). + */ + if( seq->last_bpl != IM_REGION_LSKIP( ir ) ) { + seq->last_bpl = IM_REGION_LSKIP( ir ); + + 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_error( "im_conv", "%s", _( "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_conv", "%s", _( "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_error( "im_conv", "%s", _( "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, conv_start, conv_gen, conv_stop, 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_close( 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_close, 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 +conv_stop( void *vseq, void *a, void *b ) +{ + ConvSequence *seq = (ConvSequence *) vseq; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +conv_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + Conv *conv = (Conv *) b; + ConvSequence *seq; + + if( !(seq = IM_NEW( out, ConvSequence )) ) + 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 ) { + conv_stop( 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 +conv_gen( REGION *or, void *vseq, void *a, void *b ) +{ + ConvSequence *seq = (ConvSequence *) vseq; + IMAGE *in = (IMAGE *) a; + Conv *conv = (Conv *) b; + 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_error( "im_convf", + "%s", _( "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_convf", "%s", _( "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_error( "im_convf", "%s", _( "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, conv_start, conv_gen, conv_stop, 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/libvips/convolution/im_convsep.c b/libvips/convolution/im_convsep.c new file mode 100644 index 00000000..1606c939 --- /dev/null +++ b/libvips/convolution/im_convsep.c @@ -0,0 +1,445 @@ +/* @(#) 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 + * 12/5/08 + * - int rounding was +1 too much, argh + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 +conv_stop( void *vseq, void *a, void *b ) +{ + ConvSequence *seq = (ConvSequence *) vseq; + Conv *conv = (Conv *) b; + + /* 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 * +conv_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + Conv *conv = (Conv *) b; + ConvSequence *seq; + + if( !(seq = IM_NEW( out, ConvSequence )) ) + 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 ) { + conv_stop( 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 +conv_gen( REGION *or, void *vseq, void *a, void *b ) +{ + ConvSequence *seq = (ConvSequence *) vseq; + IMAGE *in = (IMAGE *) a; + Conv *conv = (Conv *) b; + REGION *ir = seq->ir; + INTMASK *mask = conv->mask; + + /* You might think this should be (scale+1)/2, but then we'd be adding + * one for scale == 1. + */ + int rounding = mask->scale / 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", "%s", _( "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", "%s", _( "nonsense mask parameters" ) ); + return( -1 ); + } + if( mask->xsize != 1 && mask->ysize != 1 ) { + im_error( "im_convsep", + "%s", _( "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", "%s", _( "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, conv_start, conv_gen, conv_stop, 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/libvips/convolution/im_convsepf.c b/libvips/convolution/im_convsepf.c new file mode 100644 index 00000000..92cf5e4e --- /dev/null +++ b/libvips/convolution/im_convsepf.c @@ -0,0 +1,357 @@ +/* @(#) 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 +conv_stop( void *vseq, void *a, void *b ) +{ + ConvSequence *seq = (ConvSequence *) vseq; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Convolution start function. + */ +static void * +conv_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + Conv *conv = (Conv *) b; + ConvSequence *seq; + + if( !(seq = IM_NEW( out, ConvSequence )) ) + 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 ) { + conv_stop( 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 +conv_gen( REGION *or, void *vseq, void *a, void *b ) +{ + ConvSequence *seq = (ConvSequence *) vseq; + IMAGE *in = (IMAGE *) a; + Conv *conv = (Conv *) b; + 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", + "%s", _( "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", "%s", _( "bad mask parameters" ) ); + return( -1 ); + } + if( mask->xsize != 1 && mask->ysize != 1 ) { + im_error( "im_convsepf", + "%s", _( "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", + "%s", _( "image too small for mask" ) ); + return( -1 ); + } + + /* SMALLTILE seems fastest. + */ + if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) || + im_generate( out, conv_start, conv_gen, conv_stop, 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/libvips/convolution/im_convsub.c b/libvips/convolution/im_convsub.c new file mode 100644 index 00000000..738d240f --- /dev/null +++ b/libvips/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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + Embed *embed = (Embed *) b; + 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", "%s", _( "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 && + in->Coding != IM_CODING_RAD ) { + im_error( "im_embed", "%s", _( "unknown image coding type" ) ); + return( -1 ); + } + if( flag < 0 || flag > 4 ) { + im_error( "im_embed", "%s", _( "unknown flag" ) ); + return( -1 ); + } + if( w <= 0 || h <= 0 ) { + im_error( "im_embed", "%s", _( "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/libvips/convolution/im_fastcor.c b/libvips/convolution/im_fastcor.c new file mode 100644 index 00000000..459b8c72 --- /dev/null +++ b/libvips/convolution/im_fastcor.c @@ -0,0 +1,211 @@ +/* @(#) 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE *ref = (IMAGE *) b; + 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/libvips/convolution/im_gaussmasks.c b/libvips/convolution/im_gaussmasks.c new file mode 100644 index 00000000..c73320ea --- /dev/null +++ b/libvips/convolution/im_gaussmasks.c @@ -0,0 +1,217 @@ +/* @(#) 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 + * 18/3/09 + * - bumped max mask size *40 + * - added _sep variant + * 30/3/09 + * - set scale in _sep variant, why not + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 5000 + +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_error( "im_gauss_dmask", "%s", _( "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 ) ; +} + +/* Just return the central line of the mask. This helps nip, which really + * struggles with large matrix manipulations. + */ +INTMASK * +im_gauss_imask_sep( const char *filename, double sigma, double min_amplitude ) +{ + INTMASK *im; + INTMASK *im2; + int i; + int sum; + + if( !(im = im_gauss_imask( filename, sigma, min_amplitude )) ) + return( NULL ); + if( !(im2 = im_create_imask( filename, im->xsize, 1 )) ) { + im_free_imask( im ); + return( NULL ); + } + + sum = 0; + for( i = 0; i < im->xsize; i++ ) { + im2->coeff[i] = im->coeff[i + im->xsize * (im->ysize / 2)]; + sum += im2->coeff[i]; + } + im2->scale = sum; + + im_free_imask( im ); + + return( im2 ) ; +} diff --git a/libvips/convolution/im_gaussnoise.c b/libvips/convolution/im_gaussnoise.c new file mode 100644 index 00000000..e2c32617 --- /dev/null +++ b/libvips/convolution/im_gaussnoise.c @@ -0,0 +1,158 @@ +/* @(#) 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 *seq, void *a, void *b ) +{ + GnoiseInfo *gin = (GnoiseInfo *) a; + 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/libvips/convolution/im_gradcor.c b/libvips/convolution/im_gradcor.c new file mode 100644 index 00000000..2b2b3246 --- /dev/null +++ b/libvips/convolution/im_gradcor.c @@ -0,0 +1,557 @@ +/* @(#) Like im_spcor(), but with a new metric. + * @(#) + * @(#) takes the gradient images of the two images, and takes the dot-product + * @(#) correlation of the two vector images. + * @(#) + * @(#) (vector images are never really used, the two components are + * @(#) calculated separately) + * @(#) + * @(#) The vector expression of this method is my (tcv) own creation. It is + * @(#) equivalent to the complex-number method of: + * @(#) + * @(#) ARGYRIOU, V. et al. 2003. Estimation of sub-pixel motion using + * @(#) gradient cross correlation. Electronics Letters, 39 (13). + * @(#) + * @(#) It's suitability for sub-pixel alignment is not (yet) tested. + * + * 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 ! xgrad || ! ygrad || ! grads + || 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 + } + + /* Keep gcc happy. + */ + return 0; +#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 + } + + /* Keep gcc happy. + */ + return 0; +#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/libvips/convolution/im_logmasks.c b/libvips/convolution/im_logmasks.c new file mode 100644 index 00000000..4aec46f6 --- /dev/null +++ b/libvips/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/libvips/convolution/im_mpercent.c b/libvips/convolution/im_mpercent.c new file mode 100644 index 00000000..0c6dc56e --- /dev/null +++ b/libvips/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/libvips/convolution/im_phasecor_fft.c b/libvips/convolution/im_phasecor_fft.c new file mode 100644 index 00000000..fd3a3b4b --- /dev/null +++ b/libvips/convolution/im_phasecor_fft.c @@ -0,0 +1,63 @@ +/* Like im_spcor(), but calculates phase correlation in the Fourier domain. + * + * Copyright: 2008, Nottingham Trent University + * + * Author: Tom Vajzovic + * Written on: 2008-01-16 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_phasecor_fft( IMAGE *in1, IMAGE *in2, IMAGE *out ){ +#define FUNCTION_NAME "im_fft_phasecor" + IMAGE *temp1= im_open_local( out, FUNCTION_NAME ": temp1", "t" ); + IMAGE *temp2= im_open_local( out, FUNCTION_NAME ": temp2", "t" ); + IMAGE *temp3= im_open_local( out, FUNCTION_NAME ": temp3", "t" ); + + if( ! temp1 || ! temp2 || ! temp3 ) + return -1; + + return im_incheck( in1 ) + || im_incheck( in2 ) + || im_outcheck( out ) + || im_fwfft( in1, temp1 ) + || im_fwfft( in2, temp2 ) + || im_cross_phase( temp1, temp2, temp3 ) + || im_invfftr( temp3, out ); +#undef FUNCTION_NAME +} diff --git a/libvips/convolution/im_rank.c b/libvips/convolution/im_rank.c new file mode 100644 index 00000000..10cf503c --- /dev/null +++ b/libvips/convolution/im_rank.c @@ -0,0 +1,426 @@ +/* @(#) 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 +rank_stop( void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Rank start function. + */ +static void * +rank_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + RankInfo *rnk = (RankInfo *) b; + SeqInfo *seq; + + if( !(seq = IM_NEW( out, SeqInfo )) ) + 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 ) { + rank_stop( 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 +rank_gen( REGION *or, void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + IMAGE *in = (IMAGE *) a; + RankInfo *rnk = (RankInfo *) b; + 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, rank_start, rank_gen, rank_stop, 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/libvips/convolution/im_rank_image.c b/libvips/convolution/im_rank_image.c new file mode 100644 index 00000000..59127bf2 --- /dev/null +++ b/libvips/convolution/im_rank_image.c @@ -0,0 +1,330 @@ +/* @(#) 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 +rank_stop( void *vseq, void *a, void *b ) +{ + RankSequence *seq = (RankSequence *) vseq; + Rank *rank = (Rank *) b; + int i; + + for( i = 0; i < rank->n; i++ ) + IM_FREEF( im_region_free, seq->ir[i] ); + + return( 0 ); +} + +/* Make a sequence value. + */ +static void * +rank_start( IMAGE *out, void *a, void *b ) +{ + IMAGE **in = (IMAGE **) a; + Rank *rank = (Rank *) b; + 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 ) { + rank_stop( seq, in, rank ); + return( NULL ); + } + + for( i = 0; i < rank->n; i++ ) + if( !(seq->ir[i] = im_region_create( in[i] )) ) { + rank_stop( 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 +rank_gen( REGION *or, void *vseq, void *a, void *b ) +{ + RankSequence *seq = (RankSequence *) vseq; + Rank *rank = (Rank *) b; + 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, + rank_start, rank_gen, rank_stop, 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/libvips/convolution/im_resize_linear.c b/libvips/convolution/im_resize_linear.c new file mode 100644 index 00000000..fe371fd5 --- /dev/null +++ b/libvips/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/libvips/convolution/im_sharpen.c b/libvips/convolution/im_sharpen.c new file mode 100644 index 00000000..bc7d24eb --- /dev/null +++ b/libvips/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", "%s", _( "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", "%s", _( "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/libvips/convolution/im_shrink.c b/libvips/convolution/im_shrink.c new file mode 100644 index 00000000..7dbef949 --- /dev/null +++ b/libvips/convolution/im_shrink.c @@ -0,0 +1,323 @@ +/* @(#) 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 + * 20/12/08 + * - fall back to im_copy() for 1/1 shrink + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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( void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Make a sequence value. + */ +static void * +shrink_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + ShrinkInfo *st = (ShrinkInfo *) b; + SeqInfo *seq; + + if( !(seq = IM_NEW( out, SeqInfo )) ) + 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( k = 0; k < ir->im->Bands; k++ ) { \ + 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( k = 0; k < ir->im->Bands; k++ ) { \ + 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, void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + ShrinkInfo *st = (ShrinkInfo *) b; + 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, k; + + /* 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_error( "im_shrink", "%s", _( "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_error( "im_shrink", "%s", _( "non-complex input only" ) ); + return( -1 ); + } + if( xshrink < 1.0 || yshrink < 1.0 ) { + im_error( "im_shrink", + "%s", _( "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_error( "im_shrink", + "%s", _( "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( xshrink == 1 && yshrink == 1 ) { + return( im_copy( in, out ) ); + } + else 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_error( "im_shrink", "%s", _( "unknown coding type" ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libvips/convolution/im_spcor.c b/libvips/convolution/im_spcor.c new file mode 100644 index 00000000..101f072b --- /dev/null +++ b/libvips/convolution/im_spcor.c @@ -0,0 +1,333 @@ +/* @(#) 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; 2006, 2007 Nottingham Trent University. + * + * + * 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 + * + * 2006-10-24 tcv + * - add im_spcor2 + * + * 2007-11-12 tcv + * - make im_spcor a wrapper selecting either im__spcor or im__spcor2 + * 2008-09-09 JC + * - roll back the windowed version for now, it has some tile edge effects + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 per-call state 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) */ +} Spcor; + +#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 from mean. \ + */ \ + 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-difference from mean. \ + */ \ + sum3 += (rp - spcor->rmean) * (ip - imean); \ + } \ + } \ +} + +/* spcor generate function. + */ +static int +spcor_gen( REGION *or, void *vseq, void *a, void *b ) +{ + REGION *ir = (REGION *) vseq; + Spcor *spcor = (Spcor *) b; + IMAGE *ref = spcor->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 / (spcor->c1 * c2); + + *q++ = cc; + } + } + + return( 0 ); +} + +/* Pre-calculate stuff for our reference image. + */ +static Spcor * +spcor_new( IMAGE *out, IMAGE *ref ) +{ + Spcor *spcor; + int sz = ref->Xsize * ref->Ysize; + PEL *p = (PEL *) ref->data; + double s; + int i; + + if( !(spcor = IM_NEW( out, Spcor )) ) + return( NULL ); + + /* Pre-calculate stuff on our reference image. + */ + spcor->ref = ref; + if( im_avg( spcor->ref, &spcor->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] - spcor->rmean; + s += t * t; + } + spcor->c1 = sqrt( s ); + + return( spcor ); +} + +int +im_spcor_raw( IMAGE *in, IMAGE *ref, IMAGE *out ) +{ + Spcor *spcor; + + /* PIO between in and out; WIO from ref, since it's probably tiny. + */ + if( im_piocheck( in, out ) || + im_incheck( ref ) ) + return( -1 ); + + /* Check sizes. + */ + if( in->Xsize < ref->Xsize || + in->Ysize < ref->Ysize ) { + im_error( "im_spcor_raw", + "%s", _( "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_error( "im_spcor_raw", + "%s", _( "input not uncoded 1 band" ) ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR && + in->BandFmt != IM_BANDFMT_CHAR && + in->BandFmt != IM_BANDFMT_SHORT && + in->BandFmt != IM_BANDFMT_USHORT ) { + im_error( "im_spcor_raw", + "%s", _( "input not char/uchar/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( !(spcor = spcor_new( 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, spcor ) ) + 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 ); +} + diff --git a/libvips/convolution/im_stretch3.c b/libvips/convolution/im_stretch3.c new file mode 100644 index 00000000..dcc1eaaa --- /dev/null +++ b/libvips/convolution/im_stretch3.c @@ -0,0 +1,328 @@ +/* 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 +stretch_stop( void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +static void * +stretch_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + StretchInfo *sin = (StretchInfo *) b; + SeqInfo *seq; + + if( !(seq = IM_NEW( out, SeqInfo )) ) + 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 ) { + stretch_stop( seq, NULL, NULL ); + 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, void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + StretchInfo *sin = (StretchInfo *) b; + 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_error( "im_stretch3", + "%s", _( "not uncoded unsigned short" ) ); + return( -1 ); + } + if( dx < 0 || dx >= 1.0 || dy < 0 || dy >= 1.0 ) { + im_error( "im_stretch3", + "%s", _( "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, + stretch_start, stretch_gen, stretch_stop, in, sin ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/convolution/im_zerox.c b/libvips/convolution/im_zerox.c new file mode 100644 index 00000000..74fff483 --- /dev/null +++ b/libvips/convolution/im_zerox.c @@ -0,0 +1,181 @@ +/* @(#) 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 + ba]; \ + \ + 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + IMAGE *in = (IMAGE *) a; + int flag = GPOINTER_TO_INT( b ); + 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 ba = in->Bands; + int ne = ba * 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", "%s", _( "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", "%s", _( "non-complex uncoded only" ) ); + return( -1 ); + } + if( in->Xsize < 2 ) { + im_error( "im_zerox", "%s", _( "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/libvips/convolution/rotmask.c b/libvips/convolution/rotmask.c new file mode 100644 index 00000000..85c1057e --- /dev/null +++ b/libvips/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/libvips/convolution/rw_mask.c b/libvips/convolution/rw_mask.c new file mode 100644 index 00000000..1839e999 --- /dev/null +++ b/libvips/convolution/rw_mask.c @@ -0,0 +1,727 @@ +/* @(#) 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", "%s", _( "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", "%s", _( "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", "%s", _( "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", + "%s", _( "error reading matrix header" ) ); + return( -1 ); + } + if( i == 4 && v[2] == 0 ) { + im_error( "read_header", + "%s", _( "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", + "%s", _( "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", "%s", _( "bad arguments" ) ); + 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", "%s", _( "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", "%s", _( "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", "%s", _( "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/libvips/dummy.c b/libvips/dummy.c new file mode 100644 index 00000000..066b323d --- /dev/null +++ b/libvips/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/libvips/dummy2.cc b/libvips/dummy2.cc new file mode 100644 index 00000000..dda00e51 --- /dev/null +++ b/libvips/dummy2.cc @@ -0,0 +1,4 @@ +/* mac os x libtool needs to links libraries containing c++ (eg. cimg) with + * g++ ... force this with a dummy c++ file at the top level + */ +const int im__dummy_value = 42; diff --git a/libvips/format/Makefile.am b/libvips/format/Makefile.am new file mode 100644 index 00000000..779b24b4 --- /dev/null +++ b/libvips/format/Makefile.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES = libformat.la + +libformat_la_SOURCES = \ + dbh.h \ + format.c \ + format_dispatch.c \ + im_analyze2vips.c \ + im_csv2vips.c \ + im_exr2vips.c \ + im_jpeg2vips.c \ + im_magick2vips.c \ + im_png2vips.c \ + im_ppm2vips.c \ + im_raw2vips.c \ + im_tiff2vips.c \ + im_tile_cache.c \ + im_vips2csv.c \ + im_vips2jpeg.c \ + im_vips2png.c \ + im_vips2ppm.c \ + im_vips2tiff.c \ + im_vips2raw.c \ + matlab.c \ + radiance.c + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/format/dbh.h b/libvips/format/dbh.h new file mode 100644 index 00000000..782cbf67 --- /dev/null +++ b/libvips/format/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/libvips/format/format.c b/libvips/format/format.c new file mode 100644 index 00000000..0c2165a0 --- /dev/null +++ b/libvips/format/format.c @@ -0,0 +1,339 @@ +/* VIPS function dispatch tables for image format load/save. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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*/ + +/* To iterate over supported formats, we build a temp list of subclasses of + * VipsFormat, sort by priority, iterate, and free. + */ + +static void * +format_add_class( VipsFormatClass *format, GSList **formats ) +{ + /* Append so we don't reverse the list of formats. + */ + *formats = g_slist_append( *formats, format ); + + return( NULL ); +} + +static gint +format_compare( VipsFormatClass *a, VipsFormatClass *b ) +{ + return( b->priority - a->priority ); +} + +void * +vips_format_map( VSListMap2Fn fn, void *a, void *b ) +{ + GSList *formats; + void *result; + + formats = NULL; + (void) vips_class_map_concrete_all( g_type_from_name( "VipsFormat" ), + (VipsClassMap) format_add_class, (void *) &formats ); + + formats = g_slist_sort( formats, (GCompareFunc) format_compare ); + result = im_slist_map2( formats, fn, a, b ); + g_slist_free( formats ); + + return( result ); +} + +/* Abstract base class for image formats. + */ + +G_DEFINE_ABSTRACT_TYPE( VipsFormat, vips_format, VIPS_TYPE_OBJECT ); + +static void +vips_format_print_class( VipsObjectClass *object_class, VipsBuf *buf ) +{ + VipsFormatClass *class = VIPS_FORMAT_CLASS( object_class ); + const char **p; + + VIPS_OBJECT_CLASS( vips_format_parent_class )-> + print_class( object_class, buf ); + + vips_buf_appends( buf, ", (" ); + for( p = class->suffs; *p; p++ ) { + vips_buf_appendf( buf, "%s", *p ); + if( p[1] ) + vips_buf_appends( buf, ", " ); + } + vips_buf_appends( buf, ") " ); + + if( class->is_a ) + vips_buf_appends( buf, "is_a " ); + if( class->header ) + vips_buf_appends( buf, "header " ); + if( class->load ) + vips_buf_appends( buf, "load " ); + if( class->save ) + vips_buf_appends( buf, "save " ); + if( class->get_flags ) + vips_buf_appends( buf, "get_flags " ); +} + +static void +vips_format_class_init( VipsFormatClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + + object_class->print_class = vips_format_print_class; +} + +static void +vips_format_init( VipsFormat *object ) +{ +} + +VipsFormatFlags +vips_format_get_flags( VipsFormatClass *format, const char *filename ) +{ + return( format->get_flags ? format->get_flags( filename ) : 0 ); +} + +/* VIPS format class. + */ + +static const char *vips_suffs[] = { ".v", NULL }; + +static int +file2vips( const char *filename, IMAGE *out ) +{ + IMAGE *im; + + if( !(im = im_open_local( out, filename, "r" )) || + im_copy( im, out ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips2file( IMAGE *im, const char *filename ) +{ + IMAGE *out; + + if( !(out = im_open_local( im, filename, "w" )) || + im_copy( im, out ) ) + return( -1 ); + + return( 0 ); +} + +static VipsFormatFlags +vips_flags( const char *filename ) +{ + return( VIPS_FORMAT_PARTIAL ); +} + +/* Vips format adds no new members. + */ +typedef VipsFormat VipsFormatVips; +typedef VipsFormatClass VipsFormatVipsClass; + +static void +vips_format_vips_class_init( VipsFormatVipsClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "vips"; + object_class->description = _( "VIPS" ); + + format_class->is_a = im_isvips; + format_class->header = file2vips; + format_class->load = file2vips; + format_class->save = vips2file; + format_class->get_flags = vips_flags; + format_class->suffs = vips_suffs; +} + +static void +vips_format_vips_init( VipsFormatVips *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatVips, vips_format_vips, VIPS_TYPE_FORMAT ); + +/* Called on startup: register the base vips formats. + */ +void +im__format_init( void ) +{ + extern GType vips_format_csv_get_type(); + extern GType vips_format_ppm_get_type(); + extern GType vips_format_analyze_get_type(); + extern GType vips_format_rad_get_type(); + + vips_format_vips_get_type(); +#ifdef HAVE_JPEG + extern GType vips_format_jpeg_get_type(); + vips_format_jpeg_get_type(); +#endif /*HAVE_JPEG*/ +#ifdef HAVE_PNG + extern GType vips_format_png_get_type(); + vips_format_png_get_type(); +#endif /*HAVE_PNG*/ + vips_format_csv_get_type(); + vips_format_ppm_get_type(); + vips_format_analyze_get_type(); +#ifdef HAVE_OPENEXR + extern GType vips_format_exr_get_type(); + vips_format_exr_get_type(); +#endif /*HAVE_OPENEXR*/ +#ifdef HAVE_MATIO + extern GType vips_format_mat_get_type(); + vips_format_mat_get_type(); +#endif /*HAVE_MATIO*/ + vips_format_rad_get_type(); +#ifdef HAVE_MAGICK + extern GType vips_format_magick_get_type(); + vips_format_magick_get_type(); +#endif /*HAVE_MAGICK*/ +#ifdef HAVE_TIFF + extern GType vips_format_tiff_get_type(); + vips_format_tiff_get_type(); +#endif /*HAVE_TIFF*/ +} + +/* Can this format open this file? + */ +static void * +format_for_file_sub( VipsFormatClass *format, + const char *name, const char *filename ) +{ + if( format->is_a ) { + if( format->is_a( filename ) ) + return( format ); + } + else if( im_filename_suffix_match( filename, format->suffs ) ) + return( format ); + + return( NULL ); +} + +VipsFormatClass * +vips_format_for_file( const char *name ) +{ + char filename[FILENAME_MAX]; + char options[FILENAME_MAX]; + VipsFormatClass *format; + + /* Break any options off the name ... eg. "fred.tif:jpeg,tile" + * etc. + */ + im_filename_split( name, filename, options ); + + if( !im_existsf( "%s", filename ) ) { + im_error( "format_for_file", + _( "file \"%s\" not found" ), filename ); + return( NULL ); + } + + if( !(format = (VipsFormatClass *) vips_format_map( + (VSListMap2Fn) format_for_file_sub, + (void *) name, (void *) filename )) ) { + im_error( "format_for_file", + _( "file \"%s\" not a known format" ), filename ); + return( NULL ); + } + + return( format ); +} + +/* Can we write this filename with this format? Ignore formats without a save + * method. + */ +static void * +format_for_name_sub( VipsFormatClass *format, const char *name ) +{ + if( format->save && + im_filename_suffix_match( name, format->suffs ) ) + return( format ); + + return( NULL ); +} + +VipsFormatClass * +vips_format_for_name( const char *name ) +{ + VipsFormatClass *format; + + if( !(format = (VipsFormatClass *) vips_format_map( + (VSListMap2Fn) format_for_name_sub, (void *) name, NULL )) ) { + char suffix[FILENAME_MAX]; + + im_filename_suffix( name, suffix ); + im_error( "vips_format_for_name", + _( "\"%s\" is not a supported image format." ), + suffix ); + + return( NULL ); + } + + return( format ); +} + +int +vips_format_read( const char *name, IMAGE *out ) +{ + VipsFormatClass *format; + + if( !(format = vips_format_for_file( name )) || + format->load( name, out ) ) + return( -1 ); + + return( 0 ); +} + +int +vips_format_write( IMAGE *im, const char *name ) +{ + VipsFormatClass *format; + + if( !(format = vips_format_for_name( name )) || + format->save( im, name ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/format/format_dispatch.c b/libvips/format/format_dispatch.c new file mode 100644 index 00000000..e5778f7a --- /dev/null +++ b/libvips/format/format_dispatch.c @@ -0,0 +1,417 @@ +/* VIPS function dispatch tables for image format load/save. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 +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 */ +}; + +/* Package up all these functions. + */ +static im_function *list[] = { + &csv2vips_desc, + &jpeg2vips_desc, + &magick2vips_desc, + &png2vips_desc, + &exr2vips_desc, + &ppm2vips_desc, + &analyze2vips_desc, + &tiff2vips_desc, + &vips2csv_desc, + &vips2jpeg_desc, + &vips2mimejpeg_desc, + &vips2png_desc, + &vips2ppm_desc, + &vips2tiff_desc +}; + +/* Package of functions. + */ +im_package im__format = { + "format", + IM_NUMBER( list ), + list +}; diff --git a/libvips/format/im_analyze2vips.c b/libvips/format/im_analyze2vips.c new file mode 100644 index 00000000..ee4e9c12 --- /dev/null +++ b/libvips/format/im_analyze2vips.c @@ -0,0 +1,623 @@ +/* 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 + * 12/5/09 + * - fix signed/unsigned warning + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_free( d ); + return( NULL ); + } + + /* Ouch! Should check at configure time I guess. + */ + g_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( (int) len != d->hk.sizeof_hdr ) { + 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 ); + } + } +} + +static int +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 ); +} + +static int +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 )) ) { + im_error( "im_analyze2vips", + "%s", _( "header file size incorrect" ) ); + 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 )) ) { + im_error( "im_analyze2vips", + "%s", _( "header file size incorrect" ) ); + 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 ); +} + +static const char *analyze_suffs[] = { ".img", ".hdr", NULL }; + +static VipsFormatFlags +analyze_flags( const char *filename ) +{ + return( VIPS_FORMAT_PARTIAL ); +} + +/* analyze format adds no new members. + */ +typedef VipsFormat VipsFormatAnalyze; +typedef VipsFormatClass VipsFormatAnalyzeClass; + +static void +vips_format_analyze_class_init( VipsFormatAnalyzeClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "analyze"; + object_class->description = _( "Analyze 6.0" ); + + format_class->is_a = isanalyze; + format_class->header = analyze2vips_header; + format_class->load = im_analyze2vips; + format_class->get_flags = analyze_flags; + format_class->suffs = analyze_suffs; +} + +static void +vips_format_analyze_init( VipsFormatAnalyze *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatAnalyze, vips_format_analyze, VIPS_TYPE_FORMAT ); + diff --git a/libvips/format/im_csv2vips.c b/libvips/format/im_csv2vips.c new file mode 100644 index 00000000..8e8d417d --- /dev/null +++ b/libvips/format/im_csv2vips.c @@ -0,0 +1,346 @@ +/* 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", + "%s", _( "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", + "%s", _( "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", "%s", _( "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", + "%s", _( "unexpected end of file" ) ); + return( -1 ); + } + else if( ch == '\n' ) { + im_error( "im_csv2vips", + "%s", _( "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. + */ +static int +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 ); +} + +static const char *csv_suffs[] = { ".csv", NULL }; + +/* csv format adds no new members. + */ +typedef VipsFormat VipsFormatCsv; +typedef VipsFormatClass VipsFormatCsvClass; + +static void +vips_format_csv_class_init( VipsFormatCsvClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "csv"; + object_class->description = _( "CSV" ); + + format_class->header = csv2vips_header; + format_class->load = im_csv2vips; + format_class->save = im_vips2csv; + format_class->suffs = csv_suffs; +} + +static void +vips_format_csv_init( VipsFormatCsv *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatCsv, vips_format_csv, VIPS_TYPE_FORMAT ); diff --git a/libvips/format/im_exr2vips.c b/libvips/format/im_exr2vips.c new file mode 100644 index 00000000..98260b5e --- /dev/null +++ b/libvips/format/im_exr2vips.c @@ -0,0 +1,493 @@ +/* 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", "%s", + _( "OpenEXR support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_OPENEXR*/ + +#include +#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 tiled 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 +read_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. + */ +static int +exr2vips_header( const char *name, IMAGE *out ) +{ + Read *read; + + if( !(read = read_new( name, out )) || + read_header( read, out ) ) + return( -1 ); + + return( 0 ); +} + +/* Test for tiled EXR. + */ +static int +isexrtiled( const char *name ) +{ + Read *read; + int tiled; + + if( !(read = read_new( name, NULL )) ) + return( -1 ); + tiled = read->tiles != NULL; + read_destroy( read ); + + return( tiled ); +} + +static int +fill_region( REGION *out, void *seq, void *a, void *b ) +{ + ImfRgba *imf_buffer = (ImfRgba *) seq; + Read *read = (Read *) a; + 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, void *a, void *b ) +{ + Read *read = (Read *) a; + ImfRgba *imf_buffer; + + if( !(imf_buffer = IM_ARRAY( out, + read->tile_width * read->tile_height, ImfRgba )) ) + return( NULL ); + + return( imf_buffer ); +} + +/* Read tilewise. + */ +static int +exr2vips_tiles( Read *read, IMAGE *out ) +{ + if( read_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 )) || + read_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 ); +} + +static int +isexr( const char *filename ) +{ + unsigned char buf[4]; + + if( im__get_bytes( filename, buf, 4 ) ) + if( buf[0] == 0x76 && buf[1] == 0x2f && + buf[2] == 0x31 && buf[3] == 0x01 ) + return( 1 ); + + return( 0 ); +} + +static const char *exr_suffs[] = { ".exr", NULL }; + +static VipsFormatFlags +exr_flags( const char *filename ) +{ + VipsFormatFlags flags; + + flags = 0; + if( isexrtiled( filename ) ) + flags |= VIPS_FORMAT_PARTIAL; + + return( flags ); +} + +/* exr format adds no new members. + */ +typedef VipsFormat VipsFormatExr; +typedef VipsFormatClass VipsFormatExrClass; + +static void +vips_format_exr_class_init( VipsFormatExrClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "exr"; + object_class->description = _( "OpenEXR" ); + + format_class->is_a = isexr; + format_class->header = exr2vips_header; + format_class->load = im_exr2vips; + format_class->get_flags = exr_flags; + format_class->suffs = exr_suffs; +} + +static void +vips_format_exr_init( VipsFormatExr *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatExr, vips_format_exr, VIPS_TYPE_FORMAT ); + +#endif /*HAVE_OPENEXR*/ diff --git a/libvips/format/im_file2vips.c b/libvips/format/im_file2vips.c new file mode 100644 index 00000000..5fd7f4ce --- /dev/null +++ b/libvips/format/im_file2vips.c @@ -0,0 +1,815 @@ +/* Read a VIPS file into a IMAGE * + * + * 22/5/08 + * - from im_open.c, im_openin.c, im_desc_hd.c, im_readhist.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 + +#include +#include +#include +#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 +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* 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*/ + +/* Our XML namespace. + */ +#define NAMESPACE "http://www.vips.ecs.soton.ac.uk/vips" + +/* 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_RAD: + case IM_CODING_NONE: + psize = (gint64) IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize; + break; + + default: + psize = im->Length; + break; + } + + return( psize + im->sizeof_header ); +} + +/* 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; + + /* Always write the magic number MSB first. + */ + magic = im_amiMSBfirst() ? IM_MAGIC_SPARC : IM_MAGIC_INTEL; + to[0] = magic >> 24; + to[1] = magic >> 16; + to[2] = magic >> 8; + to[3] = magic; + q = to + 4; + + 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 ); +} + +/* 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 ); +} + +/* 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 ); +} + +/* Open a VIPS image and byte-swap the image data if necessary. + */ +int +im_file2vips( const char *filename, IMAGE *im ) +{ + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + IMAGE *im2; + + im_filename_split( filename, name, mode ); + + if( !(im = im_init( name )) ) + 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_RAD ) + 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 ); +} + +/* + else if( im_isvips( name ) ) { + if( mode[1] == 'w' ) { + 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 ); + } + */ + +/* Just here for compatibility. + */ +IMAGE * +im_open_header( const char *file ) +{ + return( im_open( file, "r" ) ); +} diff --git a/libvips/format/im_jpeg2vips.c b/libvips/format/im_jpeg2vips.c new file mode 100644 index 00000000..ee375942 --- /dev/null +++ b/libvips/format/im_jpeg2vips.c @@ -0,0 +1,761 @@ +/* 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( const char *name, IMAGE *out ) +{ + im_error( "im_jpeg2vips", "%s", + _( "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( "im_jpeg2vips", _( "%s" ), buffer ); + +#ifdef DEBUG + printf( "im_jpeg2vips: 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( "im_jpeg2vips: 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]; + VipsBuf name; + char value_text[256]; + VipsBuf value; + char exif_value[256]; + + vips_buf_init_static( &name, name_text, 256 ); + vips_buf_init_static( &value, value_text, 256 ); + + vips_buf_appendf( &name, "exif-%s", exif_tag_get_title( entry->tag ) ); + vips_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, + vips_buf_all( &name ), vips_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", + "%s", _( "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", "%s", _( "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. Set invert_pels if the pixel reader needs to + * do 255-pel. + */ +static int +read_jpeg_header( struct jpeg_decompress_struct *cinfo, + IMAGE *out, gboolean *invert_pels, int shrink ) +{ + jpeg_saved_marker_ptr p; + int type; + + /* 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. libjpeg will set out_color_space sanely for us + * for YUV YCCK etc. + */ + jpeg_read_header( cinfo, TRUE ); + cinfo->scale_denom = shrink; + jpeg_calc_output_dimensions( cinfo ); + *invert_pels = FALSE; + switch( cinfo->out_color_space ) { + case JCS_GRAYSCALE: + type = IM_TYPE_B_W; + break; + + case JCS_CMYK: + type = IM_TYPE_CMYK; + /* Photoshop writes CMYK JPEG inverted :-( Maybe this is a + * way to spot photoshop CMYK JPGs. + */ + if( cinfo->saw_Adobe_marker ) + *invert_pels = TRUE; + break; + + case JCS_RGB: + default: + type = IM_TYPE_sRGB; + break; + } + + /* Set VIPS header. + */ + im_initdesc( out, + cinfo->output_width, cinfo->output_height, + cinfo->output_components, + IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, type, + 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 cinfo to a VIPS image. + */ +static int +read_jpeg_image( struct jpeg_decompress_struct *cinfo, IMAGE *out, + gboolean invert_pels ) +{ + int x, 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++ ) { + /* We set an error handler that longjmps() out, so I don't + * think this can fail. + */ + jpeg_read_scanlines( cinfo, &row_pointer[0], 1 ); + + if( invert_pels ) { + for( x = 0; x < sz; x++ ) + row_pointer[0][x] = 255 - row_pointer[0][x]; + } + 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. + */ +static int +jpeg2vips( const char *name, IMAGE *out, gboolean header_only ) +{ + char filename[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char *p, *q; + int shrink; + struct jpeg_decompress_struct cinfo; + ErrorManager eman; + FILE *fp; + int result; + gboolean invert_pels; + gboolean fail_on_warn; + + /* By default, we ignore any warnings. We want to get as much of + * the user's data as we can. + */ + fail_on_warn = FALSE; + + /* Parse the filename. + */ + im_filename_split( name, filename, mode ); + p = &mode[0]; + shrink = 1; + if( (q = im_getnextoption( &p )) ) { + shrink = atoi( q ); + + if( shrink != 1 && shrink != 2 && + shrink != 4 && shrink != 8 ) { + im_error( "im_jpeg2vips", + _( "bad shrink factor %d" ), shrink ); + return( -1 ); + } + } + if( (q = im_getnextoption( &p )) ) { + if( im_isprefix( "fail", q ) ) + fail_on_warn = TRUE; + } + + /* 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. + */ + if( !(fp = im__file_open_read( filename )) ) + 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! + */ + result = read_jpeg_header( &cinfo, out, &invert_pels, shrink ); + if( !header_only && !result ) + result = read_jpeg_image( &cinfo, out, invert_pels ); + + /* Close and tidy. + */ + fclose( fp ); + eman.fp = NULL; + jpeg_destroy_decompress( &cinfo ); + + if( eman.pub.num_warnings != 0 ) { + if( fail_on_warn ) { + im_error( "im_jpeg2vips", "%s", im_error_buffer() ); + result = -1; + } + else { + im_warn( "im_jpeg2vips", _( "read gave %ld warnings" ), + eman.pub.num_warnings ); + im_warn( "im_jpeg2vips", "%s", im_error_buffer() ); + } + } + + return( result ); +} + +/* Read a JPEG file into a VIPS image. + */ +int +im_jpeg2vips( const char *name, IMAGE *out ) +{ + return( jpeg2vips( name, out, FALSE ) ); +} + +static int +isjpeg( const char *filename ) +{ + unsigned char buf[2]; + + if( im__get_bytes( filename, buf, 2 ) ) + if( (int) buf[0] == 0xff && (int) buf[1] == 0xd8 ) + return( 1 ); + + return( 0 ); +} + +static int +jpeg2vips_header( const char *name, IMAGE *out ) +{ + return( jpeg2vips( name, out, TRUE ) ); +} + +static const char *jpeg_suffs[] = { ".jpg", ".jpeg", ".jpe", NULL }; + +/* jpeg format adds no new members. + */ +typedef VipsFormat VipsFormatJpeg; +typedef VipsFormatClass VipsFormatJpegClass; + +static void +vips_format_jpeg_class_init( VipsFormatJpegClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "jpeg"; + object_class->description = _( "JPEG" ); + + format_class->is_a = isjpeg; + format_class->header = jpeg2vips_header; + format_class->load = im_jpeg2vips; + format_class->save = im_vips2jpeg; + format_class->suffs = jpeg_suffs; +} + +static void +vips_format_jpeg_init( VipsFormatJpeg *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatJpeg, vips_format_jpeg, VIPS_TYPE_FORMAT ); + +#endif /*HAVE_JPEG*/ diff --git a/libvips/format/im_magick2vips.c b/libvips/format/im_magick2vips.c new file mode 100644 index 00000000..3d2df9ba --- /dev/null +++ b/libvips/format/im_magick2vips.c @@ -0,0 +1,699 @@ +/* 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 + * 21/2/08 + * - use MaxRGB if QuantumRange is missing (thanks Bob) + * - look for MAGICKCORE_HDRI_SUPPORT (thanks Marcel) + * - use image->attributes if GetNextImageAttribute() is missing + * 3/3/09 + * - allow funky bit depths, like 14 (thanks Mikkel) + * 17/3/09 + * - reset dcm:display-range to help DICOM read + * 20/4/09 + * - argh libMagick uses 255 == transparent ... we must invert all + * alpha channels + * 12/5/09 + * - fix signed/unsigned warnings + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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", "%s", + _( "libMagick support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_MAGICK*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* pre-float Magick used to call this MaxRGB. + */ +#if !defined(QuantumRange) +# define QuantumRange MaxRGB +#endif + +/* And this used to be UseHDRI. + */ +#if MAGICKCORE_HDRI_SUPPORT +# define UseHDRI=1 +#endif + +/* 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; + +#ifdef HAVE_MAGICK_ATTR + const ImageAttribute *attr; +#endif /*HAVE_MAGICK_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 ); + + /* Depth can be 'fractional'. + */ + im->BandFmt = -1; + if( image->depth >= 1 && image->depth <= 8 ) + im->BandFmt = IM_BANDFMT_UCHAR; + if( image->depth >= 9 && image->depth <= 16 ) + im->BandFmt = IM_BANDFMT_USHORT; +#ifdef UseHDRI + if( image->depth == 32 ) + im->BandFmt = IM_BANDFMT_FLOAT; + if( image->depth == 64 ) + im->BandFmt = IM_BANDFMT_DOUBLE; +#else /*!UseHDRI*/ + if( image->depth == 32 ) + im->BandFmt = IM_BANDFMT_UINT; +#endif /*UseHDRI*/ + + if( im->BandFmt == -1 ) { + im_error( "im_magick2vips", _( "unsupported bit depth %d" ), + (int) image->depth ); + return( -1 ); + } + + im->Bbits = im_bits_of_fmt( im->BandFmt ); + + 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_MAGICK_ATTR +#ifdef HAVE_GETNEXTIMAGEATTRIBUTE + /* Gah, magick6.something and later only. Attach any attributes. + */ + ResetImageAttributeIterator( image ); + while( (attr = GetNextImageAttribute( image )) ) { +#elif defined(HAVE_GETIMAGEATTRIBUTE) + /* GraphicsMagick is missing the iterator: we have to loop ourselves. + * ->attributes is marked as private in the header, but there's no + * getter so we have to access it directly. + */ + for( attr = image->attributes; attr; attr = attr->next ) { +#else /*stuff*/ + #error attributes enabled, but no access funcs found +#endif + char name_text[256]; + VipsBuf name; + + vips_buf_init_static( &name, name_text, 256 ); + vips_buf_appendf( &name, "magick-%s", attr->key ); + im_meta_set_string( im, vips_buf_all( &name ), attr->value ); + +#ifdef DEBUG + printf( "key = \"%s\", value = \"%s\"\n", + attr->key, attr->value ); +#endif /*DEBUG*/ + } +#endif /*HAVE_MAGICK_ATTR*/ + + /* 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 != (unsigned int) im->Xsize || + p->rows != (unsigned int) im->Ysize || + p->depth != (unsigned int) 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] = MAX - 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] = MAX - 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 *seq, void *a, void *b ) +{ + Read *read = (Read *) a; + 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", + "%s", _( "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 ); + + /* When reading DICOM images, we want to ignore any + * window_center/_width setting, since it may put pixels outside the + * 0-65535 range and lose data. + * + * These window settings are attached as vips metadata, so our caller + * can interpret them if it wants. + */ + SetImageOption( read->image_info, "dcm:display-range", "reset" ); + + 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 ); +} + +static int +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", "%s", _( "bad image size" ) ); + return( -1 ); + } + + return( 0 ); +} + +static int +ismagick( const char *filename ) +{ + IMAGE *im; + int result; + + if( !(im = im_open( "dummy", "p" )) ) + return( -1 ); + result = magick2vips_header( filename, im ); + im_error_clear(); + im_close( im ); + + return( result == 0 ); +} + +static const char *magick_suffs[] = { NULL }; + +/* magick format adds no new members. + */ +typedef VipsFormat VipsFormatMagick; +typedef VipsFormatClass VipsFormatMagickClass; + +static void +vips_format_magick_class_init( VipsFormatMagickClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "magick"; + object_class->description = _( "libMagick-supported" ); + + format_class->is_a = ismagick; + format_class->header = magick2vips_header; + format_class->load = im_magick2vips; + format_class->suffs = magick_suffs; + + /* This can be very slow :-( Use our own jpeg/tiff/png etc. loaders in + * preference if we can. + */ + format_class->priority = -1000; +} + +static void +vips_format_magick_init( VipsFormatMagick *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatMagick, vips_format_magick, VIPS_TYPE_FORMAT ); + +#endif /*HAVE_MAGICK*/ diff --git a/libvips/format/im_png2vips.c b/libvips/format/im_png2vips.c new file mode 100644 index 00000000..3194f188 --- /dev/null +++ b/libvips/format/im_png2vips.c @@ -0,0 +1,400 @@ +/* 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 + * 28/2/09 + * - small 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 + + */ + +/* +#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", "%s", + _( "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_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 ) +{ + IM_FREE( read->name ); + IM_FREEF( fclose, read->fp ); + if( read->pPng ) + png_destroy_read_struct( &read->pPng, &read->pInfo, NULL ); + IM_FREE( read->row_pointer ); + IM_FREE( read->data ); + + 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; + + if( !(read->fp = im__file_open_read( name )) ) { + read_destroy( read ); + 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", "%s", _( "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. + */ +static int +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 ); +} + +static int +ispng( const char *filename ) +{ + unsigned char buf[8]; + + return( im__get_bytes( filename, buf, 8 ) && + !png_sig_cmp( buf, 0, 8 ) ); +} + +static const char *png_suffs[] = { ".png", NULL }; + +/* png format adds no new members. + */ +typedef VipsFormat VipsFormatPng; +typedef VipsFormatClass VipsFormatPngClass; + +static void +vips_format_png_class_init( VipsFormatPngClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "png"; + object_class->description = _( "PNG" ); + + format_class->is_a = ispng; + format_class->header = png2vips_header; + format_class->load = im_png2vips; + format_class->save = im_vips2png; + format_class->suffs = png_suffs; +} + +static void +vips_format_png_init( VipsFormatPng *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatPng, vips_format_png, VIPS_TYPE_FORMAT ); + +#endif /*HAVE_PNG*/ diff --git a/libvips/format/im_ppm2vips.c b/libvips/format/im_ppm2vips.c new file mode 100644 index 00000000..09e03f37 --- /dev/null +++ b/libvips/format/im_ppm2vips.c @@ -0,0 +1,510 @@ +/* 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 +#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", "%s", _( "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", "%s", _( "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", "%s", + _( "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 ) ); +} + +static int +ppm2vips_header( const char *filename, IMAGE *out ) +{ + FILE *fp; + int bits; + int ascii; + + if( !(fp = im__file_open_read( filename )) ) + return( -1 ); + if( read_header( fp, out, &bits, &ascii ) ) { + fclose( fp ); + return( -1 ); + } + + fclose( fp ); + + return( 0 ); +} + +/* Can this PPM file be read with a mmap? + */ +static int +isppmmmap( const char *filename ) +{ + IMAGE *im; + FILE *fp; + int bits; + int ascii; + + if( !(fp = im__file_open_read( filename )) ) + return( -1 ); + + if( !(im = im_open( "temp", "p" )) ) { + fclose( fp ); + return( 0 ); + } + if( read_header( fp, im, &bits, &ascii ) ) { + im_close( im ); + fclose( fp ); + return( 0 ); + } + im_close( im ); + fclose( fp ); + + return( !ascii && bits >= 8 ); +} + +int +im_ppm2vips( const char *filename, IMAGE *out ) +{ + FILE *fp; + + if( !(fp = im__file_open_read( filename )) ) + return( -1 ); + if( parse_ppm( fp, filename, out ) ) { + fclose( fp ); + return( -1 ); + } + fclose( fp ); + + return( 0 ); +} + +static int +isppm( const char *filename ) +{ + unsigned char buf[2]; + + if( im__get_bytes( filename, buf, 2 ) ) + if( buf[0] == 'P' && (buf[1] >= '1' || buf[1] <= '6') ) + return( 1 ); + + return( 0 ); +} + +/* ppm flags function. + */ +static VipsFormatFlags +ppm_flags( const char *filename ) +{ + VipsFormatFlags flags; + + flags = 0; + if( isppmmmap( filename ) ) + flags |= VIPS_FORMAT_PARTIAL; + + return( flags ); +} + +static const char *ppm_suffs[] = { ".ppm", ".pgm", ".pbm", NULL }; + +/* ppm format adds no new members. + */ +typedef VipsFormat VipsFormatPpm; +typedef VipsFormatClass VipsFormatPpmClass; + +static void +vips_format_ppm_class_init( VipsFormatPpmClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "ppm"; + object_class->description = _( "PPM/PBM/PNM" ); + + format_class->is_a = isppm; + format_class->header = ppm2vips_header; + format_class->load = im_ppm2vips; + format_class->save = im_vips2ppm; + format_class->get_flags = ppm_flags; + format_class->suffs = ppm_suffs; +} + +static void +vips_format_ppm_init( VipsFormatPpm *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatPpm, vips_format_ppm, VIPS_TYPE_FORMAT ); + diff --git a/libvips/format/im_raw2vips.c b/libvips/format/im_raw2vips.c new file mode 100644 index 00000000..e8f50605 --- /dev/null +++ b/libvips/format/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/libvips/format/im_tiff2vips.c b/libvips/format/im_tiff2vips.c new file mode 100644 index 00000000..c298be3e --- /dev/null +++ b/libvips/format/im_tiff2vips.c @@ -0,0 +1,1585 @@ +/* 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 TIFFReadTile() 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() + * 9/4/08 + * - set IM_META_RESOLUTION_UNIT + * 17/4/08 + * - allow CMYKA (thanks Doron) + * 15/8/08 + * - reorganise for image format system + * 20/12/08 + * - dont read with mmap: no performance advantage with libtiff, chews up + * VM wastefully + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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", "%s", + _( "TIFF support disabled" ) ); + return( -1 ); +} + +#else /*HAVE_TIFF*/ + +#include +#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. Shared with im_vips2tiff. These can be called from + * more than one thread, but im_error and im_warn have mutexes in, so that's + * OK. + */ +void +im__thandler_error( char *module, char *fmt, va_list ap ) +{ + im_verror( module, fmt, ap ); +} + +void +im__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", "%s", _( "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/CMYK/CMYKA. + */ +static void +rgbcmyk8_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", + "%s", _( "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) rgbcmyk8_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", + "%s", _( "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 ); +} + +/* Read a CMYK image. + */ +static int +parse_cmyk( ReadTiff *rtiff, IMAGE *out ) +{ + int bands; + + /* Check other TIFF fields to make sure we can read this. Can have 5 + * bands for CMYKA. + */ + if( !tfequals( rtiff->tiff, TIFFTAG_BITSPERSAMPLE, 8 ) || + !tfequals( rtiff->tiff, TIFFTAG_INKSET, INKSET_CMYK ) || + !tfget16( rtiff->tiff, TIFFTAG_SAMPLESPERPIXEL, &bands ) ) + return( -1 ); + if( bands != 4 && bands != 5 ) { + im_error( "im_tiff2vips", + "%s", _( "4 or 5 bands CMYK TIFF only" ) ); + return( -1 ); + } + + out->Bands = bands; + out->Bbits = 8; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Coding = IM_CODING_NONE; + out->Type = IM_TYPE_CMYK; + + rtiff->sfn = (scanline_process_fn) rgbcmyk8_line; + rtiff->client = out; + + 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; + im_meta_set_string( out, + IM_META_RESOLUTION_UNIT, "in" ); + break; + + case RESUNIT_CENTIMETER: + /* In pixels-per-centimetre ... convert to mm. + */ + x /= 10.0; + y /= 10.0; + im_meta_set_string( out, + IM_META_RESOLUTION_UNIT, "cm" ); + break; + + default: + im_error( "im_tiff2vips", + "%s", _( "unknown resolution unit" ) ); + return( -1 ); + } + } + else { + im_warn( "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, void *a, void *b ) +{ + ReadTiff *rtiff = (ReadTiff *) a; + 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, void *seq, void *a, void *b ) +{ + tdata_t *buf = (tdata_t *) seq; + ReadTiff *rtiff = (ReadTiff *) a; + 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 ); +} + +static int +seq_stop( void *seq, void *a, void *b ) +{ + im_free( seq ); + + 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, seq_stop, 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", "%s", _( "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 mmap --- no performance advantage with libtiff, and it burns up + * our VM if the tiff file is large. + */ + if( !(tif = TIFFOpen( filename, "rm" )) ) { + 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 ); +} + +/* + + FIXME ... Unused for now, perhaps if we add another format flag. + +static int +istiffpyramid( const char *name ) +{ + TIFF *tif; + + TIFFSetErrorHandler( (TIFFErrorHandler) im__thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) im__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) im__thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) im__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. + */ +static int +tiff2vips_header( const char *filename, IMAGE *out ) +{ + ReadTiff *rtiff; + + TIFFSetErrorHandler( (TIFFErrorHandler) im__thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) im__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 ); +} + +static int +istiff( const char *filename ) +{ + unsigned char buf[2]; + + if( im__get_bytes( filename, buf, 2 ) ) + if( (buf[0] == 'M' && buf[1] == 'M') || + (buf[0] == 'I' && buf[1] == 'I') ) + return( 1 ); + + return( 0 ); +} + +static int +istifftiled( const char *filename ) +{ + TIFF *tif; + int tiled; + + /* Override the default TIFF error handler. + */ + TIFFSetErrorHandler( (TIFFErrorHandler) im__thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) im__thandler_warning ); + + if( !(tif = TIFFOpen( filename, "rm" )) ) { + /* Not a TIFF file ... return False. + */ + im_error_clear(); + return( 0 ); + } + tiled = TIFFIsTiled( tif ); + TIFFClose( tif ); + + return( tiled ); +} + +/* TIFF flags function. + */ +static VipsFormatFlags +tiff_flags( const char *filename ) +{ + VipsFormatFlags flags; + + flags = 0; + if( istifftiled( filename ) ) + flags |= VIPS_FORMAT_PARTIAL; + + return( flags ); +} + +static const char *tiff_suffs[] = { ".tif", ".tiff", NULL }; + +/* tiff format adds no new members. + */ +typedef VipsFormat VipsFormatTiff; +typedef VipsFormatClass VipsFormatTiffClass; + +static void +vips_format_tiff_class_init( VipsFormatTiffClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "tiff"; + object_class->description = _( "TIFF" ); + + format_class->is_a = istiff; + format_class->header = tiff2vips_header; + format_class->load = im_tiff2vips; + format_class->save = im_vips2tiff; + format_class->get_flags = tiff_flags; + format_class->suffs = tiff_suffs; +} + +static void +vips_format_tiff_init( VipsFormatTiff *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatTiff, vips_format_tiff, VIPS_TYPE_FORMAT ); + +#endif /*HAVE_TIFF*/ diff --git a/libvips/format/im_tile_cache.c b/libvips/format/im_tile_cache.c new file mode 100644 index 00000000..ac3a985e --- /dev/null +++ b/libvips/format/im_tile_cache.c @@ -0,0 +1,391 @@ +/* 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, void *a, void *b ) +{ + Read *read = (Read *) a; + 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", "%s", _( "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/libvips/format/im_vips2csv.c b/libvips/format/im_vips2csv.c new file mode 100644 index 00000000..74dc0340 --- /dev/null +++ b/libvips/format/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", "%s", _( "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/libvips/format/im_vips2jpeg.c b/libvips/format/im_vips2jpeg.c new file mode 100644 index 00000000..58c73952 --- /dev/null +++ b/libvips/format/im_vips2jpeg.c @@ -0,0 +1,897 @@ +/* Convert 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", "%s", + _( "JPEG support disabled" ) ); + + return( -1 ); +} + +int +im_vips2bufjpeg( IMAGE *in, IMAGE *out, int qfac, char **obuf, int *olen ) +{ + im_error( "im_vips2bufjpeg", "%s", + _( "JPEG support disabled" ) ); + + return( -1 ); +} + +int +im_vips2mimejpeg( IMAGE *in, int qfac ) +{ + im_error( "im_vips2mimejpeg", "%s", + _( "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; + im_threadgroup_t *tg; + JSAMPROW *row_pointer; + char *profile_bytes; + unsigned int profile_length; + IMAGE *inverted; +} Write; + +static void +write_destroy( Write *write ) +{ + jpeg_destroy_compress( &write->cinfo ); + IM_FREEF( im_threadgroup_free, write->tg ); + IM_FREEF( im_close, write->in ); + IM_FREEF( fclose, write->eman.fp ); + IM_FREE( write->row_pointer ); + IM_FREE( write->profile_bytes ); + IM_FREEF( im_close, write->inverted ); + 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, IM__RGB_CMYK )) ) { + im_error( "im_vips2jpeg", + "%s", _( "unable to convert to saveable format" ) ); + write_destroy( write ); + return( NULL ); + } + + write->tg = NULL; + write->row_pointer = NULL; + 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; + write->inverted = 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", + "%s", _( "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_typeof( 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", "%s", _( "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_typeof( 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 ); +} + +static int +write_jpeg_block( REGION *region, Rect *area, void *a, void *b ) +{ + Write *write = (Write *) a; + int i; + + /* We are running in a background thread. We need to catch longjmp()s + * here instead. + */ + if( setjmp( write->eman.jmp ) ) + return( -1 ); + + for( i = 0; i < area->height; i++ ) + write->row_pointer[i] = (JSAMPROW) + IM_REGION_ADDR( region, 0, area->top + i ); + + jpeg_write_scanlines( &write->cinfo, write->row_pointer, area->height ); + + return( 0 ); +} + +/* Write a VIPS image to a JPEG compress struct. + */ +static int +write_vips( Write *write, int qfac, const char *profile ) +{ + IMAGE *in; + J_COLOR_SPACE space; + + /* The image we'll be writing ... can change, see CMYK. + */ + in = write->in; + + /* 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 || in->Bands == 4 ); + + /* Check input image. + */ + if( im_pincheck( in ) ) + return( -1 ); + if( qfac < 0 || qfac > 100 ) { + im_error( "im_vips2jpeg", + "%s", _( "qfac should be in 0-100" ) ); + return( -1 ); + } + + /* Set compression parameters. + */ + write->cinfo.image_width = in->Xsize; + write->cinfo.image_height = in->Ysize; + write->cinfo.input_components = in->Bands; + if( in->Bands == 4 && in->Type == IM_TYPE_CMYK ) { + space = JCS_CMYK; + /* IJG always sets an Adobe marker, so we should invert CMYK. + */ + if( !(write->inverted = im_open( "vips2jpeg_invert", "p" )) || + im_invert( in, write->inverted ) ) + return( -1 ); + in = write->inverted; + } + else if( in->Bands == 3 ) + space = JCS_RGB; + else if( in->Bands == 1 ) + space = JCS_GRAYSCALE; + else + /* Use luminance compression for all channels. + */ + space = JCS_UNKNOWN; + write->cinfo.in_color_space = space; + + /* Build VIPS output stuff now we know the image we'll be writing. + */ + write->tg = im_threadgroup_create( in ); + write->row_pointer = IM_ARRAY( NULL, write->tg->nlines, JSAMPROW ); + if( !write->tg || !write->row_pointer ) + return( -1 ); + + /* 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_typeof( in, IM_META_ICC_NAME ) && + write_profile_meta( write ) ) + return( -1 ); + + /* Write data. Note that the write function grabs the longjmp()! + */ + if( im_wbuffer( write->tg, write_jpeg_block, write, NULL ) ) + return( -1 ); + + /* We have to reinstate the setjmp() before we jpeg_finish_compress(). + */ + if( setjmp( write->eman.jmp ) ) + return( -1 ); + + 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. + */ + if( !(write->eman.fp = im__file_open_write( name )) ) { + write_destroy( write ); + 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. + + FIXME ... argh, the retuen length should really be a size_t, but we can't fix this now sadly + + */ +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" ); + if( fwrite( buf, sizeof( char ), len, stdout ) != (size_t) len ) { + im_error( "im_vips2mimejpeg", + "%s", _( "error writing output" ) ); + return( -1 ); + } + + fflush( stdout ); + im_close( base ); + + return( 0 ); +} + +#endif /*HAVE_JPEG*/ diff --git a/libvips/format/im_vips2png.c b/libvips/format/im_vips2png.c new file mode 100644 index 00000000..4131ebf5 --- /dev/null +++ b/libvips/format/im_vips2png.c @@ -0,0 +1,316 @@ +/* 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 + * 2/11/07 + * - use im_wbuffer() API for BG writes + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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", "%s", + _( "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; + 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_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, IM__RGBA )) ) { + im_error( "im_vips2png", + "%s", _( "unable to convert to RGB for save" ) ); + write_destroy( write ); + return( NULL ); + } + + 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->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 ); +} + +static int +write_png_block( REGION *region, Rect *area, void *a, void *b ) +{ + Write *write = (Write *) a; + int i; + + /* Catch PNG errors. Yuk. + */ + if( setjmp( write->pPng->jmpbuf ) ) + return( -1 ); + + for( i = 0; i < area->height; i++ ) + write->row_pointer[i] = (png_bytep) + IM_REGION_ADDR( region, 0, area->top + i ); + + png_write_rows( write->pPng, write->row_pointer, area->height ); + + return( 0 ); +} + +/* Write a VIPS image to PNG. + */ +static int +write_vips( Write *write, int compress, int interlace ) +{ + IMAGE *in = write->in; + + int 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", + "%s", _( "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++ ) + if( im_wbuffer( write->tg, write_png_block, write, NULL ) ) + return( -1 ); + + /* The setjmp() was held by our background writer: reset it. + */ + if( setjmp( write->pPng->jmpbuf ) ) + return( -1 ); + + 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. + */ + if( !(write->fp = im__file_open_write( name )) ) { + write_destroy( write ); + 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/libvips/format/im_vips2ppm.c b/libvips/format/im_vips2ppm.c new file mode 100644 index 00000000..b1a76982 --- /dev/null +++ b/libvips/format/im_vips2ppm.c @@ -0,0 +1,287 @@ +/* Write a ppm file. + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 9/9/05 + * - tiny cleanups + * 3/11/07 + * - use im_wbuffer() for bg writes + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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; + im_threadgroup_t *tg; + FILE *fp; + char *name; +} Write; + +static void +write_destroy( Write *write ) +{ + IM_FREEF( im_threadgroup_free, write->tg ); + 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->tg = im_threadgroup_create( write->in ); + write->name = im_strdup( NULL, name ); + write->fp = im__file_open_write( name ); + + if( !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", + "%s", _( "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", + "%s", _( "write error ... disc full?" ) ); + return( -1 ); + } + + p += sk; + } + + return( 0 ); +} + +static int +write_ppm_block( REGION *region, Rect *area, void *a, void *b ) +{ + Write *write = (Write *) a; + write_fn fn = (write_fn) b; + int i; + + for( i = 0; i < area->height; i++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( region, 0, area->top + i ); + + if( fn( write->in, write->fp, p ) ) + return( -1 ); + } + + 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; + + 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 && ascii ) + magic = "P3"; + else if( in->Bands == 3 && !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 ); + + if( im_wbuffer( write->tg, write_ppm_block, write, fn ) ) + 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", + "%s", _( "bad mode string, " + "should be \"binary\" or \"ascii\"" ) ); + return( -1 ); + } + } + + if( in->Bbits > 8 && !ascii ) { + im_error( "im_vips2ppm", + "%s", _( "can't write binary >8 bit images" ) ); + return( -1 ); + } + if( !im_isuint( in ) ) { + im_error( "im_vips2ppm", + "%s", _( "unsigned int formats only" ) ); + return( -1 ); + } + if( in->Coding != IM_CODING_NONE && in->Coding != IM_CODING_LABQ ) { + im_error( "im_vips2ppm", + "%s", _( "uncoded or IM_CODING_LABQ only" ) ); + return( -1 ); + } + if( in->Bands != 1 && in->Bands != 3 ) { + im_error( "im_vips2ppm", "%s", _( "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/libvips/format/im_vips2raw.c b/libvips/format/im_vips2raw.c new file mode 100644 index 00000000..6285d4c8 --- /dev/null +++ b/libvips/format/im_vips2raw.c @@ -0,0 +1,126 @@ +/* Write raw image data to file. Usefull when defining new formats... + * + * Jesper Friis + * + * 10/06/08 JF + * - initial code based on im_vips2ppm() + * + * 04/07/08 JF + * - replaced FILE with plain file handlers for reducing + * confusion about binary vs. non-binary file modes. + */ + + +/* + This file is part of the QED plugin to VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + + + +/* What we track during a write + */ +typedef struct { + IMAGE *in; + im_threadgroup_t *tg; + int fd; +} Write; + + + +static void +write_destroy( Write *write ) +{ + IM_FREEF( im_threadgroup_free, write->tg ); + im_free( write ); +} + + +static Write * +write_new( IMAGE *in, int fd ) +{ + Write *write; + + if( !(write = IM_NEW( NULL, Write )) ) + return( NULL ); + + write->in = in; + write->tg = im_threadgroup_create( write->in ); + write->fd = fd; + + if( !write->tg || !write->fd ) { + write_destroy( write ); + return( NULL ); + } + + return( write ); +} + + +static int +write_block( REGION *region, Rect *area, void *a, void *b ) +{ + Write *write = (Write *) a; + int i; + + for( i = 0; i < area->height; i++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( region, area->left, area->top + i ); + if( im__write( write->fd, p, IM_IMAGE_SIZEOF_PEL(write->in)*area->width ) ) + return( -1 ); + } + + return( 0 ); +} + + +int +im_vips2raw( IMAGE *in, int fd ) +{ + Write *write; + + if( im_pincheck( in ) || !(write = write_new( in, fd )) ) + return( -1 ); + + if( im_wbuffer( write->tg, write_block, write, NULL ) ) { + write_destroy( write ); + return( -1 ); + } + + write_destroy( write ); + return( 0 ); +} + diff --git a/libvips/format/im_vips2tiff.c b/libvips/format/im_vips2tiff.c new file mode 100644 index 00000000..eef3cea4 --- /dev/null +++ b/libvips/format/im_vips2tiff.c @@ -0,0 +1,1679 @@ +/* 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 + * 3/11/07 + * - use im_wbuffer() for background writes + * 15/2/08 + * - set TIFFTAG_JPEGQUALITY explicitly when we copy TIFF files, since + * libtiff doesn't keep this in the header (thanks Joe) + * 20/2/08 + * - use tiff error handler from im_tiff2vips.c + * 27/2/08 + * - don't try to copy icc profiles when building pyramids (thanks Joe) + * 9/4/08 + * - use IM_META_RESOLUTION_UNIT to set default resunit + * 17/4/08 + * - allow CMYKA (thanks Doron) + * 5/9/08 + * - trigger eval callbacks during tile 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 + + */ + +/* 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", "%s", + _( "TIFF support disabled" ) ); + + return( -1 ); +} + +#else /*HAVE_TIFF*/ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#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; + +/* Use these from im_tiff2vips(). + */ +void im__thandler_error( char *module, char *fmt, va_list ap ); +void im__thandler_warning( char *module, char *fmt, va_list 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 from a file. + */ +static int +embed_profile_file( 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 ); +} + +static int +embed_profile( TiffWrite *tw, TIFF *tif ) +{ + if( tw->embed && embed_profile_file( tif, tw->icc_profile ) ) + return( -1 ); + if( !tw->embed && im_header_get_typeof( tw->im, IM_META_ICC_NAME ) && + embed_profile_meta( tif, tw->im ) ) + return( -1 ); + + 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 ); + + if( tw->predictor != -1 ) + TIFFSetField( tif, TIFFTAG_PREDICTOR, tw->predictor ); + + /* Attach ICC profile. + */ + if( embed_profile( tw, tif ) ) + 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; + + case 5: + if( tw->im->Type == IM_TYPE_CMYK ) { + photometric = PHOTOMETRIC_SEPARATED; + TIFFSetField( tif, + TIFFTAG_INKSET, INKSET_CMYK ); + } + 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", "%s", _( "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", "%s", _( "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", + "%s", _( "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 ); + + /* Trigger any eval callbacks on our source image and + * check for errors. + */ + if( im__handle_eval( im, area.width, area.height ) || + im_threadgroup_iserror( tw->tg ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +write_tif_block( REGION *region, Rect *area, void *a, void *b ) +{ + TiffWrite *tw = (TiffWrite *) a; + IMAGE *im = tw->im; + + int y; + + for( y = 0; y < area->height; y++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( region, 0, area->top + y ); + + /* 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; + } + + if( TIFFWriteScanline( tw->tif, p, area->top + y, 0 ) < 0 ) + return( -1 ); + } + + return( 0 ); +} + +/* Write as scan-lines. + */ +static int +write_tif_strip( TiffWrite *tw ) +{ + g_assert( !tw->tbuf ); + + if( !(tw->tbuf = im_malloc( NULL, TIFFScanlineSize( tw->tif ) )) ) + return( -1 ); + + if( im_wbuffer( tw->tg, write_tif_block, tw, NULL ) ) + 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; + if( !im_meta_get_string( im, IM_META_RESOLUTION_UNIT, &p ) && + strcmp( p, "in" ) == 0 ) { + tw->resunit = RESUNIT_INCH; + tw->xres *= 2.54; + tw->yres *= 2.54; + } + + /* 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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", "%s", + _( "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", "%s", + _( "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 ) ) { + if( tw->resunit == RESUNIT_INCH ) { + tw->xres /= 2.54; + tw->yres /= 2.54; + } + tw->resunit = RESUNIT_CENTIMETER; + } + else if( im_isprefix( "res_inch", q ) ) { + if( tw->resunit == RESUNIT_CENTIMETER ) { + tw->xres *= 2.54; + tw->yres *= 2.54; + } + 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", "%s", + _( "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", "%s", _( "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", + "%s", _( "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", "%s", _( "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( TiffWrite *tw, TIFF *out, TIFF *in ) +{ + uint32 i32; + uint16 i16; + 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_XRESOLUTION, f ); + CopyField( TIFFTAG_YRESOLUTION, f ); + CopyField( TIFFTAG_RESOLUTIONUNIT, i16 ); + CopyField( TIFFTAG_COMPRESSION, 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( tw->predictor != -1 ) + TIFFSetField( out, TIFFTAG_PREDICTOR, tw->predictor ); + + /* TIFFTAG_JPEGQUALITY is a pesudo-tag, so we can't copy it. + * Set explicitly from TiffWrite. + */ + if( tw->compression == COMPRESSION_JPEG ) + TIFFSetField( out, TIFFTAG_JPEGQUALITY, tw->jpqual ); + + /* We can't copy profiles :( Set again from TiffWrite. + */ + if( embed_profile( tw, out ) ) + return( -1 ); + + buf = im_malloc( NULL, TIFFTileSize( in ) ); + n = TIFFNumberOfTiles( in ); + for( tile = 0; tile < n; tile++ ) { + tsize_t len; + + /* It'd be good to use TIFFReadRawTile()/TIFFWriteRawTile() + * here to save compression/decompression, but sadly it seems + * not to work :-( investigate at some point. + */ + 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( TiffWrite *tw, TIFF *out, const char *name ) +{ + TIFF *in; + + if( !(in = tiff_openin( name )) ) + return( -1 ); + + if( tiff_copy( tw, 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( tw, out, tw->bname ) ) { + TIFFClose( out ); + return( -1 ); + } + + for( layer = tw->layer; layer; layer = layer->below ) + if( tiff_append( tw, 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) im__thandler_error ); + TIFFSetWarningHandler( (TIFFErrorHandler) im__thandler_warning ); + + /* Check input image. + */ + if( im_pincheck( im ) ) + return( -1 ); + if( im->Coding != IM_CODING_LABQ && im->Coding != IM_CODING_NONE ) { + im_error( "im_vips2tiff", "%s", _( "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", "%s", + _( "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 > 5 ) { + im_error( "im_vips2tiff", "%s", + _( "1 to 5 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 ) { + /* The strip writer uses wbuffer and does start/end for us, we + * have our own loop for tile writing and so we need to call + * ourselves. + */ + if( im__start_eval( im ) ) { + free_tiff_write( tw ); + return( -1 ); + } + res = write_tif_tile( tw ); + im__end_eval( im ); + } + 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/libvips/format/matlab.c b/libvips/format/matlab.c new file mode 100644 index 00000000..c07b4318 --- /dev/null +++ b/libvips/format/matlab.c @@ -0,0 +1,309 @@ +/* Read matlab save files with libmatio + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + + Remaining issues: + ++ it transposes images, I think, since Matlab seems to use column-major order + ++ will colour images work? no idea, needs testing + ++ it will not do complex images + ++ it will not handle sparse matricies + ++ it loads the first variable in the file with between 1 and 3 dimensions, + is this sensible behaviour? + ++ load only, no save + ++ could use much less memory --- we use Mat_VarReadDataAll() to read the + whole variable into mem, then copy to a vips buffer, yuk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifdef HAVE_MATIO + +#include +#include +#include + +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* What we track during a Mat-file read. + */ +typedef struct { + char *filename; + IMAGE *out; + + mat_t *mat; + matvar_t *var; +} Read; + +static void +read_destroy( Read *read ) +{ + IM_FREE( read->filename ); + IM_FREEF( Mat_VarFree, read->var ); + IM_FREEF( Mat_Close, read->mat ); + + im_free( read ); +} + +static Read * +read_new( const char *filename, IMAGE *out ) +{ + Read *read; + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + + read->filename = im_strdup( NULL, filename ); + read->out = out; + read->mat = NULL; + read->var = NULL; + + if( !(read->mat = Mat_Open( filename, MAT_ACC_RDONLY )) ) { + im_error( "mat2vips", + _( "unable to open \"%s\"" ), filename ); + read_destroy( read ); + return( NULL ); + } + + for(;;) { + if( !(read->var = Mat_VarReadNextInfo( read->mat )) ) { + im_error( "mat2vips", + _( "no matrix variables in \"%s\"" ), + filename ); + read_destroy( read ); + return( NULL ); + } + +#ifdef DEBUG + printf( "mat2vips: seen:\n" ); + printf( "var->name == %s\n", read->var->name ); + printf( "var->class_type == %d\n", read->var->class_type ); + printf( "var->rank == %d\n", read->var->rank ); +#endif /*DEBUG*/ + + /* Vector to colour image is OK for us. + */ + if( read->var->rank >= 1 && read->var->rank <= 3 ) + break; + + IM_FREEF( Mat_VarFree, read->var ); + } + + return( read ); +} + +/* Matlab classes -> VIPS band formats. + */ +static int mat2vips_formats[][2] = { + { MAT_C_UINT8, IM_BANDFMT_UCHAR }, + { MAT_C_INT8, IM_BANDFMT_CHAR }, + { MAT_C_UINT16, IM_BANDFMT_USHORT }, + { MAT_C_INT16, IM_BANDFMT_SHORT }, + { MAT_C_UINT32, IM_BANDFMT_UINT }, + { MAT_C_INT32, IM_BANDFMT_INT }, + { MAT_C_SINGLE, IM_BANDFMT_FLOAT }, + { MAT_C_DOUBLE, IM_BANDFMT_DOUBLE } +}; + +static int +mat2vips_get_header( matvar_t *var, IMAGE *im ) +{ + int width, height, bands, format, type; + int i; + + width = 1; + height = 1; + bands = 1; + switch( var->rank ) { + case 3: + bands = var->dims[2]; + + case 2: + height = var->dims[1]; + + case 1: + width = var->dims[0]; + break; + + default: + im_error( "mat2vips", _( "unsupported bands %d\n" ), + var->rank ); + return( -1 ); + } + + if( bands > 1 ) + type = IM_TYPE_MULTIBAND; + else + type = IM_TYPE_B_W; + + for( i = 0; i < IM_NUMBER( mat2vips_formats ); i++ ) + if( mat2vips_formats[i][0] == var->class_type ) + break; + if( i == IM_NUMBER( mat2vips_formats ) ) { + im_error( "mat2vips", _( "unsupported class type %d\n" ), + var->class_type ); + return( -1 ); + } + format = mat2vips_formats[i][1]; + + im_initdesc( im, + width, height, bands, + im_bits_of_fmt( format ), format, + IM_CODING_NONE, type, 1.0, 1.0, 0, 0 ); + + return( 0 ); +} + +static int +mat2vips_header( const char *filename, IMAGE *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "mat2vips_header: reading \"%s\"\n", filename ); +#endif /*DEBUG*/ + + if( !(read = read_new( filename, out )) ) + return( -1 ); + if( mat2vips_get_header( read->var, read->out ) ) { + read_destroy( read ); + return( -1 ); + } + read_destroy( read ); + + return( 0 ); +} + +static int +mat2vips_get_data( mat_t *mat, matvar_t *var, IMAGE *im ) +{ + int y; + + if( Mat_VarReadDataAll( mat, var ) ) { + im_error( "mat2vips", "%s", _( "Mat_VarReadDataAll failed" ) ); + return( -1 ); + } + if( im_outcheck( im ) || + im_setupout( im ) ) + return( -1 ); + + for( y = 0; y < im->Ysize; y++ ) + if( im_writeline( y, im, + var->data + y * IM_IMAGE_SIZEOF_LINE( im ) ) ) + return( -1 ); + + return( 0 ); +} + +static int +mat2vips( const char *filename, IMAGE *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "mat2vips: reading \"%s\"\n", filename ); +#endif /*DEBUG*/ + + if( !(read = read_new( filename, out )) ) + return( -1 ); + if( mat2vips_get_header( read->var, read->out ) || + mat2vips_get_data( read->mat, read->var, read->out ) ) { + read_destroy( read ); + return( -1 ); + } + read_destroy( read ); + + return( 0 ); +} + +static int +ismat( const char *filename ) +{ + mat_t *mat; + + if( !(mat = Mat_Open( filename, MAT_ACC_RDONLY )) ) + return( 0 ); + Mat_Close( mat ); + + return( 1 ); +} + +static const char *mat_suffs[] = { ".mat", NULL }; + +/* mat format adds no new members. + */ +typedef VipsFormat VipsFormatMat; +typedef VipsFormatClass VipsFormatMatClass; + +static void +vips_format_mat_class_init( VipsFormatMatClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "mat"; + object_class->description = _( "Matlab" ); + + format_class->is_a = ismat; + format_class->header = mat2vips_header; + format_class->load = mat2vips; + format_class->save = NULL; + format_class->suffs = mat_suffs; +} + +static void +vips_format_mat_init( VipsFormatMat *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatMat, vips_format_mat, VIPS_TYPE_FORMAT ); + +#endif /*HAVE_MATIO*/ diff --git a/libvips/format/radiance.c b/libvips/format/radiance.c new file mode 100644 index 00000000..bb290b48 --- /dev/null +++ b/libvips/format/radiance.c @@ -0,0 +1,1202 @@ +/* Read Radiance (.hdr) files + * + * 3/3/09 + * - write packed data, a separate im_rad2float() operation can unpack + * 23/3/09 + * - add radiance 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 + + */ + +/* + + Remaining issues: + ++ it ignores some header fields, like VIEW and DATE + ++ it will not rotate/flip as the FORMAT string asks + + */ + +/* + + Sections of this reader from Greg Ward and Radiance with kind + permission. The Radience copyright notice appears below. + + */ + +/* ==================================================================== + * The Radiance Software License, Version 1.0 + * + * Copyright (c) 1990 - 2009 The Regents of the University of California, + * through Lawrence Berkeley National Laboratory. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes Radiance software + * (http://radsite.lbl.gov/) + * developed by the Lawrence Berkeley National Laboratory + * (http://www.lbl.gov/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Radiance," "Lawrence Berkeley National Laboratory" + * and "The Regents of the University of California" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact radiance@radsite.lbl.gov. + * + * 5. Products derived from this software may not be called "Radiance", + * nor may "Radiance" appear in their name, without prior written + * permission of Lawrence Berkeley National Laboratory. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Lawrence Berkeley National Laboratory OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of Lawrence Berkeley National Laboratory. For more + * information on Lawrence Berkeley National Laboratory, please see + * . + */ + +/* +#define DEBUG + */ + +#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*/ + +/* Begin copy-paste from Radiance sources. + */ + + /* flags for scanline ordering */ +#define XDECR 1 +#define YDECR 2 +#define YMAJOR 4 + + /* standard scanline ordering */ +#define PIXSTANDARD (YMAJOR|YDECR) +#define PIXSTDFMT "-Y %d +X %d\n" + + /* structure for image dimensions */ +typedef struct { + int rt; /* orientation (from flags above) */ + int xr, yr; /* x and y resolution */ +} RESOLU; + + /* macros to get scanline length and number */ +#define scanlen(rs) ((rs)->rt & YMAJOR ? (rs)->xr : (rs)->yr) +#define numscans(rs) ((rs)->rt & YMAJOR ? (rs)->yr : (rs)->xr) + + /* resolution string buffer and its size */ +#define RESOLU_BUFLEN 32 + + /* macros for reading/writing resolution struct */ +#define fputsresolu(rs,fp) fputs(resolu2str(resolu_buf,rs),fp) +#define fgetsresolu(rs,fp) str2resolu(rs, \ + fgets(resolu_buf,RESOLU_BUFLEN,fp)) + + /* reading/writing of standard ordering */ +#define fprtresolu(sl,ns,fp) fprintf(fp,PIXSTDFMT,ns,sl) +#define fscnresolu(sl,ns,fp) (fscanf(fp,PIXSTDFMT,ns,sl)==2) + + /* defined in resolu.c */ +typedef int gethfunc(char *s, void *p); /* callback to process header lines */ + + +#define RED 0 +#define GRN 1 +#define BLU 2 +#define CIEX 0 /* or, if input is XYZ... */ +#define CIEY 1 +#define CIEZ 2 +#define EXP 3 /* exponent same for either format */ +#define COLXS 128 /* excess used for exponent */ +#define WHT 3 /* used for RGBPRIMS type */ + +#undef BYTE +#define BYTE unsigned char /* 8-bit unsigned integer */ + +typedef BYTE COLR[4]; /* red, green, blue (or X,Y,Z), exponent */ + +typedef float COLORV; +typedef COLORV COLOR[3]; /* red, green, blue (or X,Y,Z) */ + +typedef float RGBPRIMS[4][2]; /* (x,y) chromaticities for RGBW */ +typedef float (*RGBPRIMP)[2]; /* pointer to RGBPRIMS array */ + +typedef float COLORMAT[3][3]; /* color coordinate conversion matrix */ + +#define copycolr(c1,c2) (c1[0]=c2[0],c1[1]=c2[1], \ + c1[2]=c2[2],c1[3]=c2[3]) + +#define colval(col,pri) ((col)[pri]) + +#define setcolor(col,r,g,b) ((col)[RED]=(r),(col)[GRN]=(g),(col)[BLU]=(b)) + +#define copycolor(c1,c2) ((c1)[0]=(c2)[0],(c1)[1]=(c2)[1],(c1)[2]=(c2)[2]) + +#define scalecolor(col,sf) ((col)[0]*=(sf),(col)[1]*=(sf),(col)[2]*=(sf)) + +#define addcolor(c1,c2) ((c1)[0]+=(c2)[0],(c1)[1]+=(c2)[1],(c1)[2]+=(c2)[2]) + +#define multcolor(c1,c2) ((c1)[0]*=(c2)[0],(c1)[1]*=(c2)[1],(c1)[2]*=(c2)[2]) + +#ifdef NTSC +#define CIE_x_r 0.670 /* standard NTSC primaries */ +#define CIE_y_r 0.330 +#define CIE_x_g 0.210 +#define CIE_y_g 0.710 +#define CIE_x_b 0.140 +#define CIE_y_b 0.080 +#define CIE_x_w 0.3333 /* use true white */ +#define CIE_y_w 0.3333 +#else +#define CIE_x_r 0.640 /* nominal CRT primaries */ +#define CIE_y_r 0.330 +#define CIE_x_g 0.290 +#define CIE_y_g 0.600 +#define CIE_x_b 0.150 +#define CIE_y_b 0.060 +#define CIE_x_w 0.3333 /* use true white */ +#define CIE_y_w 0.3333 +#endif + +#define STDPRIMS {{CIE_x_r,CIE_y_r},{CIE_x_g,CIE_y_g}, \ + {CIE_x_b,CIE_y_b},{CIE_x_w,CIE_y_w}} + +#define CIE_D ( CIE_x_r*(CIE_y_g - CIE_y_b) + \ + CIE_x_g*(CIE_y_b - CIE_y_r) + \ + CIE_x_b*(CIE_y_r - CIE_y_g) ) +#define CIE_C_rD ( (1./CIE_y_w) * \ + ( CIE_x_w*(CIE_y_g - CIE_y_b) - \ + CIE_y_w*(CIE_x_g - CIE_x_b) + \ + CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g ) ) +#define CIE_C_gD ( (1./CIE_y_w) * \ + ( CIE_x_w*(CIE_y_b - CIE_y_r) - \ + CIE_y_w*(CIE_x_b - CIE_x_r) - \ + CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r ) ) +#define CIE_C_bD ( (1./CIE_y_w) * \ + ( CIE_x_w*(CIE_y_r - CIE_y_g) - \ + CIE_y_w*(CIE_x_r - CIE_x_g) + \ + CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r ) ) + +#define CIE_rf (CIE_y_r*CIE_C_rD/CIE_D) +#define CIE_gf (CIE_y_g*CIE_C_gD/CIE_D) +#define CIE_bf (CIE_y_b*CIE_C_bD/CIE_D) + +/* As of 9-94, CIE_rf=.265074126, CIE_gf=.670114631 and CIE_bf=.064811243 */ + +/***** The following definitions are valid for RGB colors only... *****/ + +#define bright(col) (CIE_rf*(col)[RED]+CIE_gf*(col)[GRN]+CIE_bf*(col)[BLU]) +#define normbright(c) ( ( (long)(CIE_rf*256.+.5)*(c)[RED] + \ + (long)(CIE_gf*256.+.5)*(c)[GRN] + \ + (long)(CIE_bf*256.+.5)*(c)[BLU] ) >> 8 ) + + /* luminous efficacies over visible spectrum */ +#define MAXEFFICACY 683. /* defined maximum at 550 nm */ +#define WHTEFFICACY 179. /* uniform white light */ +#define D65EFFICACY 203. /* standard illuminant D65 */ +#define INCEFFICACY 160. /* illuminant A (incand.) */ +#define SUNEFFICACY 208. /* illuminant B (solar dir.) */ +#define SKYEFFICACY D65EFFICACY /* skylight (should be 110) */ +#define DAYEFFICACY D65EFFICACY /* combined sky and solar */ + +#define luminance(col) (WHTEFFICACY * bright(col)) + +/***** ...end of stuff specific to RGB colors *****/ + +#define intens(col) ( (col)[0] > (col)[1] \ + ? (col)[0] > (col)[2] ? (col)[0] : (col)[2] \ + : (col)[1] > (col)[2] ? (col)[1] : (col)[2] ) + +#define colrval(c,p) ( (c)[EXP] ? \ + ldexp((c)[p]+.5,(int)(c)[EXP]-(COLXS+8)) : \ + 0. ) + +#define WHTCOLOR {1.0,1.0,1.0} +#define BLKCOLOR {0.0,0.0,0.0} +#define WHTCOLR {128,128,128,COLXS+1} +#define BLKCOLR {0,0,0,0} + + /* picture format identifier */ +#define COLRFMT "32-bit_rle_rgbe" +#define CIEFMT "32-bit_rle_xyze" +#define PICFMT "32-bit_rle_???e" /* matches either */ +#define LPICFMT 15 /* max format id len */ + + /* macros for exposures */ +#define EXPOSSTR "EXPOSURE=" +#define LEXPOSSTR 9 +#define isexpos(hl) (!strncmp(hl,EXPOSSTR,LEXPOSSTR)) +#define exposval(hl) atof((hl)+LEXPOSSTR) +#define fputexpos(ex,fp) fprintf(fp,"%s%e\n",EXPOSSTR,ex) + + /* macros for pixel aspect ratios */ +#define ASPECTSTR "PIXASPECT=" +#define LASPECTSTR 10 +#define isaspect(hl) (!strncmp(hl,ASPECTSTR,LASPECTSTR)) +#define aspectval(hl) atof((hl)+LASPECTSTR) +#define fputaspect(pa,fp) fprintf(fp,"%s%f\n",ASPECTSTR,pa) + + /* macros for primary specifications */ +#define PRIMARYSTR "PRIMARIES=" +#define LPRIMARYSTR 10 +#define isprims(hl) (!strncmp(hl,PRIMARYSTR,LPRIMARYSTR)) +#define primsval(p,hl) sscanf(hl+LPRIMARYSTR, \ + "%f %f %f %f %f %f %f %f", \ + &(p)[RED][CIEX],&(p)[RED][CIEY], \ + &(p)[GRN][CIEX],&(p)[GRN][CIEY], \ + &(p)[BLU][CIEX],&(p)[BLU][CIEY], \ + &(p)[WHT][CIEX],&(p)[WHT][CIEY]) +#define fputprims(p,fp) fprintf(fp, \ + "%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n",\ + PRIMARYSTR, \ + (p)[RED][CIEX],(p)[RED][CIEY], \ + (p)[GRN][CIEX],(p)[GRN][CIEY], \ + (p)[BLU][CIEX],(p)[BLU][CIEY], \ + (p)[WHT][CIEX],(p)[WHT][CIEY]) + + /* macros for color correction */ +#define COLCORSTR "COLORCORR=" +#define LCOLCORSTR 10 +#define iscolcor(hl) (!strncmp(hl,COLCORSTR,LCOLCORSTR)) +#define colcorval(cc,hl) sscanf(hl+LCOLCORSTR,"%f %f %f", \ + &(cc)[RED],&(cc)[GRN],&(cc)[BLU]) +#define fputcolcor(cc,fp) fprintf(fp,"%s %f %f %f\n",COLCORSTR, \ + (cc)[RED],(cc)[GRN],(cc)[BLU]) + +#define CGAMUT_LOWER 01 +#define CGAMUT_UPPER 02 +#define CGAMUT (CGAMUT_LOWER|CGAMUT_UPPER) + +#define rgb_cie(xyz,rgb) colortrans(xyz,rgb2xyzmat,rgb) + +#define cpcolormat(md,ms) memcpy((void *)md,(void *)ms,sizeof(COLORMAT)) + + + + +#define MAXLINE 512 + +char HDRSTR[] = "#?"; /* information header magic number */ + +char FMTSTR[] = "FORMAT="; /* format identifier */ + +char TMSTR[] = "CAPDATE="; /* capture date identifier */ + +static gethfunc mycheck; + + + +static int +formatval( /* get format value (return true if format) */ + register char *r, + register char *s +) +{ + register char *cp = FMTSTR; + + while (*cp) if (*cp++ != *s++) return(0); + while (isspace(*s)) s++; + if (!*s) return(0); + if (r == NULL) return(1); + do + *r++ = *s++; + while(*s && !isspace(*s)); + *r = '\0'; + return(1); +} + + +static int +isformat( /* is line a format line? */ + char *s +) +{ + return(formatval(NULL, s)); +} + + + +static int +getheader( /* get header from file */ + FILE *fp, + gethfunc *f, + void *p +) +{ + char buf[MAXLINE]; + + for ( ; ; ) { + buf[MAXLINE-2] = '\n'; + if (fgets(buf, MAXLINE, fp) == NULL) + return(-1); + if (buf[0] == '\n') + return(0); +#ifdef MSDOS + if (buf[0] == '\r' && buf[1] == '\n') + return(0); +#endif + if (buf[MAXLINE-2] != '\n') { + ungetc(buf[MAXLINE-2], fp); /* prevent false end */ + buf[MAXLINE-2] = '\0'; + } + if (f != NULL && (*f)(buf, p) < 0) + return(-1); + } +} + + +struct check { + FILE *fp; + char fs[64]; +}; + + +static int +mycheck( /* check a header line for format info. */ + char *s, + void *cp +) +{ + if (!formatval(((struct check*)cp)->fs, s) + && ((struct check*)cp)->fp != NULL) { + fputs(s, ((struct check*)cp)->fp); + } + return(0); +} + + +static int +globmatch( /* check for match of s against pattern p */ + register char *p, + register char *s +) +{ + int setmatch; + + do { + switch (*p) { + case '?': /* match any character */ + if (!*s++) + return(0); + break; + case '*': /* match any string */ + while (p[1] == '*') p++; + do + if ( (p[1]=='?' || p[1]==*s) && + globmatch(p+1,s) ) + return(1); + while (*s++); + return(0); + case '[': /* character set */ + setmatch = *s == *++p; + if (!*p) + return(0); + while (*++p != ']') { + if (!*p) + return(0); + if (*p == '-') { + setmatch += p[-1] <= *s && *s <= p[1]; + if (!*++p) + break; + } else + setmatch += *p == *s; + } + if (!setmatch) + return(0); + s++; + break; + case '\\': /* literal next */ + p++; + /* fall through */ + default: /* normal character */ + if (*p != *s) + return(0); + s++; + break; + } + } while (*p++); + return(1); +} + + +/* + * Checkheader(fin,fmt,fout) returns a value of 1 if the input format + * matches the specification in fmt, 0 if no input format was found, + * and -1 if the input format does not match or there is an + * error reading the header. If fmt is empty, then -1 is returned + * if any input format is found (or there is an error), and 0 otherwise. + * If fmt contains any '*' or '?' characters, then checkheader + * does wildcard expansion and copies a matching result into fmt. + * Be sure that fmt is big enough to hold the match in such cases, + * and that it is not a static, read-only string! + * The input header (minus any format lines) is copied to fout + * if fout is not NULL. + */ + +static int +checkheader( + FILE *fin, + char *fmt, + FILE *fout +) +{ + struct check cdat; + register char *cp; + + cdat.fp = fout; + cdat.fs[0] = '\0'; + if (getheader(fin, mycheck, &cdat) < 0) + return(-1); + if (!cdat.fs[0]) + return(0); + for (cp = fmt; *cp; cp++) /* check for globbing */ + if ((*cp == '?') | (*cp == '*')) { + if (globmatch(fmt, cdat.fs)) { + strcpy(fmt, cdat.fs); + return(1); + } else + return(-1); + } + return(strcmp(fmt, cdat.fs) ? -1 : 1); /* literal match */ +} + + +static char resolu_buf[RESOLU_BUFLEN]; /* resolution line buffer */ + + +static int +str2resolu(rp, buf) /* convert resolution line to struct */ +register RESOLU *rp; +char *buf; +{ + register char *xndx, *yndx; + register char *cp; + + if (buf == NULL) + return(0); + xndx = yndx = NULL; + for (cp = buf; *cp; cp++) + if (*cp == 'X') + xndx = cp; + else if (*cp == 'Y') + yndx = cp; + if (xndx == NULL || yndx == NULL) + return(0); + rp->rt = 0; + if (xndx > yndx) rp->rt |= YMAJOR; + if (xndx[-1] == '-') rp->rt |= XDECR; + if (yndx[-1] == '-') rp->rt |= YDECR; + if ((rp->xr = atoi(xndx+1)) <= 0) + return(0); + if ((rp->yr = atoi(yndx+1)) <= 0) + return(0); + return(1); +} + + +#ifdef getc_unlocked /* avoid horrendous overhead of flockfile */ +#undef getc +#undef putc +#define getc getc_unlocked +#define putc putc_unlocked +#endif + +#define MINELEN 8 /* minimum scanline length for encoding */ +#define MAXELEN 0x7fff /* maximum scanline length for encoding */ +#define MINRUN 4 /* minimum run length */ + + + +static int +oldreadcolrs(scanline, len, fp) /* read in an old colr scanline */ +register COLR *scanline; +int len; +register FILE *fp; +{ + int rshift; + register int i; + + rshift = 0; + + while (len > 0) { + scanline[0][RED] = getc(fp); + scanline[0][GRN] = getc(fp); + scanline[0][BLU] = getc(fp); + scanline[0][EXP] = getc(fp); + if (feof(fp) || ferror(fp)) + return(-1); + if (scanline[0][RED] == 1 && + scanline[0][GRN] == 1 && + scanline[0][BLU] == 1) { + for (i = scanline[0][EXP] << rshift; i > 0; i--) { + copycolr(scanline[0], scanline[-1]); + scanline++; + len--; + } + rshift += 8; + } else { + scanline++; + len--; + rshift = 0; + } + } + return(0); +} + + +static int +freadcolrs(scanline, len, fp) /* read in an encoded colr scanline */ +register COLR *scanline; +int len; +register FILE *fp; +{ + register int i, j; + int code, val; + /* determine scanline type */ + if ((len < MINELEN) | (len > MAXELEN)) + return(oldreadcolrs(scanline, len, fp)); + if ((i = getc(fp)) == EOF) + return(-1); + if (i != 2) { + ungetc(i, fp); + return(oldreadcolrs(scanline, len, fp)); + } + scanline[0][GRN] = getc(fp); + scanline[0][BLU] = getc(fp); + if ((i = getc(fp)) == EOF) + return(-1); + if (scanline[0][GRN] != 2 || scanline[0][BLU] & 128) { + scanline[0][RED] = 2; + scanline[0][EXP] = i; + return(oldreadcolrs(scanline+1, len-1, fp)); + } + if ((scanline[0][BLU]<<8 | i) != len) + return(-1); /* length mismatch! */ + /* read each component */ + for (i = 0; i < 4; i++) + for (j = 0; j < len; ) { + if ((code = getc(fp)) == EOF) + return(-1); + if (code > 128) { /* run */ + code &= 127; + if ((val = getc(fp)) == EOF) + return -1; + if (j + code > len) + return -1; /* overrun */ + while (code--) + scanline[j++][i] = val; + } else { /* non-run */ + if (j + code > len) + return -1; /* overrun */ + while (code--) { + if ((val = getc(fp)) == EOF) + return -1; + scanline[j++][i] = val; + } + } + } + return(0); +} + +static void +fputformat( /* put out a format value */ + char *s, + FILE *fp +) +{ + fputs(FMTSTR, fp); + fputs(s, fp); + putc('\n', fp); +} + +char * +resolu2str(buf, rp) /* convert resolution struct to line */ +char *buf; +register RESOLU *rp; +{ + if (rp->rt&YMAJOR) + sprintf(buf, "%cY %d %cX %d\n", + rp->rt&YDECR ? '-' : '+', rp->yr, + rp->rt&XDECR ? '-' : '+', rp->xr); + else + sprintf(buf, "%cX %d %cY %d\n", + rp->rt&XDECR ? '-' : '+', rp->xr, + rp->rt&YDECR ? '-' : '+', rp->yr); + return(buf); +} + +static int +fwritecolrs(scanline, len, fp) /* write out a colr scanline */ +register COLR *scanline; +int len; +register FILE *fp; +{ + register int i, j, beg, cnt = 1; + int c2; + + if ((len < MINELEN) | (len > MAXELEN)) /* OOBs, write out flat */ + return(fwrite((char *)scanline,sizeof(COLR),len,fp) - len); + /* put magic header */ + putc(2, fp); + putc(2, fp); + putc(len>>8, fp); + putc(len&255, fp); + /* put components seperately */ + for (i = 0; i < 4; i++) { + for (j = 0; j < len; j += cnt) { /* find next run */ + for (beg = j; beg < len; beg += cnt) { + for (cnt = 1; cnt < 127 && beg+cnt < len && + scanline[beg+cnt][i] == scanline[beg][i]; cnt++) + ; + if (cnt >= MINRUN) + break; /* long enough */ + } + if (beg-j > 1 && beg-j < MINRUN) { + c2 = j+1; + while (scanline[c2++][i] == scanline[j][i]) + if (c2 == beg) { /* short run */ + putc(128+beg-j, fp); + putc(scanline[j][i], fp); + j = beg; + break; + } + } + while (j < beg) { /* write out non-run */ + if ((c2 = beg-j) > 128) c2 = 128; + putc(c2, fp); + while (c2--) + putc(scanline[j++][i], fp); + } + if (cnt >= MINRUN) { /* write out run */ + putc(128+cnt, fp); + putc(scanline[beg][i], fp); + } else + cnt = 0; + } + } + return(ferror(fp) ? -1 : 0); +} + + + + + +/* End copy-paste from Radiance sources. + */ + +/* What we track during radiance file read. + */ +typedef struct { + char *filename; + IMAGE *out; + + FILE *fin; + char format[256]; + double expos; + COLOR colcor; + double aspect; + RGBPRIMS prims; + RESOLU rs; + + COLR *buf; +} Read; + +static int +israd( const char *filename ) +{ + FILE *fin; + char format[256]; + int result; + +#ifdef DEBUG + printf( "israd: \"%s\"\n", filename ); +#endif /*DEBUG*/ + + if( !(fin = im__file_open_read( filename )) ) + return( 0 ); + strcpy( format, PICFMT ); + result = checkheader( fin, format, NULL ); + fclose( fin ); + + return( result == 1 ); +} + +static void +read_destroy( Read *read ) +{ + IM_FREE( read->filename ); + IM_FREEF( fclose, read->fin ); + IM_FREE( read->buf ); + + im_free( read ); +} + +static Read * +read_new( const char *filename, IMAGE *out ) +{ + Read *read; + int i; + + if( !(read = IM_NEW( NULL, Read )) ) + return( NULL ); + + read->filename = im_strdup( NULL, filename ); + read->out = out; + read->fin = NULL; + strcpy( read->format, COLRFMT ); + read->expos = 1.0; + for( i = 0; i < 3; i++ ) + read->colcor[i] = 1.0; + read->aspect = 1.0; + read->prims[0][0] = CIE_x_r; + read->prims[0][1] = CIE_y_r; + read->prims[1][0] = CIE_x_g; + read->prims[1][1] = CIE_y_g; + read->prims[2][0] = CIE_x_b; + read->prims[2][1] = CIE_y_b; + read->prims[3][0] = CIE_x_w; + read->prims[3][1] = CIE_y_w; + read->buf = NULL; + + if( !(read->fin = im__file_open_read( filename )) ) { + read_destroy( read ); + return( NULL ); + } + + return( read ); +} + +static int +rad2vips_process_line( char *line, Read *read ) +{ + if( isformat( line ) ) { + if( formatval( line, read->format ) ) + return( -1 ); + } + else if( isexpos( line ) ) { + read->expos *= exposval( line ); + } + else if( iscolcor( line ) ) { + COLOR cc; + int i; + + colcorval( cc, line ); + for( i = 0; i < 3; i++ ) + read->colcor[i] *= cc[i]; + } + else if( isaspect( line ) ) { + read->aspect *= aspectval( line ); + } + else if( isprims( line ) ) { + primsval( read->prims, line ); + } + + return( 0 ); +} + +static const char *prims_name[4][2] = { + { "rad-prims-rx", "rad-prims-ry" }, + { "rad-prims-gx", "rad-prims-gy" }, + { "rad-prims-bx", "rad-prims-by" }, + { "rad-prims-wx", "rad-prims-wy" } +}; + +static const char *colcor_name[3] = { + "rad-colcor-r", + "rad-colcor-g", + "rad-colcor-b" +}; + +static int +rad2vips_get_header( Read *read, FILE *fin, IMAGE *out ) +{ + int i, j; + + if( getheader( fin, (gethfunc *) rad2vips_process_line, read ) || + !fgetsresolu( &read->rs, fin ) ) { + im_error( "rad2vips", + "%s", _( "error reading radiance header" ) ); + return( -1 ); + } + out->Xsize = scanlen( &read->rs ); + out->Ysize = numscans( &read->rs ); + + out->Bands = 4; + out->BandFmt = IM_BANDFMT_UCHAR; + out->Bbits = im_bits_of_fmt( out->BandFmt ); + + out->Coding = IM_CODING_RAD; + out->Xres = 1.0; + out->Yres = read->aspect; + out->Xoffset = 0.0; + out->Yoffset = 0.0; + + if( im_meta_set_string( out, "rad-format", read->format ) ) + return( -1 ); + + if( strcmp( read->format, COLRFMT ) == 0 ) + out->Type = IM_TYPE_RGB; + else if( strcmp( read->format, CIEFMT ) == 0 ) + out->Type = IM_TYPE_XYZ; + else + out->Type = IM_TYPE_MULTIBAND; + + if( im_meta_set_double( out, "rad-expos", read->expos ) ) + return( -1 ); + + for( i = 0; i < 3; i++ ) + if( im_meta_set_double( out, colcor_name[i], read->colcor[i] ) ) + return( -1 ); + + if( im_meta_set_double( out, "rad-aspect", read->aspect ) ) + return( -1 ); + + for( i = 0; i < 4; i++ ) + for( j = 0; j < 2; j++ ) + if( im_meta_set_double( out, + prims_name[i][j], read->prims[i][j] ) ) + return( -1 ); + + return( 0 ); +} + +static int +rad2vips_header( const char *filename, IMAGE *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "rad2vips_header: reading \"%s\"\n", filename ); +#endif /*DEBUG*/ + + if( !(read = read_new( filename, out )) ) + return( -1 ); + if( rad2vips_get_header( read, read->fin, read->out ) ) { + read_destroy( read ); + return( -1 ); + } + read_destroy( read ); + + return( 0 ); +} + +static int +rad2vips_get_data( Read *read, FILE *fin, IMAGE *im ) +{ + int y; + +#ifdef DEBUG + printf( "rad2vips_get_data\n" ); +#endif /*DEBUG*/ + + if( im_outcheck( im ) || + im_setupout( im ) ) + return( -1 ); + if( !(read->buf = IM_ARRAY( NULL, im->Xsize, COLR )) ) + return( -1 ); + + for( y = 0; y < im->Ysize; y++ ) { + if( freadcolrs( read->buf, im->Xsize, fin ) ) { + im_error( "rad2vips", "%s", _( "read error" ) ); + return( -1 ); + } + if( im_writeline( y, im, (void *) read->buf ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +rad2vips( const char *filename, IMAGE *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "rad2vips: reading \"%s\"\n", filename ); +#endif /*DEBUG*/ + + if( !(read = read_new( filename, out )) ) + return( -1 ); + if( rad2vips_get_header( read, read->fin, read->out ) || + rad2vips_get_data( read, read->fin, read->out ) ) { + read_destroy( read ); + return( -1 ); + } + read_destroy( read ); + + return( 0 ); +} + +/* What we track during an radiance-file write. + */ +typedef struct { + IMAGE *in; + char *filename; + + im_threadgroup_t *tg; + FILE *fout; + char format[256]; + double expos; + COLOR colcor; + double aspect; + RGBPRIMS prims; + RESOLU rs; +} Write; + +static void +write_destroy( Write *write ) +{ + IM_FREEF( im_threadgroup_free, write->tg ); + IM_FREE( write->filename ); + IM_FREEF( fclose, write->fout ); + + im_free( write ); +} + +static Write * +write_new( IMAGE *in, const char *filename ) +{ + Write *write; + int i; + + if( !(write = IM_NEW( NULL, Write )) ) + return( NULL ); + + write->in = in; + write->filename = im_strdup( NULL, filename ); + write->tg = im_threadgroup_create( write->in ); + write->fout = im__file_open_write( filename ); + strcpy( write->format, COLRFMT ); + write->expos = 1.0; + for( i = 0; i < 3; i++ ) + write->colcor[i] = 1.0; + write->aspect = 1.0; + write->prims[0][0] = CIE_x_r; + write->prims[0][1] = CIE_y_r; + write->prims[1][0] = CIE_x_g; + write->prims[1][1] = CIE_y_g; + write->prims[2][0] = CIE_x_b; + write->prims[2][1] = CIE_y_b; + write->prims[3][0] = CIE_x_w; + write->prims[3][1] = CIE_y_w; + + if( !write->filename || !write->tg || !write->fout ) { + write_destroy( write ); + return( NULL ); + } + + return( write ); +} + +static int +vips2rad_put_header( Write *write ) +{ + char *str; + int i, j; + double d; + + (void) im_meta_get_double( write->in, "rad-expos", &write->expos ); + (void) im_meta_get_double( write->in, "rad-aspect", &write->aspect ); + + if( !im_meta_get_string( write->in, "rad-format", &str ) ) + im_strncpy( write->format, str, 256 ); + if( write->in->Type == IM_TYPE_RGB ) + strcpy( write->format, COLRFMT ); + if( write->in->Type == IM_TYPE_XYZ ) + strcpy( write->format, CIEFMT ); + + for( i = 0; i < 3; i++ ) + if( !im_meta_get_double( write->in, colcor_name[i], &d ) ) + write->colcor[i] = d; + for( i = 0; i < 4; i++ ) + for( j = 0; j < 2; j++ ) + if( !im_meta_get_double( write->in, + prims_name[i][j], &d ) ) + write->prims[i][j] = d; + + /* Make y decreasing for consistency with vips. + */ + write->rs.rt = YDECR | YMAJOR; + write->rs.xr = write->in->Xsize; + write->rs.yr = write->in->Ysize; + + fprintf( write->fout, "#?RADIANCE\n" ); + + fputformat( write->format, write->fout ); + fputexpos( write->expos, write->fout ); + fputcolcor( write->colcor, write->fout ); + fprintf( write->fout, "SOFTWARE=vips %s\n", im_version_string() ); + fputaspect( write->aspect, write->fout ); + fputprims( write->prims, write->fout ); + fputs( "\n", write->fout ); + fputsresolu( &write->rs, write->fout ); + + return( 0 ); +} + +static int +vips2rad_put_data_block( REGION *region, Rect *area, void *a, void *b ) +{ + Write *write = (Write *) a; + int i; + + for( i = 0; i < area->height; i++ ) { + PEL *p = (PEL *) IM_REGION_ADDR( region, 0, area->top + i ); + + if( fwritecolrs( p, area->width, write->fout ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +vips2rad_put_data( Write *write ) +{ + if( im_wbuffer( write->tg, vips2rad_put_data_block, write, NULL ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips2rad( IMAGE *in, const char *filename ) +{ + Write *write; + +#ifdef DEBUG + printf( "vips2rad: writing \"%s\"\n", filename ); +#endif /*DEBUG*/ + + if( in->BandFmt == IM_BANDFMT_FLOAT && + in->Bands == 3 && + in->Coding == IM_CODING_NONE ) { + IMAGE *t; + + if( !(t = im_open_local( in, "vips2rad", "p" )) || + im_float2rad( in, t ) ) + return( -1 ); + + in = t; + } + + if( im_pincheck( in ) ) + return( -1 ); + if( in->Coding != IM_CODING_RAD ) { + im_error( "vip2rad", "%s", _( "Radiance coding only" ) ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR || in->Bands != 4 ) { + im_error( "vip2rad", "%s", _( "4 band uchar only" ) ); + return( -1 ); + } + if( !(write = write_new( in, filename )) ) + return( -1 ); + if( vips2rad_put_header( write ) || + vips2rad_put_data( write ) ) { + write_destroy( write ); + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} + +static const char *rad_suffs[] = { ".hdr", NULL }; + +/* rad format adds no new members. + */ +typedef VipsFormat VipsFormatRad; +typedef VipsFormatClass VipsFormatRadClass; + +static void +vips_format_rad_class_init( VipsFormatRadClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; + + object_class->nickname = "rad"; + object_class->description = _( "Radiance" ); + + format_class->is_a = israd; + format_class->header = rad2vips_header; + format_class->load = rad2vips; + format_class->save = vips2rad; + format_class->suffs = rad_suffs; +} + +static void +vips_format_rad_init( VipsFormatRad *object ) +{ +} + +G_DEFINE_TYPE( VipsFormatRad, vips_format_rad, VIPS_TYPE_FORMAT ); + diff --git a/libvips/freq_filt/Makefile.am b/libvips/freq_filt/Makefile.am new file mode 100644 index 00000000..7e1755ab --- /dev/null +++ b/libvips/freq_filt/Makefile.am @@ -0,0 +1,17 @@ +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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/freq_filt/fft_sp.c b/libvips/freq_filt/fft_sp.c new file mode 100644 index 00000000..5b0fafd9 --- /dev/null +++ b/libvips/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/libvips/freq_filt/fmaskcir.c b/libvips/freq_filt/fmaskcir.c new file mode 100644 index 00000000..98b7fc27 --- /dev/null +++ b/libvips/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/libvips/freq_filt/freq_dispatch.c b/libvips/freq_filt/freq_dispatch.c new file mode 100644 index 00000000..bf6bb73c --- /dev/null +++ b/libvips/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/libvips/freq_filt/im_disp_ps.c b/libvips/freq_filt/im_disp_ps.c new file mode 100644 index 00000000..9e278622 --- /dev/null +++ b/libvips/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/libvips/freq_filt/im_fractsurf.c b/libvips/freq_filt/im_fractsurf.c new file mode 100644 index 00000000..fa85bd1d --- /dev/null +++ b/libvips/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/libvips/freq_filt/im_freq_mask.c b/libvips/freq_filt/im_freq_mask.c new file mode 100644 index 00000000..fe734bb1 --- /dev/null +++ b/libvips/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/libvips/freq_filt/im_freqflt.c b/libvips/freq_filt/im_freqflt.c new file mode 100644 index 00000000..45fe0e1e --- /dev/null +++ b/libvips/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/libvips/freq_filt/im_fwfft.c b/libvips/freq_filt/im_fwfft.c new file mode 100644 index 00000000..2f568e13 --- /dev/null +++ b/libvips/freq_filt/im_fwfft.c @@ -0,0 +1,643 @@ +/* @(#) 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", "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "one band non-complex uncoded only" ) ); + return( -1 ); + } + if( !bpx || !bpy ) { + im_error( "im_fwfft", + "%s", _( "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", + "%s", _( "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/libvips/freq_filt/im_invfft.c b/libvips/freq_filt/im_invfft.c new file mode 100644 index 00000000..4b773859 --- /dev/null +++ b/libvips/freq_filt/im_invfft.c @@ -0,0 +1,286 @@ +/* @(#) 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", "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "one band complex uncoded only" ) ); + return( -1 ); + } + if( !bpx || !bpy ) { + im_error( "im_invfft", + "%s", _( "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", + "%s", _( "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/libvips/freq_filt/im_invfftr.c b/libvips/freq_filt/im_invfftr.c new file mode 100644 index 00000000..314bbf5f --- /dev/null +++ b/libvips/freq_filt/im_invfftr.c @@ -0,0 +1,335 @@ +/* @(#) 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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "one band complex uncoded only" ) ); + return( -1 ); + } + if( !bpx || !bpy ) { + im_error( "im_invfft", + "%s", _( "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", + "%s", _( "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/libvips/freq_filt/im_rotquad.c b/libvips/freq_filt/im_rotquad.c new file mode 100644 index 00000000..d4361356 --- /dev/null +++ b/libvips/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/libvips/histograms_lut/Makefile.am b/libvips/histograms_lut/Makefile.am new file mode 100644 index 00000000..c781026e --- /dev/null +++ b/libvips/histograms_lut/Makefile.am @@ -0,0 +1,23 @@ +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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/histograms_lut/hist_dispatch.c b/libvips/histograms_lut/hist_dispatch.c new file mode 100644 index 00000000..f49044b6 --- /dev/null +++ b/libvips/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/libvips/histograms_lut/im_buildlut.c b/libvips/histograms_lut/im_buildlut.c new file mode 100644 index 00000000..547311c7 --- /dev/null +++ b/libvips/histograms_lut/im_buildlut.c @@ -0,0 +1,247 @@ +/* @(#) Build a LUT from a set of x/y points. Eg. if input is + * @(#) + * @(#) 0 0 + * @(#) 255 100 + * @(#) + * @(#) we generate + * @(#) + * @(#) 0 0 + * @(#) 1 0.4 + * @(#) .. etc. linear interpolation + * @(#) 255 100 + * @(#) + * @(#) 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 + * 18/3/09 + * - saner limit and rounding behaviour + * 30/3/09 + * - argh, fixed 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 +#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 */ + double *buf; /* Ouput buffer */ +} 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 ) +{ + int i; + + if( state->data ) + for( i = 0; i < state->input->ysize; i++ ) + IM_FREE( state->data[i] ); + + IM_FREE( state->data ); + IM_FREE( state->buf ); +} + +/* 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", + "%s", _( "x value not an int" ) ); + return( -1 ); + } + + if( v < xlow ) + xlow = v; + if( v > xhigh ) + xhigh = v; + } + state->lut_size = xhigh - xlow + 1; + + if( state->lut_size < 1 ) { + im_error( "im_buildlut", "%s", _( "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]; + + if( !(state->buf = IM_ARRAY( NULL, + state->lut_size * (input->xsize - 1), double )) ) + 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 +buildlut( State *state ) +{ + const DOUBLEMASK *input = state->input; + const int ysize = input->ysize; + const int xsize = input->xsize; + const int bands = xsize - 1; + + int b, i, x; + + /* Do each output channel separately. + */ + for( b = 0; b < bands; b++ ) { + for( i = 0; i < ysize - 1; i++ ) { + const int x1 = state->data[i][0]; + const int x2 = state->data[i + 1][0]; + const int dx = x2 - x1; + const double y1 = state->data[i][b + 1]; + const double y2 = state->data[i + 1][b + 1]; + const double dy = y2 - y1; + + for( x = 0; x < dx; x++ ) + state->buf[b + (x + x1) * bands] = + y1 + x * dy / dx; + } + + /* We are inclusive: pop the final value in by hand. + */ + state->buf[b + (int) state->data[ysize - 1][0] * bands] = + state->data[ysize - 1][b + 1]; + } + + return( 0 ); +} + +int +im_buildlut( DOUBLEMASK *input, IMAGE *output ) +{ + State state; + + if( !input || input->xsize < 2 || input->ysize < 1 ) { + im_error( "im_buildlut", "%s", _( "bad input matrix size" ) ); + return( -1 ); + } + + if( build_state( &state, input ) || + buildlut( &state ) ) { + 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 ) || + im_writeline( 0, output, (PEL *) state.buf ) ) { + free_state( &state ); + return( -1 ); + } + + free_state( &state ); + + return( 0 ); +} diff --git a/libvips/histograms_lut/im_gammacorrect.c b/libvips/histograms_lut/im_gammacorrect.c new file mode 100644 index 00000000..14e4ad7a --- /dev/null +++ b/libvips/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/libvips/histograms_lut/im_heq.c b/libvips/histograms_lut/im_heq.c new file mode 100644 index 00000000..aaca77f8 --- /dev/null +++ b/libvips/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/libvips/histograms_lut/im_hist.c b/libvips/histograms_lut/im_hist.c new file mode 100644 index 00000000..89d63d04 --- /dev/null +++ b/libvips/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/libvips/histograms_lut/im_histeq.c b/libvips/histograms_lut/im_histeq.c new file mode 100644 index 00000000..03708c95 --- /dev/null +++ b/libvips/histograms_lut/im_histeq.c @@ -0,0 +1,214 @@ +/* @(#) 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 + * 12/5/08 + * - histcum works for signed hists now 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 + + */ + +#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", "%s", _( "input coded" ) ); + return( -1 ); + } + if( px > 65536 ) { + im_error( "im_histcum", "%s", _( "input too large" ) ); + return( -1 ); + } + if( im_incheck( in ) ) + return( -1 ); + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Xsize = px; + out->Ysize = 1; + if( im_isuint( in ) ) + out->BandFmt = IM_BANDFMT_UINT; + else if( im_isint( in ) ) + out->BandFmt = IM_BANDFMT_INT; + 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, signed int ); break; + case IM_BANDFMT_UCHAR: + ACCUMULATE( unsigned char, unsigned int ); break; + case IM_BANDFMT_SHORT: + ACCUMULATE( signed short, signed int ); break; + case IM_BANDFMT_USHORT: + ACCUMULATE( unsigned short, unsigned int ); break; + case IM_BANDFMT_INT: + ACCUMULATE( signed int, signed 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/libvips/histograms_lut/im_histgr.c b/libvips/histograms_lut/im_histgr.c new file mode 100644 index 00000000..a1b1422d --- /dev/null +++ b/libvips/histograms_lut/im_histgr.c @@ -0,0 +1,384 @@ +/* @(#) 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, void *a, void *b ) +{ + Histogram *mhist = (Histogram *) a; + + return( (void *) + build_hist( out, mhist->bands, mhist->which, mhist->size ) ); +} + +/* Join a sub-hist onto the main hist. + */ +static int +merge_subhist( void *seq, void *a, void *b ) +{ + Histogram *shist = (Histogram *) seq; + Histogram *mhist = (Histogram *) a; + int i, j; + + g_assert( shist->bands == mhist->bands && shist->size == mhist->size ); + + /* 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, void *seq, void *a, void *b ) +{ + Histogram *hist = (Histogram *) seq; + 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, void *seq, void *a, void *b ) +{ + Histogram *hist = (Histogram *) seq; + 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, void *seq, void *a, void *b ) +{ + Histogram *hist = (Histogram *) seq; + 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, void *seq, void *a, void *b ) +{ + Histogram *hist = (Histogram *) seq; + 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; + im_generate_fn scanfn; + 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", "%s", _( "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", "%s", _( "input not uchar or ushort" ) ); + return( -1 ); + } + + /* How many output bands? + */ + if( bandno > in->Bands || bandno < -1 ) { + im_error( "im_histgr", "%s", _( "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/libvips/histograms_lut/im_histnD.c b/libvips/histograms_lut/im_histnD.c new file mode 100644 index 00000000..d9983d49 --- /dev/null +++ b/libvips/histograms_lut/im_histnD.c @@ -0,0 +1,272 @@ +/* @(#) 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, void *a, void *b ) +{ + Histogram *mhist = (Histogram *) a; + + return( (void *) + build_hist( mhist->in, mhist->out, mhist->bins ) ); +} + +/* Join a sub-hist onto the main hist. + */ +static int +merge_subhist( void *seq, void *a, void *b ) +{ + Histogram *shist = (Histogram *) seq; + Histogram *mhist = (Histogram *) a; + 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, void *seq, void *a, void *b ) +{ + Histogram *hist = (Histogram *) seq; + 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", "%s", _( " uncoded images only" ) ); + return( -1 ); + } + if( in->BandFmt != IM_BANDFMT_UCHAR && + in->BandFmt != IM_BANDFMT_USHORT ) { + im_error( "im_histnD", + "%s", _( " 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/libvips/histograms_lut/im_histplot.c b/libvips/histograms_lut/im_histplot.c new file mode 100644 index 00000000..d8e421f7 --- /dev/null +++ b/libvips/histograms_lut/im_histplot.c @@ -0,0 +1,342 @@ +/* @(#) 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! + * 12/5/09 + * - fix signed/unsigned warning + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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", "%s", _( "uncoded only" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_histplot", "%s", _( "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( z = 0; z < nb; z++ ) \ + q[z] = p1[z] < ((TYPE) x) ? 0 : 255; \ + \ + q += nb; \ + } \ +} + + +/* Generate function. + */ +static int +make_vert_gen( REGION *or, void *seq, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + 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, z; + + 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", + "%s", _( "internal error #8255" ) ); + return( -1 ); + } + } + + return( 0 ); +} + +#define HORZ( TYPE ) { \ + TYPE *p1 = (TYPE *) p; \ + \ + for( y = to; y < bo; y++ ) { \ + for( z = 0; z < nb; z++ ) \ + q[z] = p1[z] < ((TYPE) (ht - y)) ? 0 : 255; \ + \ + q += lsk; \ + } \ +} + + +/* Generate function. + */ +static int +make_horz_gen( REGION *or, void *seq, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + 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, z; + + 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", + "%s", _( "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_error( "im_histplot", "%s", _( "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", "%s", _( "Xsize or Ysize not 1" ) ); + return( -1 ); + } + + if( normalise( hist, norm ) || + plot( norm, histplot ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/histograms_lut/im_histspec.c b/libvips/histograms_lut/im_histspec.c new file mode 100644 index 00000000..55dce220 --- /dev/null +++ b/libvips/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/libvips/histograms_lut/im_hsp.c b/libvips/histograms_lut/im_hsp.c new file mode 100644 index 00000000..79af9c2b --- /dev/null +++ b/libvips/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/libvips/histograms_lut/im_identity.c b/libvips/histograms_lut/im_identity.c new file mode 100644 index 00000000..030781df --- /dev/null +++ b/libvips/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/libvips/histograms_lut/im_invertlut.c b/libvips/histograms_lut/im_invertlut.c new file mode 100644 index 00000000..26d6ffb6 --- /dev/null +++ b/libvips/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/libvips/histograms_lut/im_lhisteq.c b/libvips/histograms_lut/im_lhisteq.c new file mode 100644 index 00000000..94b1d443 --- /dev/null +++ b/libvips/histograms_lut/im_lhisteq.c @@ -0,0 +1,221 @@ +/* @(#) 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 + * 23/6/08 + * - check for window too small 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 + + */ + +#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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + LhistInfo *inf = (LhistInfo *) b; + 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_error( "im_lhisteq", + "%s", _( "one band uchar uncoded only" ) ); + return( -1 ); + } + if( xwin > in->Xsize || ywin > in->Ysize ) { + im_error( "im_lhisteq", "%s", _( "window too large" ) ); + return( -1 ); + } + if( xwin <= 0 || ywin <= 0 ) { + im_error( "im_lhisteq", "%s", _( "window too small" ) ); + 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/libvips/histograms_lut/im_maplut.c b/libvips/histograms_lut/im_maplut.c new file mode 100644 index 00000000..d8992da6 --- /dev/null +++ b/libvips/histograms_lut/im_maplut.c @@ -0,0 +1,637 @@ +/* @(#) 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 :-) + * 7/11/07 + * - new eval start/end system + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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; + +static int +lut_start( LutInfo *st ) +{ + st->overflow = 0; + + return( 0 ); +} + +/* Print overflows, if any. + */ +static int +lut_end( LutInfo *st ) +{ + if( st->overflow ) + im_warn( "im_maplut", _( "%d overflows detected" ), + st->overflow ); + + 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_evalstart_callback( out, + (im_callback_fn) lut_start, st, NULL ) || + im_add_evalend_callback( out, + (im_callback_fn) lut_end, 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 +maplut_stop( void *vseq, void *a, void *b ) +{ + Seq *seq = (Seq *) vseq; + LutInfo *st = (LutInfo *) b; + + /* Add to global stats. + */ + st->overflow += seq->overflow; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Our start function. + */ +static void * +maplut_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + Seq *seq; + + if( !(seq = IM_NEW( out, Seq )) ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->overflow = 0; + + if( !(seq->ir = im_region_create( in )) ) + return( NULL ); + + return( 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", "%s", _( "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", "%s", _( "bad lut file" ) ); \ + return( -1 ); \ + } + +/* Do a map. + */ +static int +maplut_gen( REGION *or, void *vseq, void *a, void *b ) +{ + Seq *seq = (Seq *) vseq; + IMAGE *in = (IMAGE *) a; + LutInfo *st = (LutInfo *) b; + 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", "%s", _( "lut is not uncoded" ) ); + return( -1 ); + } + if( lut->Xsize * lut->Ysize > 100000 ) { + im_error( "im_maplut", "%s", _( "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", "%s", _( "input is not uncoded" ) ); + return( -1 ); + } + if( !im_isuint( in ) ) { + im_error( "im_maplut", "%s", + _( "input is not some unsigned integer type" ) ); + return( -1 ); + } + if( in->Bands != 1 && lut->Bands != 1 && lut->Bands != in->Bands ) { + im_error( "im_maplut", "%s", _( "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", "%s", _( "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, maplut_start, maplut_gen, maplut_stop, in, st ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/histograms_lut/im_project.c b/libvips/histograms_lut/im_project.c new file mode 100644 index 00000000..e00daaac --- /dev/null +++ b/libvips/histograms_lut/im_project.c @@ -0,0 +1,303 @@ +/* @(#) 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, void *a, void *b ) +{ + Project *mproject = (Project *) a; + + return( 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( void *seq, void *a, void *b ) +{ + Project *sproject = (Project *) seq; + Project *mproject = (Project *) a; + 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, void *seq, void *a, void *b ) +{ + Project *project = (Project *) seq; + 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", "%s", _( "uncoded images only" ) ); + return( -1 ); + } + if( im_iscomplex( in ) ) { + im_error( "im_project", "%s", _( "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/libvips/histograms_lut/im_stdif.c b/libvips/histograms_lut/im_stdif.c new file mode 100644 index 00000000..f15e41f9 --- /dev/null +++ b/libvips/histograms_lut/im_stdif.c @@ -0,0 +1,262 @@ +/* @(#) 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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + StdifInfo *inf = (StdifInfo *) b; + 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/libvips/histograms_lut/tone.c b/libvips/histograms_lut/tone.c new file mode 100644 index 00000000..0466a5dc --- /dev/null +++ b/libvips/histograms_lut/tone.c @@ -0,0 +1,539 @@ +/* @(#) 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", + "%s", _( "bad in_max, out_max parameters" ) ); + return( -1 ); + } + if( Lb < 0 || Lb > 100 || Lw < 0 || Lw > 100 || Lb > Lw ) { + im_error( "im_tone_build", + "%s", _( "bad Lb, Lw parameters" ) ); + return( -1 ); + } + if( Ps < 0.0 || Ps > 1.0 ) { + im_error( "im_tone_build", + "%s", _( "Ps not in range [0.0,1.0]" ) ); + return( -1 ); + } + if( Pm < 0.0 || Pm > 1.0 ) { + im_error( "im_tone_build", + "%s", _( "Pm not in range [0.0,1.0]" ) ); + return( -1 ); + } + if( Ph < 0.0 || Ph > 1.0 ) { + im_error( "im_tone_build", + "%s", _( "Ph not in range [0.0,1.0]" ) ); + return( -1 ); + } + if( S < -30 || S > 30 ) { + im_error( "im_tone_build", + "%s", _( "S not in range [-30,+30]" ) ); + return( -1 ); + } + if( M < -30 || M > 30 ) { + im_error( "im_tone_build", + "%s", _( "M not in range [-30,+30]" ) ); + return( -1 ); + } + if( H < -30 || H > 30 ) { + im_error( "im_tone_build", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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/libvips/include/Makefile.am b/libvips/include/Makefile.am new file mode 100644 index 00000000..d3c74249 --- /dev/null +++ b/libvips/include/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = vips diff --git a/libvips/include/vips/Makefile.am b/libvips/include/vips/Makefile.am new file mode 100644 index 00000000..65f53301 --- /dev/null +++ b/libvips/include/vips/Makefile.am @@ -0,0 +1,33 @@ +pkginclude_HEADERS = \ + arithmetic.h \ + colour.h \ + debug.h \ + dispatch.h \ + format.h \ + fmask.h \ + mosaic.h \ + interpolate.h \ + object.h \ + almostdeprecated.h \ + proto.h \ + image.h \ + rect.h \ + region.h \ + r_access.h \ + struct.h \ + private.h \ + semaphore.h \ + transform.h \ + threadgroup.h \ + thread.h \ + util.h \ + meta.h \ + version.h \ + vips.h \ + intl.h \ + buf.h + +vipsc++.h: + vips --cpph all > vipsc++.h + +EXTRA_DIST = version.h.in internal.h diff --git a/libvips/include/vips/almostdeprecated.h b/libvips/include/vips/almostdeprecated.h new file mode 100644 index 00000000..421e6c96 --- /dev/null +++ b/libvips/include/vips/almostdeprecated.h @@ -0,0 +1,74 @@ +/* Old and broken stuff that we still enable by default + * + * 30/6/09 + * - from vips.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_ALMOSTDEPRECATED_H +#define IM_ALMOSTDEPRECATED_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Was public, now deprecated. + */ +typedef enum { + IM_BBITS_BYTE = 8, + IM_BBITS_SHORT = 16, + IM_BBITS_INT = 32, + IM_BBITS_FLOAT = 32, + IM_BBITS_COMPLEX = 64, + IM_BBITS_DOUBLE = 64, + IM_BBITS_DPCOMPLEX = 128 +} VipsBBits; + +/* Used to define a region of interest for im_extract() etc. Too boring to be + * public API, see im_extract_area() 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; + +/* Compatibility typedefs. + */ +typedef VipsDemandStyle im_demand_type; +typedef VipsProgress im_time_t; +typedef VipsImage IMAGE; + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_ALMOSTDEPRECATED_H*/ diff --git a/libvips/include/vips/arithmetic.h b/libvips/include/vips/arithmetic.h new file mode 100644 index 00000000..6c8a20d6 --- /dev/null +++ b/libvips/include/vips/arithmetic.h @@ -0,0 +1,96 @@ +/* Headers for arithmetic + * + * 30/6/09 + * - 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_ARITHMETIC_H +#define IM_ARITHMETIC_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* 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 *in1, IMAGE *in2, IMAGE *out ); +int im_subtract( IMAGE *in1, IMAGE *in2, IMAGE *out ); +int im_invert( IMAGE *in, IMAGE *out ); +int im_linreg( IMAGE **ins, IMAGE *out, double *xs ); +int im_lintra( double a, IMAGE *in, double b, IMAGE *out ); +int im_lintra_vec( int n, double *a, IMAGE *in, double *b, IMAGE *out ); +int im_multiply( IMAGE *in1, IMAGE *in2, IMAGE *out ); +int im_divide( IMAGE *in1, IMAGE *in2, IMAGE *out ); +int im_point_bilinear( IMAGE *im, double x, double y, int band, double *val ); +int im_powtra( IMAGE *in, IMAGE *out, double e ); +int im_powtra_vec( IMAGE *in, IMAGE *out, int n, double *e ); +int im_exptra( IMAGE *in, IMAGE *out ); +int im_exp10tra( IMAGE *in, IMAGE *out ); +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 ); +int im_cross_phase( IMAGE *a, IMAGE *b, IMAGE *out ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_ARITHMETIC_H*/ diff --git a/libvips/include/vips/buf.h b/libvips/include/vips/buf.h new file mode 100644 index 00000000..45bf3360 --- /dev/null +++ b/libvips/include/vips/buf.h @@ -0,0 +1,86 @@ +/* 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 VIPS_BUF_H +#define VIPS_BUF_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* A string in the process of being written to ... multiple calls to + * vips_buf_append add to it, on overflow append "..." and block further writes. + */ +typedef struct { + /* All fields are private. + */ + /* */ + 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() */ +} VipsBuf; + +#define VIPS_BUF_STATIC( TEXT ) \ + { &TEXT[0], sizeof( TEXT ), 0, FALSE, 0, FALSE } + +/* Init and append to one of the above. + */ +void vips_buf_rewind( VipsBuf *buf ); +void vips_buf_destroy( VipsBuf *buf ); +void vips_buf_init( VipsBuf *buf ); +void vips_buf_set_static( VipsBuf *buf, char *base, int mx ); +void vips_buf_set_dynamic( VipsBuf *buf, int mx ); +void vips_buf_init_static( VipsBuf *buf, char *base, int mx ); +void vips_buf_init_dynamic( VipsBuf *buf, int mx ); +gboolean vips_buf_appendns( VipsBuf *buf, const char *str, int sz ); +gboolean vips_buf_appends( VipsBuf *buf, const char *str ); +gboolean vips_buf_appendf( VipsBuf *buf, const char *fmt, ... ) + __attribute__((format(printf, 2, 3))); +gboolean vips_buf_vappendf( VipsBuf *buf, const char *fmt, va_list ap ); +gboolean vips_buf_appendc( VipsBuf *buf, char ch ); +gboolean vips_buf_appendsc( VipsBuf *buf, gboolean quote, const char *str ); +gboolean vips_buf_appendgv( VipsBuf *buf, GValue *value ); +gboolean vips_buf_removec( VipsBuf *buf, char ch ); +gboolean vips_buf_change( VipsBuf *buf, const char *old, const char * ); +gboolean vips_buf_is_empty( VipsBuf *buf ); +gboolean vips_buf_is_full( VipsBuf *buf ); +const char *vips_buf_all( VipsBuf *buf ); +const char *vips_buf_firstline( VipsBuf *buf ); +gboolean vips_buf_appendg( VipsBuf *buf, double g ); +gboolean vips_buf_appendd( VipsBuf *buf, int d ); +int vips_buf_len( VipsBuf *buf ); + +#endif /*VIPS_BUF_H*/ + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ diff --git a/libvips/include/vips/colour.h b/libvips/include/vips/colour.h new file mode 100644 index 00000000..22aa38f2 --- /dev/null +++ b/libvips/include/vips/colour.h @@ -0,0 +1,240 @@ +/* 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 + +/* A colour temperature. + */ +typedef struct { + double X0, Y0, Z0; +} im_colour_temperature; + +/* 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/libvips/include/vips/debug.h b/libvips/include/vips/debug.h new file mode 100644 index 00000000..71a9732a --- /dev/null +++ b/libvips/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/libvips/include/vips/deprecated.h b/libvips/include/vips/deprecated.h new file mode 100644 index 00000000..71e1f53e --- /dev/null +++ b/libvips/include/vips/deprecated.h @@ -0,0 +1,314 @@ +/* Old and broken stuff we do not enable by default + * + * 30/6/09 + * - from vips.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_DEPRECATED_H +#define IM_DEPRECATED_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* 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 + +/* 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) + +/* 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*/ + +#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 ) {} + +/* On Rect. + */ +#define right(R) ((R)->left + (R)->width) +#define bottom(R) ((R)->top + (R)->height) + +/* 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))); + +/* Was public, now deprecated. + */ +typedef enum { + IM_BBITS_BYTE = 8, + IM_BBITS_SHORT = 16, + IM_BBITS_INT = 32, + IM_BBITS_FLOAT = 32, + IM_BBITS_COMPLEX = 64, + IM_BBITS_DOUBLE = 64, + IM_BBITS_DPCOMPLEX = 128 +} VipsBBits; + +/* Used to define a region of interest for im_extract() etc. Too boring to be + * public API, see im_extract_area() 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; + +/* Compatibility typedefs. + */ +typedef VipsDemandType im_demand_type; + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_DEPRECATED_H*/ diff --git a/libvips/include/vips/dispatch.h b/libvips/include/vips/dispatch.h new file mode 100644 index 00000000..70468e4b --- /dev/null +++ b/libvips/include/vips/dispatch.h @@ -0,0 +1,295 @@ +/* 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 +#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 */ +#define IM_TYPE_INTERPOLATE "interpolate"/* A subclass of VipsInterpolate */ +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, destroy and write objects. The "str" argument to the + * init function will not be supplied if this is not an ARG type. The + * write function writes to the GString. + */ +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; /* Destroy object */ +} 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_int; +extern im_type_desc im__input_intvec; +extern im_type_desc im__input_imask; +extern im_type_desc im__output_int; +extern im_type_desc im__output_intvec; +extern im_type_desc im__output_imask; + +extern im_type_desc im__input_double; +extern im_type_desc im__input_doublevec; +extern im_type_desc im__input_dmask; +extern im_type_desc im__output_double; +extern im_type_desc im__output_doublevec; +extern im_type_desc im__output_dmask; +extern im_type_desc im__output_dmask_screen; + +extern im_type_desc im__output_complex; + +extern im_type_desc im__input_string; +extern im_type_desc im__output_string; + +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_display; +extern im_type_desc im__output_display; + +extern im_type_desc im__input_gvalue; +extern im_type_desc im__output_gvalue; + +extern im_type_desc im__input_interpolate; + +/* VIPS print functions. + */ +int im__iprint( im_object obj ); /* int */ +int im__ivprint( im_object obj ); /* intvec */ +int im__dprint( im_object obj ); /* double */ +int im__dvprint( im_object obj ); /* doublevec */ +int im__dmsprint( im_object obj ); /* DOUBLEMASK as stats */ +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__gprint( im_object obj ); /* GValue */ + +/* Macros for convenient creation. + */ +#define IM_INPUT_INT( S ) { S, &im__input_int, NULL } +#define IM_INPUT_INTVEC( S ) { S, &im__input_intvec, NULL } +#define IM_INPUT_IMASK( S ) { S, &im__input_imask, NULL } +#define IM_OUTPUT_INT( S ) { S, &im__output_int, im__iprint } +#define IM_OUTPUT_INTVEC( S ) { S, &im__output_intvec, im__ivprint } +#define IM_OUTPUT_IMASK( S ) { S, &im__output_imask, NULL } + +#define IM_INPUT_DOUBLE( S ) { S, &im__input_double, NULL } +#define IM_INPUT_DOUBLEVEC( S ) { S, &im__input_doublevec, NULL } +#define IM_INPUT_DMASK( S ) { S, &im__input_dmask, NULL } +#define IM_OUTPUT_DOUBLE( S ) { S, &im__output_double, im__dprint } +#define IM_OUTPUT_DOUBLEVEC( S ) { S, &im__output_doublevec, im__dvprint } +#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_OUTPUT_COMPLEX( S ) { S, &im__output_complex, im__cprint } + +#define IM_INPUT_STRING( S ) { S, &im__input_string, NULL } +#define IM_OUTPUT_STRING( S ) { S, &im__output_string, im__sprint } + +#define IM_INPUT_IMAGE( S ) { S, &im__input_image, NULL } +#define IM_INPUT_IMAGEVEC( S ) { S, &im__input_imagevec, NULL } +#define IM_OUTPUT_IMAGE( S ) { S, &im__output_image, NULL } +#define IM_RW_IMAGE( S ) { S, &im__rw_image, NULL } + +#define IM_INPUT_DISPLAY( S ) { S, &im__input_display, NULL } +#define IM_OUTPUT_DISPLAY( S ) { S, &im__output_display, im__displayprint } + +#define IM_INPUT_GVALUE( S ) { S, &im__input_gvalue, NULL } +#define IM_OUTPUT_GVALUE( S ) { S, &im__output_gvalue, im__gprint } + +#define IM_INPUT_INTERPOLATE( S ) { S, &im__input_interpolate, NULL } + +/* 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/libvips/include/vips/fmask.h b/libvips/include/vips/fmask.h new file mode 100644 index 00000000..49e1ebd5 --- /dev/null +++ b/libvips/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/libvips/include/vips/format.h b/libvips/include/vips/format.h new file mode 100644 index 00000000..0d1b375f --- /dev/null +++ b/libvips/include/vips/format.h @@ -0,0 +1,124 @@ +/* Base type for supported image formats. Subclass this to add a new + * format. + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_FORMAT_H +#define IM_FORMAT_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#define VIPS_TYPE_FORMAT (vips_format_get_type()) +#define VIPS_FORMAT( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_FORMAT, VipsFormat )) +#define VIPS_FORMAT_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_FORMAT, VipsFormatClass)) +#define VIPS_IS_FORMAT( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_FORMAT )) +#define VIPS_IS_FORMAT_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_FORMAT )) +#define VIPS_FORMAT_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_FORMAT, VipsFormatClass )) + +/* Image file properties. + */ +typedef enum { + VIPS_FORMAT_NONE = 0, /* No flags set */ + VIPS_FORMAT_PARTIAL = 1 /* Lazy read OK (eg. tiled tiff) */ +} VipsFormatFlags; + +/* Don't instantiate these things, just use the class stuff. + */ + +typedef struct _VipsFormat { + VipsObject parent_object; + +} VipsFormat; + +typedef struct _VipsFormatClass { + VipsObjectClass parent_class; + + /* Is a file in this format. + */ + gboolean (*is_a)( const char * ); + + /* Read just the header into the IMAGE. + */ + int (*header)( const char *, IMAGE * ); + + /* Load the whole image. + */ + int (*load)( const char *, IMAGE * ); + + /* Write the IMAGE to the file in this format. + */ + int (*save)( IMAGE *, const char * ); + + /* Get the flags for this file in this format. + */ + VipsFormatFlags (*get_flags)( const char * ); + + /* Loop over formats in this order, default 0. We need this because + * some formats can be read by several loaders (eg. tiff can be read + * by the libMagick loader as well as by the tiff loader), and we want + * to make sure the better loader comes first. + */ + int priority; + + /* Null-terminated list of allowed suffixes, eg. ".tif", ".tiff". + */ + const char **suffs; +} VipsFormatClass; + +GType vips_format_get_type( void ); + +/* Map over and find formats. This uses type introspection to loop over + * subclasses of VipsFormat. + */ +void *vips_format_map( VSListMap2Fn fn, void *a, void *b ); +VipsFormatClass *vips_format_for_file( const char *filename ); +VipsFormatClass *vips_format_for_name( const char *filename ); + +VipsFormatFlags vips_format_get_flags( VipsFormatClass *format, + const char *filename ); + +/* Read/write an image convenience functions. + */ +int vips_format_read( const char *name, IMAGE *out ); +int vips_format_write( IMAGE *im, const char *name ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_FORMAT_H*/ diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h new file mode 100644 index 00000000..8dc9d0ca --- /dev/null +++ b/libvips/include/vips/image.h @@ -0,0 +1,268 @@ +/* VIPS image class. + * + * 7/7/09 + * - from vips.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_IMAGE_H +#define IM_IMAGE_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Needed for 'unused' below. Remove this when we remove that. + */ +#include + +/* If you read MSB first, you get these two values. + * intel order: byte 0 = b6 + * SPARC order: byte 0 = 08 + */ +#define IM_MAGIC_INTEL (0xb6a6f208U) +#define IM_MAGIC_SPARC (0x08f2a6b6U) + +/* Demand style from im_generate(). See im_demand_hint(). + */ +typedef enum { + IM_SMALLTILE, + IM_FATSTRIP, + IM_THINSTRIP, + IM_ANY +} VipsDemandStyle; + +typedef enum { + IM_TYPE_MULTIBAND = 0, + IM_TYPE_B_W = 1, + IM_TYPE_HISTOGRAM = 10, + IM_TYPE_FOURIER = 24, + IM_TYPE_XYZ = 12, + IM_TYPE_LAB = 13, + IM_TYPE_CMYK = 15, + IM_TYPE_LABQ = 16, + IM_TYPE_RGB = 17, + IM_TYPE_UCS = 18, + IM_TYPE_LCH = 19, + IM_TYPE_LABS = 21, + IM_TYPE_sRGB = 22, + IM_TYPE_YXY = 23, + IM_TYPE_RGB16 = 25, + IM_TYPE_GREY16 = 26 +} VipsType; + +typedef enum { + IM_BANDFMT_NOTSET = -1, + IM_BANDFMT_UCHAR = 0, + IM_BANDFMT_CHAR = 1, + IM_BANDFMT_USHORT = 2, + IM_BANDFMT_SHORT = 3, + IM_BANDFMT_UINT = 4, + IM_BANDFMT_INT = 5, + IM_BANDFMT_FLOAT = 6, + IM_BANDFMT_COMPLEX = 7, + IM_BANDFMT_DOUBLE = 8, + IM_BANDFMT_DPCOMPLEX = 9 +} VipsBandFmt; + +typedef enum { + IM_CODING_NONE = 0, + IM_CODING_LABQ = 2, + IM_CODING_RAD = 6 +} VipsCoding; + +/* Struct we keep a record of execution time in. Passed to eval callback, so + * it can assess progress. + * + * The 'unused' field is there for binary compatibility, remove this when we + * break ABI. Though, at least on windows, sizeof(time_t) can vary with + * compiler flags, so we might break ABI anyway. Remove the #include + * when we remove this. + */ +typedef struct { + /*< private >*/ + struct _VipsImage *im; /* Image we are part of */ + time_t unused; /* FIXME ... for binary compatibility */ + /*< public >*/ + 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 */ + GTimer *start; /* Start time */ +} VipsProgress; + +typedef struct _VipsImage { + /*< public >*/ + /* Fields from file header. + */ + int Xsize; /* image width, in pixels */ + int Ysize; /* image height, in pixels */ + int Bands; /* number of image bands */ + /*< private >*/ + /* No longer used. + */ + int Bbits; /* was number of bits in this format */ + /*< public >*/ + int BandFmt; /* #VipsBandFmt describing the pixel format */ + int Coding; /* #VipsCoding describing the pixel coding */ + int Type; /* #VipsType hinting at pixel interpretation */ + float Xres; /* horizontal pixels per millimetre */ + float Yres; /* vertical pixels per millimetre */ + /*< private >*/ + /* No longer used. + */ + int Length; + short Compression; + short Level; + /*< public >*/ + int Xoffset; /* image origin hint */ + int Yoffset; /* image origin hint */ + + /* Derived fields that user can fiddle with. + */ + /*< private >*/ + char *Hist; /* don't use ... call im_history_get() */ + /*< public >*/ + char *filename; /* pointer to copy of filename */ + char *data; /* start of image data for WIO */ + VipsProgress *time; /* evaluation progress */ + int kill; /* set to non-zero to block partial eval */ + + /*< private >*/ + 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 */ + VipsDemandStyle 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 something 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; + + /* The VipsImage (if any) we should signal eval progress on. + */ + struct _VipsImage *progress; + + /* Some more callbacks. Appended to IMAGE for binary compatibility. + */ + GSList *evalstartfns; /* list of start eval callbacks */ + GSList *preclosefns; /* list of pre-close callbacks */ + GSList *invalidatefns; /* list of invalidate callbacks */ + + /* Record the file length here. We use this to stop ourselves mapping + * things beyond the end of the file in the case that the file has + * been truncated. + */ + size_t file_length; +} VipsImage; + +/* Pixel address calculation macros. + */ + +#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*/ + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_IMAGE_H*/ diff --git a/libvips/include/vips/inlines.h b/libvips/include/vips/inlines.h new file mode 100644 index 00000000..9d1a60e6 --- /dev/null +++ b/libvips/include/vips/inlines.h @@ -0,0 +1,70 @@ +/* Inline maths functions if they are missing from libm + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_INLINE_H +#define IM_INLINE_H + +/* glib promises to define inline in a portable way */ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + + +#ifdef HAVE_HYPOT + +#define im__hypot hypot + +#else /* HAVE_HYPOT */ + +static inline double im__hypot( double a, double b ){ + double ta= fabs( a ); + double tb= fabs( b ); + + if( ta > tb ){ + tb= b / a; + return ta * sqrt( 1.0 + tb * tb ); + } + else { + ta= a / b; + return tb * sqrt( 1.0 + ta * ta ); + } +} + +#endif /* HAVE_HYPOT */ + + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_INLINE_H*/ diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h new file mode 100644 index 00000000..627d9f10 --- /dev/null +++ b/libvips/include/vips/internal.h @@ -0,0 +1,178 @@ +/* Declarations only used internally to vips. See private.h for declarations + * which are not public, but which have to be publically visible. + * + * 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*/ + +/* 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. + */ +typedef struct _Meta { + IMAGE *im; + + char *field; /* strdup() of field name */ + GValue value; /* copy of value */ +} Meta; + +void im__meta_init_types( void ); +void im__meta_destroy( IMAGE *im ); +int im__meta_cp( IMAGE *, const IMAGE * ); + +/* 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; + +/* Give progress feedback. + */ +extern int im__progress; + +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__ftruncate( int fd, gint64 pos ); +int im__seek( int fd, gint64 pos ); +int im__get_bytes( const char *filename, unsigned char buf[], int len ); +gint64 im__image_pixel_length( IMAGE *im ); + +int im__open_image_file( const char * ); +void im__format_init( void ); +void im__type_init( void ); +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__write_extension_block( IMAGE *im, void *buf, int size ); +int im__writehist( IMAGE *image ); +int im__start_eval( IMAGE *im ); +int im__handle_eval( IMAGE *im, int w, int h ); +int im__end_eval( IMAGE *im ); +int im__time_destroy( IMAGE *im ); + +void im__tiff_register( void ); +void im__jpeg_register( void ); +void im__png_register( void ); +void im__csv_register( void ); +void im__ppm_register( void ); +void im__analyze_register( void ); +void im__exr_register( void ); +void im__magick_register( void ); + +extern int im__read_test; +extern int im__mmap_limit; +extern GMutex *im__global_lock; + +typedef enum { + IM__RGB, /* 1 or 3 bands (like PPM) */ + IM__RGBA, /* 1, 2, 3 or 4 bands (like PNG) */ + IM__RGB_CMYK /* 1, 3 or 4 bands (like JPEG) */ +} im__saveable_t; + +IMAGE *im__convert_saveable( IMAGE *in, im__saveable_t saveable ); + +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__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 ); +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 ); +void im__black_region( REGION *reg ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_INTERNAL_H*/ diff --git a/libvips/include/vips/interpolate.h b/libvips/include/vips/interpolate.h new file mode 100644 index 00000000..99df93d6 --- /dev/null +++ b/libvips/include/vips/interpolate.h @@ -0,0 +1,123 @@ +/* Various interpolators. + * + * J.Cupitt, 15/10/08 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 VIPS_INTERPOLATE_H +#define VIPS_INTERPOLATE_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#define VIPS_TYPE_INTERPOLATE (vips_interpolate_get_type()) +#define VIPS_INTERPOLATE( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE, VipsInterpolate )) +#define VIPS_INTERPOLATE_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE, VipsInterpolateClass)) +#define VIPS_IS_INTERPOLATE( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE )) +#define VIPS_IS_INTERPOLATE_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE )) +#define VIPS_INTERPOLATE_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE, VipsInterpolateClass )) + +typedef struct _VipsInterpolate { + VipsObject parent_object; + +} VipsInterpolate; + +/* An interpolation function. This is a class method, but we have a lookup + * function for it to speed up dispatch. Write to the memory at "out", + * interpolate the value at position (x, y) in "in". + */ +typedef void (*VipsInterpolateMethod)( VipsInterpolate *, + PEL *out, REGION *in, double x, double y ); + +typedef struct _VipsInterpolateClass { + VipsObjectClass parent_class; + + /* Write to pixel out(x,y), interpolating from in(x,y). The caller has + * to set the regions up. + */ + VipsInterpolateMethod interpolate; + + /* This interpolator needs a window this many pixels across and down. + */ + int (*get_window_size)( VipsInterpolate * ); + + /* Or just set this if you want a constant. + */ + int window_size; +} VipsInterpolateClass; + +GType vips_interpolate_get_type( void ); +void vips_interpolate( VipsInterpolate *interpolate, + PEL *out, REGION *in, double x, double y ); +VipsInterpolateMethod vips_interpolate_get_method( VipsInterpolate * ); +int vips_interpolate_get_window_size( VipsInterpolate *interpolate ); + +/* How many bits of precision we keep for transformations, ie. how many + * pre-computed matricies we have. + */ +#define VIPS_TRANSFORM_SHIFT (5) +#define VIPS_TRANSFORM_SCALE (1 << VIPS_TRANSFORM_SHIFT) + +/* How many bits of precision we keep for interpolation, ie. where the decimal + * is in the fixed-point tables. For 16-bit pixels, we need 16 bits for the + * data and 4 bits to add 16 values together. That leaves 12 bits for the + * fractional part. + */ +#define VIPS_INTERPOLATE_SHIFT (12) +#define VIPS_INTERPOLATE_SCALE (1 << VIPS_INTERPOLATE_SHIFT) + +/* Convenience: return static interpolators, no need to unref. + */ +VipsInterpolate *vips_interpolate_nearest_static( void ); +VipsInterpolate *vips_interpolate_bilinear_static( void ); +VipsInterpolate *vips_interpolate_bicubic_static( void ); + +/* Convenience: make an interpolator from a nickname. g_object_unref() when + * you're done with it. + */ +VipsInterpolate *vips_interpolate_new( const char *nickname ); + +/* Register base vips types, called during startup. + */ +void vips__interpolate_init( void ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*VIPS_INTERPOLATE_H*/ + diff --git a/libvips/include/vips/intl.h b/libvips/include/vips/intl.h new file mode 100644 index 00000000..22877aea --- /dev/null +++ b/libvips/include/vips/intl.h @@ -0,0 +1,50 @@ +/* i18n stuff for vips. + */ + +#ifndef IM_VIPS_INTL_H +#define IM_VIPS_INTL_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +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 */ + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /* IM_VIPS_INTL_H */ diff --git a/libvips/include/vips/meta.h b/libvips/include/vips/meta.h new file mode 100644 index 00000000..94287152 --- /dev/null +++ b/libvips/include/vips/meta.h @@ -0,0 +1,89 @@ +/* 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" +#define IM_META_RESOLUTION_UNIT "resolution-unit" + +/* 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 ); + +int im_meta_set( IMAGE *, const char *field, GValue * ); +int im_meta_get( IMAGE *, const char *field, GValue * ); +GType im_meta_get_typeof( 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 ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*!IM_META_H*/ diff --git a/libvips/include/vips/mosaic.h b/libvips/include/vips/mosaic.h new file mode 100644 index 00000000..dc2d26a8 --- /dev/null +++ b/libvips/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/libvips/include/vips/object.h b/libvips/include/vips/object.h new file mode 100644 index 00000000..60229209 --- /dev/null +++ b/libvips/include/vips/object.h @@ -0,0 +1,250 @@ +/* abstract base class for all vips objects + */ + +/* + + 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 + + */ + +#ifndef VIPS_OBJECT_H +#define VIPS_OBJECT_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +typedef struct _VipsObject VipsObject; +typedef struct _VipsObjectClass VipsObjectClass; + +/* Track extra stuff for arguments to objects + */ + +/* Flags we associate with each argument. + */ +typedef enum _VipsArgumentFlags { + VIPS_ARGUMENT_NONE = 0, + + /* Must be set in the constructor. + */ + VIPS_ARGUMENT_REQUIRED = 1, + + /* Can only be set in the constructor. + */ + VIPS_ARGUMENT_CONSTRUCT = 2, + + /* Can only be set once. + */ + VIPS_ARGUMENT_SET_ONCE = 4, + + /* Have input & output flags. Both set is an error; neither set is OK. + */ + + /* Is an input argument (one we depend on) ... if it's a gobject, we + * should ref it. In our _dispose(), we should unref it. + */ + VIPS_ARGUMENT_INPUT = 8, + + /* Is an output argument (one that depends on us) ... if it's a + * gobject, we should ref ourselves. We watch "destroy" on the + * argument: if it goes, we unref ourselves. If we dispose, we + * disconnect the signal. + */ + VIPS_ARGUMENT_OUTPUT = 16 +} VipsArgumentFlags; + +/* Useful flag combinations. User-visible ones are: + +VIPS_ARGUMENT_REQURED_INPUT Eg. the "left" argument for an add operation + +VIPS_ARGUMENT_OPTIONAL_INPUT Eg. the "caption" for an object + +VIPS_ARGUMENT_REQURED_OUTPUT Eg. the "result" of an add operation + +VIPS_ARGUMENT_OPTIONAL_OUTPUT Eg. the "width" of an image + + Other combinations are used internally, eg. supplying the cast-table for an + arithmetic operation + + */ + +#define VIPS_ARGUMENT_REQUIRED_INPUT \ + (VIPS_ARGUMENT_INPUT | VIPS_ARGUMENT_REQUIRED | \ + VIPS_ARGUMENT_CONSTRUCT | VIPS_ARGUMENT_SET_ONCE) + +#define VIPS_ARGUMENT_OPTIONAL_INPUT \ + (VIPS_ARGUMENT_INPUT | \ + VIPS_ARGUMENT_CONSTRUCT | VIPS_ARGUMENT_SET_ONCE) + +#define VIPS_ARGUMENT_REQUIRED_OUTPUT \ + (VIPS_ARGUMENT_OUTPUT | VIPS_ARGUMENT_REQUIRED | \ + VIPS_ARGUMENT_SET_ONCE) + +#define VIPS_ARGUMENT_OPTIONAL_OUTPUT \ + (VIPS_ARGUMENT_OUTPUT | \ + VIPS_ARGUMENT_SET_ONCE) + +/* Keep one of these for every argument. + */ +typedef struct _VipsArgument { + GParamSpec *pspec; /* pspec for this argument */ + + /* More stuff, see below */ +} VipsArgument; + +/* Keep one of these in the class struct for every argument. + */ +typedef struct _VipsArgumentClass { + VipsArgument parent; + + /* The class of the object we are an arg for. + */ + VipsObjectClass *object_class; + + VipsArgumentFlags flags; + guint offset; /* G_STRUCT_OFFSET of member in object */ +} VipsArgumentClass; + +/* Keep one of these in the object struct for every argument instance. + */ +typedef struct _VipsArgumentInstance { + VipsArgument parent; + + /* The object we are attached to. + */ + VipsObject *object; + + /* Has been set. + */ + gboolean assigned; + + /* If this is an output argument, keep the id of our "destroy" handler + * here. + */ + gulong destroy_id; +} VipsArgumentInstance; + +/* Need to look up our VipsArgument structs from a pspec. Just hash the + * pointer (ie. we assume pspecs are never shared, is this correct?) + */ +typedef GHashTable VipsArgumentTable; + +VipsArgumentInstance *vips__argument_get_instance( VipsArgumentClass *, + VipsObject *); +VipsArgument *vips__argument_table_lookup( VipsArgumentTable *, + GParamSpec *); +typedef void *(*VipsArgumentMapFn)( VipsObject *, GParamSpec *, + VipsArgumentClass *, VipsArgumentInstance *, void *a, void *b ); +void *vips_argument_map( VipsObject *object, + VipsArgumentMapFn fn, void *a, void *b ); + +#define VIPS_TYPE_OBJECT (vips_object_get_type()) +#define VIPS_OBJECT( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), VIPS_TYPE_OBJECT, VipsObject )) +#define VIPS_OBJECT_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), VIPS_TYPE_OBJECT, VipsObjectClass)) +#define VIPS_IS_OBJECT( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_OBJECT )) +#define VIPS_IS_OBJECT_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_OBJECT )) +#define VIPS_OBJECT_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), VIPS_TYPE_OBJECT, VipsObjectClass )) + +struct _VipsObject { + GObject parent_object; + + gboolean constructed; /* Construct done and checked */ + + /* Table of argument instances for this class and any derived classes. + */ + VipsArgumentTable *argument_table; + + /* Class properties (see below), duplicated in the instance so we can + * get at them easily via the property system. + */ + char *nickname; + char *description; +}; + +struct _VipsObjectClass { + GObjectClass parent_class; + + /* Build the object ... all argument properties have been set, + * now build the thing. + */ + int (*build)( VipsObject *object ); + + /* Try to print something about the class, handy for help displays. + */ + void (*print_class)( struct _VipsObjectClass *, VipsBuf * ); + + /* Try to print something about the object, handy for debugging. + */ + void (*print)( VipsObject *, VipsBuf * ); + + /* Class nickname, eg. "VipsInterpolateBicubic" has "bicubic" as a + * nickname. Not internationalised. + */ + const char *nickname; + + /* Class description. Used for help messages, so internationalised. + */ + const char *description; + + /* Table of arguments for this class and any derived classes. Order + * is important, so keep a traverse list too. We can't rely on the + * ordering given by g_object_class_list_properties() since it comes + * from a hash :-( + */ + VipsArgumentTable *argument_table; + GSList *argument_table_traverse; +}; + +void vips_object_set_property( GObject *gobject, + guint property_id, const GValue *value, GParamSpec *pspec ); +void vips_object_get_property( GObject *gobject, + guint property_id, GValue *value, GParamSpec *pspec ); + +int vips_object_build( VipsObject *object ); +void vips_object_print_class( VipsObjectClass *klass ); +void vips_object_print( VipsObject *object ); + +GType vips_object_get_type( void ); + +void vips_object_class_install_argument( VipsObjectClass *, + GParamSpec *pspec, VipsArgumentFlags flags, guint offset ); + +typedef void *(*VipsObjectSetArguments)( VipsObject *, void *, void * ); +VipsObject *vips_object_new( GType type, + VipsObjectSetArguments set, void *a, void *b ); + +VipsObject *vips_object_new_from_string( const char *base, const char *str ); +void vips_object_to_string( VipsObject *object, VipsBuf *buf ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*VIPS_OBJECT_H*/ + + diff --git a/libvips/include/vips/private.h b/libvips/include/vips/private.h new file mode 100644 index 00000000..8ce1afd5 --- /dev/null +++ b/libvips/include/vips/private.h @@ -0,0 +1,89 @@ +/* Declarations which are public-facing, but private. See internal.h for + * declarations which are only used internally by vips and which are not + * externally visible. + * + * 6/7/09 + * - from vips.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_PRIVATE_H +#define IM_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#define IM_SPARE (8) + +/* 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 */ + +/* 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; + +/* 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 _VipsImage *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; + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_PRIVATE_H*/ diff --git a/libvips/include/vips/proto.h b/libvips/include/vips/proto.h new file mode 100644 index 00000000..f9bba8bd --- /dev/null +++ b/libvips/include/vips/proto.h @@ -0,0 +1,712 @@ +/* @(#) 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 + +/* If we're being parsed by SWIG, remove gcc attributes. + */ +#ifdef SWIG +# ifndef __attribute__ +# define __attribute__(x) /*NOTHING*/ +# endif +#endif /*SWIG*/ + +/* iofuncs + */ +int im_init_world( const char *argv0 ); +GOptionGroup *im_get_option_group( void ); + +/* Turn progress feedback on and off. + */ +void im_progress_set( int progress ); + +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_typeof( IMAGE *im, const char *field ); +int im_header_get( IMAGE *im, const char *field, GValue *value_copy ); +typedef void *(*im_header_map_fn)( IMAGE *, const char *, GValue *, void * ); +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 * ); +const char *im_guess_libdir( const char *, const char * ); +IMAGE *im_init( const char * ); +IMAGE *im_openout( const char * ); +IMAGE *im_open_vips( const char * ); +int im_openin( IMAGE *image ); +int im_openinrw( IMAGE *image ); +IMAGE *im_vips_open( const char * ); +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_isvips( const char * ); + +int im_add_close_callback( IMAGE *, im_callback_fn, void *, void * ); +int im_add_preclose_callback( IMAGE *, im_callback_fn, void *, void * ); +int im_add_evalstart_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 * ); +int im_add_invalidate_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 ); +INTMASK *im_gauss_imask_sep( 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_phasecor_fft( IMAGE *in1, IMAGE *in2, 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_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 * ); + +/* 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 ); + +/* cimg + */ +int im_greyc_mask( IMAGE *in, IMAGE *out, IMAGE *mask, + int iterations, float amplitude, float sharpness, float anisotropy, + float alpha, float sigma, float dl, float da, float gauss_prec, + int interpolation, int fast_approx ); + +/* 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; + +gboolean im_isnative( im_arch_type arch ); + +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_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_tile_cache( IMAGE *, IMAGE *, int, int, int ); +int im_magick2vips( const char *, IMAGE * ); +int im_png2vips( const char *, IMAGE * ); +int im_exr2vips( const char *, IMAGE * ); +int im_ppm2vips( const char *, IMAGE * ); +int im_vips2ppm( IMAGE *, const char * ); +int im_analyze2vips( const char *filename, IMAGE *out ); +int im_vips2csv( IMAGE *in, const char *filename ); +int im_csv2vips( 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 ); +int im_wrap( IMAGE *in, IMAGE *out, int x, int y ); +int im_vips2raw( IMAGE *in, int fd ); + +/* colour + */ +int im_Lab2LCh( IMAGE *, IMAGE * ); +int im_LCh2Lab( IMAGE *, IMAGE * ); +int im_LabQ2XYZ( IMAGE *, IMAGE * ); +int im_rad2float( IMAGE *, IMAGE * ); +int im_float2rad( 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_affinei( IMAGE *in, IMAGE *out, + VipsInterpolate *interpolate, + double a, double b, double c, double d, double dx, double dy, + int ox, int oy, int ow, int oh ); +int im_affinei_all( IMAGE *in, IMAGE *out, VipsInterpolate *interpolate, + double a, double b, double c, double d, double dx, double dy ) ; +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 ); + +/* Old stuff, for compat. + */ +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_align_bands( IMAGE *in, IMAGE *out ); +int im_maxpos_subpel( IMAGE *in, double *x, double *y ); + +/* 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/libvips/include/vips/r_access.h b/libvips/include/vips/r_access.h new file mode 100644 index 00000000..aa48bb4d --- /dev/null +++ b/libvips/include/vips/r_access.h @@ -0,0 +1,88 @@ +/* r_access.h + * + * 2006-09-21 tcv + * random access to images and regions + * + * 12/5/09 + * - add casts to IM__VALUE_FROM_ARRAY() to remove confusion about the type of the result + */ + +#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) ) ? (double) IM__DOUBLE_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_FLOAT == (band_fmt) ) ? (double) IM__FLOAT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_INT == (band_fmt) ) ? (double) IM__INT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_UINT == (band_fmt) ) ? (double) IM__UINT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_SHORT == (band_fmt) ) ? (double) IM__SHORT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_USHORT == (band_fmt) ) ? (double) IM__USHORT_FROM_ARRAY( (vptr), (i) ) \ + : ( IM_BANDFMT_CHAR == (band_fmt) ) ? (double) IM__CHAR_FROM_ARRAY( (vptr), (i) ) \ + : (double) 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/libvips/include/vips/rect.h b/libvips/include/vips/rect.h new file mode 100644 index 00000000..7ea5e48a --- /dev/null +++ b/libvips/include/vips/rect.h @@ -0,0 +1,64 @@ +/* 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; + +#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/libvips/include/vips/region.h b/libvips/include/vips/region.h new file mode 100644 index 00000000..6c4cfb1f --- /dev/null +++ b/libvips/include/vips/region.h @@ -0,0 +1,259 @@ +/* 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*/ + +/* Profiling madness only, who cares about portability. + */ +#ifdef TIME_THREAD +#include +#endif /*TIME_THREAD*/ + +/* 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 im__buffer_t { + 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 ); + +void im_region_print( REGION *region ); + +/* IMAGE functions which use regions. + */ +typedef void *(*im_start_fn)( IMAGE *, void *, void * ); +typedef int (*im_generate_fn)( REGION *, void *, void *, void *); +typedef int (*im_stop_fn)( void *, void *, void * ); +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, + im_start_fn start, im_generate_fn generate, im_stop_fn stop, + void *a, void *b +); +int im_iterate( IMAGE *im, + im_start_fn start, im_generate_fn generate, im_stop_fn stop, + 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, void *in, void *dummy ); +int im_stop_one( void *seq, void *dummy1, void *dummy2 ); +void *im_start_many( IMAGE *out, void *in, void *dummy ); +int im_stop_many( void *seq, 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_wrapone_fn)( void *in, void *out, int width, + void *a, void *b ); +typedef void (*im_wraptwo_fn)( void *in1, void *in2, void *out, + int width, void *a, void *b ); +typedef void (*im_wrapmany_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 ); +int im_wraptwo( IMAGE *in1, IMAGE *in2, IMAGE *out, + im_wraptwo_fn fn, void *a, void *b ); +int im_wrapmany( IMAGE **in, IMAGE *out, + im_wrapmany_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 ); + +/* 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/libvips/include/vips/semaphore.h b/libvips/include/vips/semaphore.h new file mode 100644 index 00000000..112cd8dd --- /dev/null +++ b/libvips/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/libvips/include/vips/struct.h b/libvips/include/vips/struct.h new file mode 100644 index 00000000..99a8bdf9 --- /dev/null +++ b/libvips/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/libvips/include/vips/thread.h b/libvips/include/vips/thread.h new file mode 100644 index 00000000..4d1b78f7 --- /dev/null +++ b/libvips/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/libvips/include/vips/threadgroup.h b/libvips/include/vips/threadgroup.h new file mode 100644 index 00000000..8912e04a --- /dev/null +++ b/libvips/include/vips/threadgroup.h @@ -0,0 +1,131 @@ +/* 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) + +/* 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 */ + + int progress; /* Set this to get eval progress feedback */ +} 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 ); + +/* Threaded, double-buffered eval to file. + */ +typedef int (*im_wbuffer_fn)( REGION *region, Rect *area, void *a, void *b ); +int im_wbuffer( im_threadgroup_t *tg, + im_wbuffer_fn write_fn, void *a, void *b ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_THREADGROUP_H*/ diff --git a/libvips/include/vips/transform.h b/libvips/include/vips/transform.h new file mode 100644 index 00000000..884e04cd --- /dev/null +++ b/libvips/include/vips/transform.h @@ -0,0 +1,69 @@ +/* Affine transforms. + */ + +/* + + 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 + + */ + +/* Params for an affine transformation. + */ +typedef struct { + /* Area of input we can use. This can be smaller than the real input + * image: we expand the input to add extra pixels for interpolation. + */ + Rect iarea; + + /* The area of the output we've been asked to generate. left/top can + * be negative. + */ + Rect oarea; + + /* The transform. + */ + double a, b, c, d; + double dx, dy; + + double ia, ib, ic, id; /* Inverse of matrix abcd */ +} Transformation; + +void im__transform_init( Transformation *trn ); +int im__transform_calc_inverse( Transformation *trn ); +int im__transform_isidentity( const Transformation *trn ); +int im__transform_add( const Transformation *in1, const Transformation *in2, + Transformation *out ); +void im__transform_print( const Transformation *trn ); + +void im__transform_forward_point( const Transformation *trn, + const double x, const double y, double *ox, double *oy ); +void im__transform_invert_point( const Transformation *trn, + const double x, const double y, double *ox, double *oy ); +void im__transform_forward_rect( const Transformation *trn, + const Rect *in, Rect *out ); +void im__transform_invert_rect( const Transformation *trn, + const Rect *in, Rect *out ); + +void im__transform_set_area( Transformation * ); + +int im__affine( IMAGE *in, IMAGE *out, Transformation *trn ); diff --git a/libvips/include/vips/type.h b/libvips/include/vips/type.h new file mode 100644 index 00000000..612302af --- /dev/null +++ b/libvips/include/vips/type.h @@ -0,0 +1,148 @@ +/* VIPS argument 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 + + */ + +#ifndef IM_TYPE_H +#define IM_TYPE_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/* Type names. Old code might use "doublevec" etc. from before we had the + * "array" type. + */ +#define IM_TYPE_NAME_DOUBLE "double" /* im_value_t is ptr to double */ +#define IM_TYPE_NAME_INT "integer" /* 32-bit integer */ +#define IM_TYPE_NAME_COMPLEX "complex" /* Pair of doubles */ +#define IM_TYPE_NAME_STRING "string" /* Zero-terminated char array */ +#define IM_TYPE_NAME_IMASK "intmask" /* Integer mask type */ +#define IM_TYPE_NAME_DMASK "doublemask" /* Double mask type */ +#define IM_TYPE_NAME_IMAGE "image" /* IMAGE descriptor */ +#define IM_TYPE_NAME_DISPLAY "display" /* Display descriptor */ +#define IM_TYPE_NAME_GVALUE "gvalue" /* GValue wrapper */ +#define IM_TYPE_NAME_ARRAY "array" /* Array of other values of some type */ + +/* Handy type lookups. + */ +#define IM_TYPE_IM (im_type_lookup( IM_TYPE_NAME_IMAGE, NULL )) +#define IM_TYPE_AR( OF ) (im_type_lookup( IM_TYPE_NAME_ARRAY, OF )) + +/* A VIPS type. + */ +typedef struct im__type_t { + const char *name; /* Name of type, eg. "double" */ + struct im__type_t *type_param; /* What this is an array of */ + size_t size; /* sizeof( im_value_t ) repres. ) */ +} im_type_t; + +/* Pass (array of pointers to im_value_t) to operations as the argument list. + * Cast to the appropriate type for this argument, eg. (int *) or (IMAGE *). + */ +typedef void im_value_t; + +/* Various im_value_t values. + */ +typedef struct { + char *name; /* Command-line name in */ + void *mask; /* Mask --- DOUBLE or INT */ +} im_value_mask_t; + +typedef struct { + int n; /* Array length */ + im_value_t **array; /* Array */ +} im_value_array_t; + +/* Flags for arguments. + * operation, + */ +typedef enum { + IM_ARGUMENT_NONE = 0, /* No flags set */ + IM_ARGUMENT_OUTPUT = 0x1 /* Is an output arg */ +} im_argument_flags; + +/* An argument to a VIPS operation. + */ +typedef struct im__argument_t { + const char *name; /* Eg. "in2" */ + im_type_t *type; /* Argument type */ + im_argument_flags flags; /* Output/input etc. */ +} im_argument_t; + +/* Flags for operations. Various hints for UIs about the behaviour of the + * operation, + */ +typedef enum { + IM_OPERATION_NONE = 0, /* No flags set */ + IM_OPERATION_PIO = 0x1, /* Is a partial function */ + IM_OPERATION_TRANSFORM = 0x2, /* Performs coord transformations */ + IM_OPERATION_PTOP = 0x4, /* Point-to-point ... can be LUTted */ + IM_OPERATION_NOCACHE = 0x8 /* Result should not be cached */ +} im_operation_flags; + +/* Type of a VIPS dispatch funtion. + */ +typedef int (*im_operation_dispatch_fn)( im_value_t **argv ); + +/* A VIPS operation. + */ +typedef struct im__operation_t { + const char *name; /* eg "im_invert" */ + const char *desc; /* One line description */ + im_operation_flags flags; /* Flags for this function */ + im_operation_dispatch_fn disp; /* Dispatch */ + int argc; /* Number of args */ + im_argument_t **argv; /* Arg list */ +} im_operation_t; + +/* Register/iterate over types. + */ +im_type_t *im_type_register( const char *name, + im_type_t *type_param, size_t size ); +void *im_type_map( VSListMap2Fn fn, void *a, void *b ); +im_type_t *im_type_lookup( const char *name, im_type_t *type_param ); + +/* Register/iterate/lookup operations. + */ +im_operation_t *im_operation_register( const char *name, const char *desc, + im_operation_flags flags, im_operation_dispatch_fn disp, int argc ); +im_operation_t *im_operation_registerv( const char *name, const char *desc, + im_operation_flags flags, im_operation_dispatch_fn disp, ... ); +void *im_operation_map( VSListMap2Fn fn, void *a, void *b ); +im_operation_t *im_operation_lookup( const char *name ); + +/* Create arguments. + */ +im_argument_t *im_argument_new( const char *name, + im_type_t *type, im_argument_flags flags ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_TYPE_H*/ diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h new file mode 100644 index 00000000..b54da355 --- /dev/null +++ b/libvips/include/vips/util.h @@ -0,0 +1,270 @@ +/* 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*/ + +#include + +/* Some platforms don't have M_PI in math.h :-( + */ +#define IM_PI (3.14159265358979323846) + +#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 ) + +/* Can't just use IM_FREEF(), we want the extra cast to void on the argument + * to im_free() to make sure we can work for "const char *" variables. + */ +#define IM_FREE( S ) do { \ + if( S ) { \ + (void) im_free( (void *) (S) ); \ + (S) = 0; \ + } \ +} while( 0 ) + +#define IM_SETSTR( S, V ) do { \ + const char *sst = (V); \ + \ + if( (S) != sst ) { \ + if( !(S) || !sst || strcmp( (S), sst ) != 0 ) { \ + 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 )) + +/* Basic function types. + */ +typedef int (*im_callback_fn)( void *, void * ); +typedef void *(*im_construct_fn)( void *, void *, void * ); + +/* 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 ); + +void *im_hash_table_map( GHashTable *hash, VSListMap2Fn fn, void *a, void *b ); + +typedef void *(*VipsTypeMap)( GType, void * ); +typedef void *(*VipsTypeMap2)( GType, void *, void * ); +typedef void *(*VipsClassMap)( VipsObjectClass *, void * ); +void *vips_type_map( GType base, VipsTypeMap2 fn, void *a, void *b ); +void *vips_type_map_concrete_all( GType base, VipsTypeMap fn, void *a ); +void *vips_class_map_concrete_all( GType base, VipsClassMap fn, void *a ); +VipsObjectClass *vips_class_find( const char *basename, const char *nickname ); +GType vips_type_find( const char *basename, const char *nickname ); + +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 ); +int im__write( int fd, const void *buf, size_t count ); + +FILE *im__file_open_read( const char *filename ); +FILE *im__file_open_write( const char *filename ); +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 ); +int im__file_write( void *data, size_t size, size_t nmemb, FILE *stream ); + +typedef enum { + VIPS_TOKEN_LEFT = 1, /* ({[ */ + VIPS_TOKEN_RIGHT, /* ]}) */ + VIPS_TOKEN_STRING, /* string or "str\"ing" */ + VIPS_TOKEN_EQUALS, /* = */ + VIPS_TOKEN_COMMA /* , */ +} VipsToken; + +const char *vips__token_get( const char *buffer, + VipsToken *token, char *string, int size ); +const char *vips__token_must( const char *buffer, VipsToken *token, + char *string, int size ); +const char *vips__token_need( const char *buffer, VipsToken need_token, + char *string, int size ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_UTIL_H*/ diff --git a/libvips/include/vips/version.h.in b/libvips/include/vips/version.h.in new file mode 100644 index 00000000..2529a6da --- /dev/null +++ b/libvips/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/libvips/include/vips/vips.h b/libvips/include/vips/vips.h new file mode 100644 index 00000000..c6379558 --- /dev/null +++ b/libvips/include/vips/vips.h @@ -0,0 +1,149 @@ +/* @(#) 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 + * 7/11/07 + * - added preclose and evalstart callbacks + * - brought time struct in here + * 7/3/08 + * - MAGIC values should be unsigned + * 2/7/08 + * - added invalidate callbacks + * 7/8/08 + * - include , thanks nicola + * 30/6/09 + * - move deprecated stuff to its own 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 + + */ + +#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 +#include + +#include +#include + +#include + +/* @(#) 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 ; + +#include +#include +#include +#include +/* #include */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef IM_ENABLE_DEPRECATED +#include +#endif /*IM_ENABLE_DEPRECATED*/ + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_VIPS_H*/ diff --git a/libvips/inplace/Makefile.am b/libvips/inplace/Makefile.am new file mode 100644 index 00000000..79edb735 --- /dev/null +++ b/libvips/inplace/Makefile.am @@ -0,0 +1,15 @@ +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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/inplace/im_circle.c b/libvips/inplace/im_circle.c new file mode 100644 index 00000000..5dc92536 --- /dev/null +++ b/libvips/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/libvips/inplace/im_flood.c b/libvips/inplace/im_flood.c new file mode 100644 index 00000000..8730cb60 --- /dev/null +++ b/libvips/inplace/im_flood.c @@ -0,0 +1,429 @@ +/* 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 + * 24/3/09 + * - added IM_CODING_RAD 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 + +#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->Coding != IM_CODING_RAD ) { + im_error( "im_flood", "%s", + _( "Coding should be NONE, LABQ or RAD" ) ); + 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->Coding != IM_CODING_RAD ) { + im_error( "im_flood", "%s", + _( "Coding should be NONE, LABQ or RAD" ) ); + 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/libvips/inplace/im_insertplace.c b/libvips/inplace/im_insertplace.c new file mode 100644 index 00000000..73605377 --- /dev/null +++ b/libvips/inplace/im_insertplace.c @@ -0,0 +1,129 @@ +/* @(#) 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 + * 24/3/09 + * - added IM_CODING_RAD 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 + +#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", "%s", + _( "inputs differ in format" ) ); + return( -1 ); + } + if( big->Coding != IM_CODING_NONE && + big->Coding != IM_CODING_LABQ && + big->Coding != IM_CODING_RAD ) { + im_error( "im_insertplace", "%s", + _( "Coding should be NONE, LABQ or RAD" ) ); + 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", + "%s", _( "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/libvips/inplace/im_line.c b/libvips/inplace/im_line.c new file mode 100644 index 00000000..925846ca --- /dev/null +++ b/libvips/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/libvips/inplace/im_paintrect.c b/libvips/inplace/im_paintrect.c new file mode 100644 index 00000000..cfad16d3 --- /dev/null +++ b/libvips/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/libvips/inplace/im_plotmask.c b/libvips/inplace/im_plotmask.c new file mode 100644 index 00000000..33b3ab98 --- /dev/null +++ b/libvips/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", + "%s", _( "internal error" ) ); + return( -1 ); + } + } + } + + im_invalidate( im ); + + return( 0 ); +} + diff --git a/libvips/inplace/inplace_dispatch.c b/libvips/inplace/inplace_dispatch.c new file mode 100644 index 00000000..07838f25 --- /dev/null +++ b/libvips/inplace/inplace_dispatch.c @@ -0,0 +1,288 @@ +/* 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", "%s", _( "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", + "%s", _( "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/libvips/inplace/line_draw.c b/libvips/inplace/line_draw.c new file mode 100644 index 00000000..a3f4fc38 --- /dev/null +++ b/libvips/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", + "%s", _( "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", + "%s", _( "ink image does not match in image" ) ); + return( -1 ); + } + if( ink->Xsize != 1 || ink->Ysize != 1 ) { + im_error( "im_lineset", "%s", _( "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/libvips/inplace/plot_point.c b/libvips/inplace/plot_point.c new file mode 100644 index 00000000..379f3157 --- /dev/null +++ b/libvips/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/libvips/inplace/smudge_area.c b/libvips/inplace/smudge_area.c new file mode 100644 index 00000000..8c534c21 --- /dev/null +++ b/libvips/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", "%s", _( "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", "%s", _( "unknown band format" ) ); + return( -1 ); + } + + im_invalidate( im ); + + return( 0 ); +} diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am new file mode 100644 index 00000000..fb50f151 --- /dev/null +++ b/libvips/iofuncs/Makefile.am @@ -0,0 +1,61 @@ +noinst_LTLIBRARIES = libiofuncs.la + +libiofuncs_la_SOURCES = \ + object.c \ + 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_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_open.c \ + im_open_vips.c \ + im_partial.c \ + im_piocheck.c \ + im_prepare.c \ + im_printdesc.c \ + im_printlines.c \ + im_render.c \ + im_setbox.c \ + im_setbuf.c \ + im_setupout.c \ + im_unmapfile.c \ + im_updatehist.c \ + im_guess_prefix.c \ + im_wbuffer.c \ + im_wrapmany.c \ + im_wraptwo.c \ + im_writeline.c \ + memory.c \ + package.c \ + predicate.c \ + region.c \ + rect.c \ + semaphore.c \ + threadgroup.c \ + util.c \ + im_init_world.c \ + buf.c \ + window.c \ + buffer.c \ + time.c + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/iofuncs/base64.c b/libvips/iofuncs/base64.c new file mode 100644 index 00000000..010ec0ad --- /dev/null +++ b/libvips/iofuncs/base64.c @@ -0,0 +1,288 @@ +/* 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() + +12/5/09 + - fix signed/unsigned warning + + */ + +/* +#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", "%s", _( "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", "%s", _( "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", "%s", _( "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; + } + } + } + + g_assert( (size_t) (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/libvips/iofuncs/base64.h b/libvips/iofuncs/base64.h new file mode 100644 index 00000000..ffe09f28 --- /dev/null +++ b/libvips/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/libvips/iofuncs/buf.c b/libvips/iofuncs/buf.c new file mode 100644 index 00000000..9c7938b2 --- /dev/null +++ b/libvips/iofuncs/buf.c @@ -0,0 +1,583 @@ +/* string buffers + */ + +/* + + 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*/ + +/** + * SECTION: buf + * @short_description: a string you can append to + * @stability: Stable + * @see_also: #vips + * @include: vips/vips.h + * + * A message buffer you can append stuff to safely and quickly. If the message + * gets too long, you get "..." and truncation. Message buffers can be on the + * stack or heap. + * + * For example: + * + * |[ + * char txt[256]; + * VipsBuf buf = VIPS_BUF_STATIC (txt); + * int i; + * + * vips_buf_appends (&buf, "Numbers are: "); + * for (i = 0; i < array_length; i++) { + * if (i > 0) + * vips_buf_appends (&buf, ", "); + * vips_buf_appendg (&buf, array[i]); + * } + * printf ("%s", vips_buf_all (&buf)); + * ]| + */ + +/* Largest string we can append in one operation. + */ +#define MAX_STRSIZE (16000) + +/** + * VIPS_BUF_STATIC: + * @TEXT: the storage area to use + * @MAX: the size of the storage area + * + * Initialize a heap buffer. For example: + * + * |[ + * char txt[256]; + * VipsBuf buf = VIPS_BUF_STATIC (txt); + * ]| + */ + +/** + * vips_buf_rewind: + * @buf: the buffer + * + * Reset the buffer to the empty string. + */ +void +vips_buf_rewind( VipsBuf *buf ) +{ + buf->i = 0; + buf->lasti = 0; + buf->full = FALSE; + + if( buf->base ) + buf->base[0] = '\0'; +} + +/** + * vips_buf_init: + * @buf: the buffer + * + * Initialize a buffer. + */ +void +vips_buf_init( VipsBuf *buf ) +{ + buf->base = NULL; + buf->mx = 0; + buf->dynamic = FALSE; + vips_buf_rewind( buf ); +} + +/** + * vips_buf_destroy: + * @buf: the buffer + * + * Destroy a buffer. Only needed for heap buffers. Leaves the buffer in the + * _init state. + */ +void +vips_buf_destroy( VipsBuf *buf ) +{ + if( buf->dynamic ) { + IM_FREE( buf->base ); + } + + vips_buf_init( buf ); +} + +/** + * vips_buf_set_static: + * @buf: the buffer + * @base: the start of the memory area to use for storage + * @mx: the size of the storage area + * + * Attach the buffer to a static memory area. The buffer needs to have been + * initialised. The memory area needs to be at least 4 bytes long. + */ +void +vips_buf_set_static( VipsBuf *buf, char *base, int mx ) +{ + g_assert( mx >= 4 ); + + vips_buf_destroy( buf ); + + buf->base = base; + buf->mx = mx; + buf->dynamic = FALSE; + vips_buf_rewind( buf ); +} + +/** + * vips_buf_init_static: + * @buf: the buffer + * @base: the start of the memory area to use for storage + * @mx: the size of the storage area + * + * Initialise and attach to a static memory area. VIPS_BUF_STATIC() is usually + * more convenient. + * + * For example: + * + * |[ + * char txt[256]; + * VipsBuf buf; + * + * vips_buf_init_static (&buf, txt, 256); + * ]| + * + * Static buffers don't need to be freed when they go out of scope, but their + * size must be set at compile-time. + */ +void +vips_buf_init_static( VipsBuf *buf, char *base, int mx ) +{ + vips_buf_init( buf ); + vips_buf_set_static( buf, base, mx ); +} + +/** + * vips_buf_set_dynamic: + * @buf: the buffer + * @mx: the size of the storage area + * + * Attach the buffer to a heap memory area. The buffer needs to have been + * initialised. The memory area needs to be at least 4 bytes long. + */ +void +vips_buf_set_dynamic( VipsBuf *buf, int mx ) +{ + g_assert( mx >= 4 ); + + if( buf->mx == mx && buf->dynamic ) + /* No change? + */ + vips_buf_rewind( buf ); + else { + vips_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; + vips_buf_rewind( buf ); + } + } +} + +/** + * vips_buf_init_dynamic: + * @buf: the buffer + * @mx: the size of the storage area + * + * Initialise and attach to a heap memory area. + * The memory area needs to be at least 4 bytes long. + * + * |[ + * VipsBuf buf; + * + * vips_buf_init_synamic (&buf, 256); + * ]| + * + * Dynamic buffers must be freed with vips_buf_destroy(), but their size can + * be set at runtime. + */ +void +vips_buf_init_dynamic( VipsBuf *buf, int mx ) +{ + vips_buf_init( buf ); + vips_buf_set_dynamic( buf, mx ); +} + +/** + * vips_buf_appendns: + * @buf: the buffer + * @str: the string to append to the buffer + * @sz: the size of the string to append + * + * Append at most @sz chars from @str to @buf. @sz < 0 means unlimited. This + * is the low-level append operation: functions like vips_buf_appendf() build + * on top of this. + * + * Returns: %FALSE on overflow, %TRUE otherwise. + */ + +gboolean +vips_buf_appendns( VipsBuf *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 ); +} + +/** + * vips_buf_appends: + * @buf: the buffer + * @str: the string to append to the buffer + * + * Append the whole of @str to @buf. + * + * Returns: %FALSE on overflow, %TRUE otherwise. + */ +gboolean +vips_buf_appends( VipsBuf *buf, const char *str ) +{ + return( vips_buf_appendns( buf, str, -1 ) ); +} + +/** + * vips_buf_appendc: + * @buf: the buffer + * @ch: the character to append to the buffer + * + * Append a single character @ch to @buf. + * + * Returns: %FALSE on overflow, %TRUE otherwise. + */ + +gboolean +vips_buf_appendc( VipsBuf *buf, char ch ) +{ + char tiny[2]; + + tiny[0] = ch; + tiny[1] = '\0'; + + return( vips_buf_appendns( buf, tiny, 1 ) ); +} + +/** + * vips_buf_change: + * @buf: the buffer + * @old: the string to search for + * @new: the string to substitute + * + * Swap the rightmost occurence of @old for @new. + * + * Returns: %FALSE on overflow, %TRUE otherwise. + */ + +gboolean +vips_buf_change( VipsBuf *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; + g_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 ); +} + +/** + * vips_buf_removec: + * @buf: the buffer + * @ch: the character to remove + * + * Remove the last character, if it's @ch. + * + * Returns: %FALSE on failure, %TRUE otherwise. + */ +gboolean +vips_buf_removec( VipsBuf *buf, char ch ) +{ + if( buf->full ) + return( FALSE ); + if( buf->i <= 0 ) + return( FALSE ); + if( buf->base[buf->i - 1] == ch ) + buf->i -= 1; + + return( TRUE ); +} + +/** + * vips_buf_appendf: + * @buf: the buffer + * @fmt: printf()-style format string + * @Varargs: arguments to format string + * + * Format the string and append to @buf. + * + * Returns: %FALSE on overflow, %TRUE otherwise. + */ +gboolean +vips_buf_appendf( VipsBuf *buf, const char *fmt, ... ) +{ + char str[MAX_STRSIZE]; + va_list ap; + + va_start( ap, fmt ); + (void) im_vsnprintf( str, MAX_STRSIZE, fmt, ap ); + va_end( ap ); + + return( vips_buf_appends( buf, str ) ); +} + +/** + * vips_buf_vappendf: + * @buf: the buffer + * @fmt: printf()-style format string + * @ap: arguments to format string + * + * Append to @buf, args as vprintf(). + * + * Returns: %FALSE on overflow, %TRUE otherwise. + */ +gboolean +vips_buf_vappendf( VipsBuf *buf, const char *fmt, va_list ap ) +{ + char str[MAX_STRSIZE]; + + (void) im_vsnprintf( str, MAX_STRSIZE, fmt, ap ); + + return( vips_buf_appends( buf, str ) ); +} + +/** + * vips_buf_appendg: + * @buf: the buffer + * @g: value to format and append + * + * Append a double, non-localised. Useful for config files etc. + * + * Returns: %FALSE on overflow, %TRUE otherwise. + */ +gboolean +vips_buf_appendg( VipsBuf *buf, double g ) +{ + char text[G_ASCII_DTOSTR_BUF_SIZE]; + + g_ascii_dtostr( text, sizeof( text ), g ); + + return( vips_buf_appends( buf, text ) ); +} + +/** + * vips_buf_appendd: + * @buf: the buffer + * @d: value to format and append + * + * Append a number. If the number is -ve, add brackets. Needed for + * building function arguments. + * + * Returns: %FALSE on overflow, %TRUE otherwise. + */ +gboolean +vips_buf_appendd( VipsBuf *buf, int d ) +{ + if( d < 0 ) + return( vips_buf_appendf( buf, " (%d)", d ) ); + else + return( vips_buf_appendf( buf, " %d", d ) ); +} + +/** + * vips_buf_appendgv: + * @buf: the buffer + * @value: #GValue to format and append + * + * Format and append a #GValue with g_strdup_value_contents(). + * + * Returns: %FALSE on overflow, %TRUE otherwise. + */ +gboolean +vips_buf_appendgv( VipsBuf *buf, GValue *value ) +{ + char *str_value; + gboolean result; + + str_value = g_strdup_value_contents( value ); + result = vips_buf_appends( buf, str_value ); + g_free( str_value ); + + return( result ); +} + +/** + * vips_buf_all: + * @buf: the buffer + * + * Return the contents of the buffer as a C string. + * + * Returns: the %NULL-terminated contents of the buffer. This is a pointer to + * the memory managed by the buffer and must not be freed. + */ +const char * +vips_buf_all( VipsBuf *buf ) +{ + buf->base[buf->i] = '\0'; + + return( buf->base ); +} + +/** + * vips_buf_firstline: + * @buf: the buffer + * + * Trim to just the first line (excluding "\n"). + * + * Returns: the %NULL-terminated contents of the buffer. This is a pointer to + * the memory managed by the buffer and must not be freed. + */ +const char * +vips_buf_firstline( VipsBuf *buf ) +{ + char *p; + + if( (p = strchr( vips_buf_all( buf ), '\n' )) ) + *p = '\0'; + + return( vips_buf_all( buf ) ); +} + +/** + * vips_buf_is_empty: + * @buf: the buffer + * + * Returns: %TRUE if the buffer is empty. + */ +gboolean +vips_buf_is_empty( VipsBuf *buf ) +{ + return( buf->i == 0 ); +} + +/** + * vips_buf_is_full: + * @buf: the buffer + * + * Returns: %TRUE if the buffer is full. + */ +gboolean +vips_buf_is_full( VipsBuf *buf ) +{ + return( buf->full ); +} + +/** + * vips_buf_len: + * @buf: the buffer + * + * Returns: the number of characters currently in the buffer. + */ +int +vips_buf_len( VipsBuf *buf ) +{ + return( buf->i ); +} + diff --git a/libvips/iofuncs/buffer.c b/libvips/iofuncs/buffer.c new file mode 100644 index 00000000..c2b292bc --- /dev/null +++ b/libvips/iofuncs/buffer.c @@ -0,0 +1,591 @@ +/* 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. child is one of parent's inputs. + */ +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 ); + + /* Propogate the progress indicator. + */ + if( child->progress && !parent->progress ) + parent->progress = child->progress; +} + +/* Break link. child is one of parent's inputs. + */ +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 ); + + /* Unlink the progress chain. + */ + if( parent->progress && parent->progress == child->progress ) + parent->progress = NULL; + + 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 ); + if( im__trigger_callbacks( im->invalidatefns ) ) + return( im ); + + 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/libvips/iofuncs/callback.c b/libvips/iofuncs/callback.c new file mode 100644 index 00000000..365118fa --- /dev/null +++ b/libvips/iofuncs/callback.c @@ -0,0 +1,171 @@ +/* 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 + * 2/7/08 + * - added invalidate callbacks + * 26/11/08 + * - don't set im_error() on callback failed, that's the user's job + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 ); +} + +int +im_add_close_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->closefns, fn, a, b ) ); +} + +int +im_add_preclose_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->preclosefns, fn, a, b ) ); +} + +/* Add an eval callback to an IMAGE. You must call this after opening the + * image but before using it as an argument to an operation. + */ +int +im_add_eval_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + /* Mark this image as needing progress feedback. im__link_make() + * propogates this value to our children as we build a pipeline. + * im__handle_eval() looks up the IMAGE it should signal on. + */ + im->progress = im; + + return( add_callback( im, &im->evalfns, fn, a, b ) ); +} + +int +im_add_evalend_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->evalendfns, fn, a, b ) ); +} + +int +im_add_evalstart_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->evalstartfns, fn, a, b ) ); +} + +int +im_add_invalidate_callback( IMAGE *im, int (*fn)(), void *a, void *b ) +{ + return( add_callback( im, &im->invalidatefns, 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 )) ) { + /* We don't set im_error() here, that's the callback's + * responsibility. + */ + *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/libvips/iofuncs/debug.c b/libvips/iofuncs/debug.c new file mode 100644 index 00000000..94d264c6 --- /dev/null +++ b/libvips/iofuncs/debug.c @@ -0,0 +1,156 @@ +/* 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 +#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, gint64 *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, gint64 *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 ) { + gint64 size = (gint64) IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize; + + printf( "\t*** %" G_GINT64_FORMAT " malloced bytes\n", size ); + *total += size; + } + + if( im->regions ) { + int n2; + gint64 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 %" G_GINT64_FORMAT + " bytes\n", total2 ); + *total += total2; + } + + return( NULL ); +} + +static void * +add_virtual( IMAGE *im, gint64 *total, void *dummy ) +{ + *total += im__image_pixel_length( im ); + + return( NULL ); +} + +/* Print one line for each open descriptor. + */ +void +im__print_all( void ) +{ + if( im__open_images ) { + int n = 0; + gint64 total = 0; + + total = 0; + 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 = %" G_GINT64_FORMAT + " real bytes\n", + total ); + + total = 0; + (void) im_slist_map2( im__open_images, + (VSListMap2Fn) add_virtual, &total, NULL ); + if( total ) + printf( "\n\t*** virtual total = %" G_GINT64_FORMAT + " 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/libvips/iofuncs/dispatch_types.c b/libvips/iofuncs/dispatch_types.c new file mode 100644 index 00000000..75c02bdf --- /dev/null +++ b/libvips/iofuncs/dispatch_types.c @@ -0,0 +1,904 @@ +/* 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", + "%s", _( "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 */ +}; + +/* Print function for doublevec output. + */ +int +im__dvprint( im_object obj ) +{ + im_doublevec_object *dv = obj; + int i; + + for( i = 0; i < dv->n; i++ ) + printf( "%G ", dv->vec[i] ); + printf( "\n" ); + + return( 0 ); +} + +/* Output double vector type. + */ +im_type_desc im__output_doublevec = { + IM_TYPE_DOUBLEVEC, /* Its an array of double */ + sizeof( im_doublevec_object ), /* Memory to allocate in vec build */ + IM_TYPE_OUTPUT, /* Output type */ + NULL, /* 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 */ +}; + +/* Print function for intvec output. + */ +int +im__ivprint( im_object obj ) +{ + im_intvec_object *iv = obj; + int i; + + for( i = 0; i < iv->n; i++ ) + printf( "%d ", iv->vec[i] ); + printf( "\n" ); + + return( 0 ); +} + +/* Output int vector type. + */ +im_type_desc im__output_intvec = { + IM_TYPE_INTVEC, /* It's an array of int */ + sizeof( im_intvec_object ), /* Memory to allocate in vec build */ + IM_TYPE_OUTPUT, /* Output arg */ + (im_init_obj_fn)NULL, /* Init function */ + (im_dest_obj_fn)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", + "%s", _( "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 */ +}; + +/* Init function for input interpolate. + */ +static int +input_interpolate_init( im_object *obj, char *str ) +{ + VipsObject *object; + + if( !(object = vips_object_new_from_string( "VipsInterpolate", str )) ) + return( -1 ); + *obj = object; + + return( 0 ); +} + +im_type_desc im__input_interpolate = { + IM_TYPE_INTERPOLATE, + 0, /* No storage required */ + IM_TYPE_ARG, /* It requires a command-line arg */ + (im_init_obj_fn) input_interpolate_init,/* Init function */ + (im_dest_obj_fn) g_object_unref /* Destroy function */ +}; + diff --git a/libvips/iofuncs/error.c b/libvips/iofuncs/error.c new file mode 100644 index 00000000..53a8e76d --- /dev/null +++ b/libvips/iofuncs/error.c @@ -0,0 +1,253 @@ +/* @(#) 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 + * 20/2/08 + * - lock around warnings and diagnostics too, why not + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 VipsBuf im_error_buf = VIPS_BUF_STATIC( im_error_text ); + +#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 = vips_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 ); + vips_buf_appendf( &im_error_buf, "%s: ", domain ); + vips_buf_vappendf( &im_error_buf, fmt, ap ); + vips_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 ); + vips_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 ) ) { + g_mutex_lock( im__global_lock ); + (void) fprintf( stderr, _( "%s: " ), _( "vips diagnostic" ) ); + (void) fprintf( stderr, _( "%s: " ), domain ); + (void) vfprintf( stderr, fmt, ap ); + (void) fprintf( stderr, "\n" ); + g_mutex_unlock( im__global_lock ); + } +} + +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 ) ) { + g_mutex_lock( im__global_lock ); + (void) fprintf( stderr, _( "%s: " ), _( "vips warning" ) ); + (void) fprintf( stderr, _( "%s: " ), domain ); + (void) vfprintf( stderr, fmt, ap ); + (void) fprintf( stderr, "\n" ); + g_mutex_unlock( im__global_lock ); + } +} + +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/libvips/iofuncs/error_exit.c b/libvips/iofuncs/error_exit.c new file mode 100644 index 00000000..370c848d --- /dev/null +++ b/libvips/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_error_buffer() ); + + exit( 1 ); +} diff --git a/libvips/iofuncs/im_binfile.c b/libvips/iofuncs/im_binfile.c new file mode 100644 index 00000000..106d66a7 --- /dev/null +++ b/libvips/iofuncs/im_binfile.c @@ -0,0 +1,186 @@ +/* @(#) 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() + * 12/5/09 + * - fix signed/unsigned warnings + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 psize; + gint64 rsize; + + /* Check parameters. + */ + if( xs <= 0 || ys <= 0 || bands <=0 ) { + im_error( "im_binfile", + "%s", _( "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 real file length and check against what we think + * the size should be. + */ + if( (rsize = im_file_length( im->fd )) == -1 ) { + im_close( im ); + return( NULL ); + } + im->file_length = (size_t) rsize; + + /* Very common, so special message. + */ + if( psize > rsize ) { + 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 < rsize ) + 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/libvips/iofuncs/im_bits_of_fmt.c b/libvips/iofuncs/im_bits_of_fmt.c new file mode 100644 index 00000000..c2abe152 --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_close.c b/libvips/iofuncs/im_close.c new file mode 100644 index 00000000..70dd3d45 --- /dev/null +++ b/libvips/iofuncs/im_close.c @@ -0,0 +1,310 @@ +/* @(#) 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 + * 7/11/07 + * - added preclose, removed evalend triggers + * 23/7/08 + * - im__close() will no longer free regions + * 9/8/08 + * - lock global image list (thanks lee) + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 + +#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; + + 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*/ + + /* Trigger all pre-close fns. + */ + result |= im__trigger_callbacks( im->preclosefns ); + IM_FREEF( im_slist_free_all, im->preclosefns ); + + /* Should be no regions defined on the image. im_close() ought to put + * us into a zombie state if there are, im__close() should not be + * called on images with running regions. + */ + if( im->regions ) { + GSList *p; + + printf( "** im__close: leaked regions!\n" ); + for( p = im->regions; p; p = p->next ) + im_region_print( (REGION *) p->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 ); + } + + /* Junk all callbacks, perform close callbacks. + */ + IM_FREEF( im_slist_free_all, im->evalstartfns ); + IM_FREEF( im_slist_free_all, im->evalfns ); + IM_FREEF( im_slist_free_all, im->evalendfns ); + IM_FREEF( im_slist_free_all, im->invalidatefns ); + 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 ); + + if( im->regions ) { + /* There are regions left on this image. + * Set close_pending and return. The image will be then + * be closed when the last region is freed + * (see im_region_free()). + */ +#ifdef DEBUG_IO + printf( "im_close: pending close for \"%s\"\n", im->filename ); +#endif /*DEBUG_IO*/ + + im->close_pending = 1; + } + else if( !im->closing ) { + /* Is this descriptor currently being closed somewhere else? + * This prevents infinite descent if a close callback + * includes an im_close for this image. + */ + im->closing = 1; + + 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 ); + g_mutex_lock( im__global_lock ); + im__open_images = g_slist_remove( im__open_images, im ); + g_mutex_unlock( im__global_lock ); + im__time_destroy( im ); + IM_FREE( im ); + } + + return( result ); +} diff --git a/libvips/iofuncs/im_cp_desc.c b/libvips/iofuncs/im_cp_desc.c new file mode 100644 index 00000000..8d72e01a --- /dev/null +++ b/libvips/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__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", + "%s", _( "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/libvips/iofuncs/im_debugim.c b/libvips/iofuncs/im_debugim.c new file mode 100644 index 00000000..59d1c6b3 --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_demand_hint.c b/libvips/iofuncs/im_demand_hint.c new file mode 100644 index 00000000..ab901d6c --- /dev/null +++ b/libvips/iofuncs/im_demand_hint.c @@ -0,0 +1,189 @@ +/* @(#) 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", + "%s", _( "too many images" ) ); + return( -1 ); + } + + return( im_demand_hint_array( im, hint, ar ) ); +} diff --git a/libvips/iofuncs/im_generate.c b/libvips/iofuncs/im_generate.c new file mode 100644 index 00000000..553feadd --- /dev/null +++ b/libvips/iofuncs/im_generate.c @@ -0,0 +1,509 @@ +/* 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 + * 7/11/07 + * - new start/end eval callbacks + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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, void *client, void *dummy ) +{ + IMAGE *in = (IMAGE *) client; + + return( im_region_create( in ) ); +} + +int +im_stop_one( void *seq, void *dummy1, void *dummy2 ) +{ + REGION *reg = (REGION *) seq; + + 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( void *seq, void *dummy1, void *dummy2 ) +{ + REGION **ar = (REGION **) seq; + + if( ar ) { + int i; + + for( i = 0; ar[i]; i++ ) + im_region_free( ar[i] ); + im_free( (char *) ar ); + } + + return( 0 ); +} + +void * +im_start_many( IMAGE *out, void *client, void *dummy ) +{ + IMAGE **in = (IMAGE **) client; + + 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 ); + + /* 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; + int result; + + result = 0; + +#ifdef DEBUG_IO + int ntiles = 0; + printf( "eval_to_memory: partial image output to memory area\n" ); +#endif /*DEBUG_IO*/ + + /* Signal start of eval. + */ + if( im__start_eval( im ) ) + return( -1 ); + + /* 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 = IM_MIN( chunk, im->Ysize - y ); + if( (result = im_region_image( or, &pos )) ) + break; + + /* Ask for evaluation of this area. + */ + if( (result = eval_to_region( or, tg )) ) + break; + + /* Trigger any eval callbacks on our source image. + */ + if( (result = im__handle_eval( im, pos.width, pos.height )) ) + break; + +#ifdef DEBUG_IO + ntiles++; +#endif /*DEBUG_IO*/ + } + + /* Signal end of eval. + */ + result |= im__end_eval( im ); + +#ifdef DEBUG_IO + printf( "eval_to_memory: %d patches written\n", ntiles ); +#endif /*DEBUG_IO*/ + + return( result ); +} + +/* A write function for VIPS images. Just write() the pixel data. + */ +static int +write_vips( REGION *region, Rect *area, void *a, void *b ) +{ + size_t nwritten, count; + void *buf; + + count = region->bpl * area->height; + buf = IM_REGION_ADDR( region, 0, area->top ); + do { + nwritten = write( region->im->fd, buf, count ); + if( nwritten == (size_t) -1 ) + return( errno ); + + buf = (void *) ((char *) buf + nwritten); + count -= nwritten; + } while( count > 0 ); + + return( 0 ); +} + +/* Attach a generate function to an image. + */ +int +im_generate( IMAGE *im, + im_start_fn start, im_generate_fn generate, im_stop_fn stop, + void *a, void *b ) +{ + int res; + REGION *or; + im_threadgroup_t *tg; + + g_assert( !im_image_sanity( im ) ); + + if( im->Xsize <= 0 || im->Ysize <= 0 || im->Bands <= 0 ) { + im_error( "im_generate", + "%s", _( "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", + "%s", _( "func already attached" ) ); + return( -1 ); + } + + im->start = start; + im->generate = generate; + im->stop = stop; + 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", + "%s", _( "func already attached" ) ); + return( -1 ); + } + + /* Get output ready. + */ + if( im_setupout( im ) ) + return( -1 ); + + /* Attach callbacks. + */ + im->start = start; + im->generate = generate; + im->stop = stop; + 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 = im_wbuffer( tg, write_vips, NULL, NULL ); + else + res = eval_to_memory( tg, or ); + + /* Clean up. + */ + im_threadgroup_free( tg ); + im_region_free( or ); + + /* 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; + + g_assert( !im_image_sanity( im ) ); + + 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/libvips/iofuncs/im_guess_prefix.c b/libvips/iofuncs/im_guess_prefix.c new file mode 100644 index 00000000..d4fb970a --- /dev/null +++ b/libvips/iofuncs/im_guess_prefix.c @@ -0,0 +1,428 @@ +/* @(#) 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) + * 5/8/08 + * - added im_guess_libdir() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 ); +} + +const char * +im_guess_libdir( const char *argv0, const char *env_name ) +{ + const char *prefix = im_guess_prefix( argv0, env_name ); + static char *libdir = NULL; + + if( libdir ) + return( libdir ); + + /* Have we been moved since configure? If not, use the configure-time + * libdir. + */ + if( strcmp( prefix, IM_PREFIX ) == 0 ) + libdir = IM_LIBDIR; + else + libdir = g_strdup_printf( "%s/lib", prefix ); + +#ifdef DEBUG + printf( "im_guess_libdir: IM_PREFIX = %s\n", IM_PREFIX ); + printf( "im_guess_libdir: IM_LIBDIR = %s\n", IM_LIBDIR ); + printf( "im_guess_libdir: prefix = %s\n", prefix ); + printf( "im_guess_libdir: libdir = %s\n", libdir ); +#endif /*DEBUG*/ + + return( libdir ); +} + diff --git a/libvips/iofuncs/im_header.c b/libvips/iofuncs/im_header.c new file mode 100644 index 00000000..e6a9ac2f --- /dev/null +++ b/libvips/iofuncs/im_header.c @@ -0,0 +1,269 @@ +/* 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 + * 29/8/09 + * - im_header_get_type() renamed as im_header_get_typeof() to prevent + * confusion with GObject-style type definers + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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*/ + +/* 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_typeof( 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_typeof( 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/libvips/iofuncs/im_histlin.c b/libvips/iofuncs/im_histlin.c new file mode 100644 index 00000000..5e3fea4e --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_image.c b/libvips/iofuncs/im_image.c new file mode 100644 index 00000000..d44adf3d --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_init.c b/libvips/iofuncs/im_init.c new file mode 100644 index 00000000..b181e8d9 --- /dev/null +++ b/libvips/iofuncs/im_init.c @@ -0,0 +1,185 @@ +/* @(#) 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 + * 7/11/07 + * - added preclose and evalstart + * 9/8/08 + * - lock global image list (thanks lee) + * 19/3/09 + * - add file_length + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 +#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; + + im->progress = NULL; + + im->evalstartfns = NULL; + im->preclosefns = NULL; + im->invalidatefns = NULL; + + im->file_length = 0; + + if( !(im->filename = im_strdup( NULL, filename )) ) { + im_close( im ); + return( NULL ); + } + + g_mutex_lock( im__global_lock ); + im__open_images = g_slist_prepend( im__open_images, im ); + g_mutex_unlock( im__global_lock ); + + return( im ); +} diff --git a/libvips/iofuncs/im_init_world.c b/libvips/iofuncs/im_init_world.c new file mode 100644 index 00000000..5410cd2b --- /dev/null +++ b/libvips/iofuncs/im_init_world.c @@ -0,0 +1,240 @@ +/* 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 + * 7/11/07 + * - progress feedback option + * 5/8/08 + * - load plugins from libdir/vips-x.x + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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; + const char *libdir; + 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" )) || + !(libdir = im_guess_libdir( 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" ); + + /* Register base vips types. + */ + im__meta_init_types(); + im__format_init(); + vips__interpolate_init(); + + /* Load up any plugins in the vips libdir. 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/vips-%d.%d", + libdir, IM_MAJOR_VERSION, IM_MINOR_VERSION ) ) { + im_warn( "im_init_world", "%s", im_errorstring() ); + im_error_clear(); + } + + /* Also load from libdir. This is old and slightly broken behaviour + * :-( kept for back compat convenience. + */ + if( im_load_plugins( "%s", libdir ) ) { + 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", 'w', 0, G_OPTION_ARG_INT, &im__tile_width, + N_( "set tile width to N (DEBUG)" ), "N" }, + { "vips-tile-height", 'h', 0, G_OPTION_ARG_INT, &im__tile_height, + N_( "set tile height to N (DEBUG)" ), "N" }, + { "vips-thinstrip-height", 't', 0, + G_OPTION_ARG_INT, &im__thinstrip_height, + N_( "set thinstrip height to N (DEBUG)" ), "N" }, + { "vips-fatstrip-height", 'f', 0, + G_OPTION_ARG_INT, &im__fatstrip_height, + N_( "set fatstrip height to N (DEBUG)" ), "N" }, + { "vips-progress", 'p', 0, G_OPTION_ARG_NONE, &im__progress, + N_( "show progress feedback" ), NULL }, + { 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/libvips/iofuncs/im_initdesc.c b/libvips/iofuncs/im_initdesc.c new file mode 100644 index 00000000..7c6833cf --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_iocheck.c b/libvips/iofuncs/im_iocheck.c new file mode 100644 index 00000000..61c6ecb7 --- /dev/null +++ b/libvips/iofuncs/im_iocheck.c @@ -0,0 +1,320 @@ +/* @(#) 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 ) +{ + g_assert( !im_image_sanity( im ) ); + +#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_error( "im_incheck", + "%s", _( "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 to WIO\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_error( "im_incheck", + _( "auto-rewind for %s failed" ), + im->filename ); + return( -1 ); + } + + break; + + default: + im_error( "im_incheck", + "%s", _( "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_error( "im_outcheck", + "%s", _( "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_error( "im_outcheck", + "%s", _( "image already written" ) ); + return( -1 ); + } + + break; + + case IM_OPENOUT: + case IM_SETBUF_FOREIGN: + /* Can write to this ok. + */ + break; + + default: + im_error( "im_outcheck", + "%s", _( "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_error( "im_rwcheck", + "%s", _( "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_error( "im_rwcheck", + "%s", _( "bad file type" ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libvips/iofuncs/im_iterate.c b/libvips/iofuncs/im_iterate.c new file mode 100644 index 00000000..a3506b75 --- /dev/null +++ b/libvips/iofuncs/im_iterate.c @@ -0,0 +1,240 @@ +/* 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 + * 7/11/07 + * - new eval start/progress/end system + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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, + * check for errors. + */ + if( im__handle_eval( im, tg->pw, tg->ph ) || + 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, + im_start_fn start, im_generate_fn generate, im_stop_fn 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, + im_start_fn start, im_generate_fn generate, im_stop_fn stop, + void *b, void *c ) +{ + IMAGE *t; + im_threadgroup_t *tg; + int result; + + g_assert( !im_image_sanity( im ) ); + + if( !(t = im_open( "iterate", "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*/ + + /* Signal start of eval. + */ + if( im__start_eval( t ) ) + return( -1 ); + + result = iterate( tg, t, start, generate, stop, b, c ); + + /* Signal end of eval. + */ + result |= im__end_eval( t ); + + im_threadgroup_free( tg ); + im_close( t ); + + return( result ); +} diff --git a/libvips/iofuncs/im_makerw.c b/libvips/iofuncs/im_makerw.c new file mode 100644 index 00000000..49342e70 --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_mapfile.c b/libvips/iofuncs/im_mapfile.c new file mode 100644 index 00000000..f7bb389a --- /dev/null +++ b/libvips/iofuncs/im_mapfile.c @@ -0,0 +1,347 @@ +/* @(#) 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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "unable to UnmapViewOfFile" ) ); + return( -1 ); + } +#else /*!OS_WIN32*/ + if( munmap( start, length ) < 0 ) { + im_error_system( errno, "im_mapfile", + "%s", _( "unable to munmap file" ) ); + return( -1 ); + } +#endif /*OS_WIN32*/ + + return( 0 ); +} + +int +im_mapfile( IMAGE *im ) +{ + 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. + */ + g_assert( im->file_length > 0 ); + if( fstat( im->fd, &st ) == -1 ) { + im_error( "im_mapfile", + "%s", _( "unable to get file status" ) ); + return( -1 ); + } + m = (mode_t) st.st_mode; + if( im->file_length < 64 ) { + im_error( "im_mapfile", + "%s", _( "file is less than 64 bytes" ) ); + return( -1 ); + } + if( !S_ISREG( m ) ) { + im_error( "im_mapfile", + "%s", _( "not a regular file" ) ); + return( -1 ); + } + + if( !(im->baseaddr = im__mmap( im->fd, 0, im->file_length, 0 )) ) + return( -1 ); + + im->length = im->file_length; + + return( 0 ); +} + +/* As above, but map read/write. + */ +int +im_mapfilerw( IMAGE *im ) +{ + 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 + */ + g_assert( im->file_length > 0 ); + if( fstat( im->fd, &st ) == -1 ) { + im_error( "im_mapfilerw", + "%s", _( "unable to get file status" ) ); + return( -1 ); + } + m = (mode_t) st.st_mode; + if( im->file_length < 64 || !S_ISREG( m ) ) { + im_error( "im_mapfile", + "%s", _( "unable to read data" ) ); + return( -1 ); + } + + if( !(im->baseaddr = im__mmap( im->fd, 1, im->file_length, 0 )) ) + return( -1 ); + + im->length = im->file_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", + "%s", _( "unable to CreateFileMapping" ) ); + return( -1 ); + } + + if( !UnmapViewOfFile( image->baseaddr ) ) { + im_error_system( GetLastError(), "im_mapfile", + "%s", _( "unable to UnmapViewOfFile" ) ); + return( -1 ); + } + if( !(baseaddr = (char *)MapViewOfFileEx( hMMFile, FILE_MAP_WRITE, + 0, 0, 0, image->baseaddr )) ) { + im_error_system( GetLastError(), "im_mapfile", + "%s", _( "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/libvips/iofuncs/im_open.c b/libvips/iofuncs/im_open.c new file mode 100644 index 00000000..59c0b8e9 --- /dev/null +++ b/libvips/iofuncs/im_open.c @@ -0,0 +1,498 @@ +/* @(#) 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) + * 7/11/07 + * - use preclose, not evalend, for delayed save + * - add simple cmd-line progress feedback + * 9/8/08 + * - lock global image list (thanks lee) + * 25/5/08 + * - break file format stuff out to the new pluggable image format system + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Progress feedback. Only really useful for testing, tbh. + */ +int im__progress = 0; + +/* Delayed save: if we write to TIFF or to JPEG format, actually do the write + * to a "p" and on preclose 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 preclose 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_preclose_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, void *a, void *dummy ) +{ + OpenLazy *lazy = (OpenLazy *) a; + + if( !lazy->lazy_im ) { + if( !(lazy->lazy_im = im_open_local( out, "read", "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, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + + 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 ); +} + +/* Progress feedback. + */ + +/* What we track during an eval. + */ +typedef struct { + IMAGE *im; + + int last_percent; /* The last %complete we displayed */ +} Progress; + +int +evalstart_cb( Progress *progress ) +{ + progress->last_percent = 0; + + return( 0 ); +} + +int +eval_cb( Progress *progress ) +{ + IMAGE *im = progress->im; + + if( im->time->percent != progress->last_percent ) { + printf( _( "%s %s: %d%% complete" ), + g_get_prgname(), im->filename, im->time->percent ); + printf( "\r" ); + fflush( stdout ); + + progress->last_percent = im->time->percent; + } + + return( 0 ); +} + +int +evalend_cb( Progress *progress ) +{ + IMAGE *im = progress->im; + + /* Spaces at end help to erase the %complete message we overwrite. + */ + printf( _( "%s %s: done in %ds \n" ), + g_get_prgname(), im->filename, im->time->run ); + + return( 0 ); +} + +IMAGE * +im_open( const char *filename, const char *mode ) +{ + IMAGE *im; + VipsFormatClass *format; + + /* 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", "%s", _( "NULL filename or mode" ) ); + return( NULL ); + } + + /* + + we can't use the vips handler in the format system, since we + want to be able to open the image directly rather than + copying to an existing descriptor + + if we don't do this, things like paintbox apps won't work + + */ + + switch( mode[0] ) { + case 'r': + if( (format = vips_format_for_file( filename )) ) { + if( strcmp( VIPS_OBJECT_CLASS( format )->nickname, + "vips" ) == 0 ) { + if( !(im = im_open_vips( filename )) ) + return( NULL ); + } + else if( !(im = open_sub( + format->header, format->load, filename )) ) + return( NULL ); + } + else + return( NULL ); + break; + + case 'w': + if( (format = vips_format_for_name( filename )) ) { + if( strcmp( VIPS_OBJECT_CLASS( format )->nickname, + "vips" ) == 0 ) + im = im_openout( filename ); + else { + if( !(im = im_open( "im_open:lw:1", "p" )) ) + return( NULL ); + if( attach_sb( im, format->save, filename ) ) { + im_close( im ); + return( NULL ); + } + } + } + else { + char suffix[FILENAME_MAX]; + + im_filename_suffix( filename, suffix ); + im_error( "im_open", + _( "unsupported filetype \"%s\"" ), + suffix ); + + return( NULL ); + } + break; + + case 't': + im = im_setbuf( filename ); + break; + + case 'p': + im = im_partial( filename ); + break; + + default: + im_error( "im_open", _( "bad mode \"%s\"" ), mode ); + return( NULL ); + } + + /* Attach progress feedback, if required. + */ + if( im__progress || g_getenv( "IM_PROGRESS" ) ) { + Progress *progress = IM_NEW( im, Progress ); + + progress->im = im; + im_add_evalstart_callback( im, + (im_callback_fn) evalstart_cb, progress, NULL ); + im_add_eval_callback( im, + (im_callback_fn) eval_cb, progress, NULL ); + im_add_evalend_callback( im, + (im_callback_fn) evalend_cb, progress, 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" ); + + g_mutex_lock( im__global_lock ); + if( !g_slist_find( im__open_images, im ) ) { + g_mutex_unlock( im__global_lock ); + return( "not on open image list" ); + } + g_mutex_unlock( im__global_lock ); + + 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->Coding != IM_CODING_RAD) || + 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", "%p", im ); + im_warn( "im_image_sanity", "\"%s\" %s", + im ? (im->filename ? im->filename : "") : "", + msg ); + im_printdesc( im ); + + return( -1 ); + } + + return( 0 ); +} + +/* Just here for compatibility. + */ +IMAGE * +im_open_header( const char *file ) +{ + return( im_open( file, "r" ) ); +} diff --git a/libvips/iofuncs/im_open_vips.c b/libvips/iofuncs/im_open_vips.c new file mode 100644 index 00000000..3504a17a --- /dev/null +++ b/libvips/iofuncs/im_open_vips.c @@ -0,0 +1,1267 @@ +/* Read and write a VIPS file into an IMAGE * + * + * 22/5/08 + * - from im_open.c, im_openin.c, im_desc_hd.c, im_readhist.c, + * im_openout.c + * 19/3/09 + * - block mmaps of nodata images + * 12/5/09 + * - fix signed/unsigned warnings + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 +#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 +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/** + * SECTION: image + * @short_description: the VIPS image class + * @stability: Stable + * @see_also: #vips + * @include: vips/vips.h + * + * The VIPS image class and associated macros. + */ + +/** + * IM_MAGIC_INTEL: + * + * The first four bytes of a VIPS file in Intel byte ordering. + */ + +/** + * IM_MAGIC_SPARC: + * + * The first four bytes of a VIPS file in SPARC byte ordering. + */ + +/** + * VipsDemandStyle: + * @IM_SMALLTILE: demand in small (typically 64x64 pixel) tiles + * @IM_FATSTRIP: demand in fat (typically 10 pixel high) strips + * @IM_THINSTRIP: demand in thin (typically 1 pixel high) strips + * @IM_ANY: demand geometry does not matter + * + * Demand style. See im_demand_hint(). + */ + +/** + * VipsType: + * @IM_TYPE_MULTIBAND: generic many-band image + * @IM_TYPE_B_W: some kind of single-band image + * @IM_TYPE_HISTOGRAM: a 1D image such as a histogram or lookup table + * @IM_TYPE_FOURIER: image is in fourier space + * @IM_TYPE_XYZ: the first three bands are colours in CIE XYZ colourspace + * @IM_TYPE_LAB: pixels are in CIE Lab space + * @IM_TYPE_CMYK: the first four bands are in CMYK space + * @IM_TYPE_LABQ: implies %IM_CODING_LABQ + * @IM_TYPE_RGB: generic RGB space + * @IM_TYPE_UCS: a uniform colourspace based on CMC + * @IM_TYPE_LCH: pixels are in CIE LCh space + * @IM_TYPE_LABS: pixels are CIE LAB coded as three signed 16-bit values + * @IM_TYPE_sRGB: pixels are sRGB + * @IM_TYPE_YXY: pixels are CIE Yxy + * @IM_TYPE_RGB16: generic 16-bit RGB + * @IM_TYPE_GREY16: generic 16-bit mono + * + * These values are set by operations as hints to user-interfaces built on top + * of VIPS to help them show images to the user in a meaningful way. + * Operations do not use these values to decide their action. + **/ + +/** + * VipsBandFmt: + * @IM_BANDFMT_UCHAR: unsigned char format + * @IM_BANDFMT_CHAR: char format + * @IM_BANDFMT_USHORT: unsigned short format + * @IM_BANDFMT_SHORT: short format + * @IM_BANDFMT_UINT: unsigned int format + * @IM_BANDFMT_INT: int format + * @IM_BANDFMT_FLOAT: float format + * @IM_BANDFMT_COMPLEX: complex (two floats) format + * @IM_BANDFMT_DOUBLE: double float format + * @IM_BANDFMT_DPCOMPLEX: double complex (two double) format + * + * The format used for each band element. + * + * Each corresponnds to a native C type for the current machine. For example, + * %IM_BANDFMT_USHORT is unsigned short. + **/ + +/** + * VipsCoding: + * @IM_CODING_NONE: pixels are not coded + * @IM_CODING_LABQ: pixels encode 3 float CIELAB values as 4 uchar + * @IM_CODING_RAD: pixels encode 3 float RGB as 4 uchar (Radiance coding) + * + * How pixels are coded. + * + * Normally, pixels are uncoded and can be manipulated as you would expect. + * However some file formats code pixels for compression, and sometimes it's + * useful to be able to manipulate images in the coded format. + */ + +/** + * VipsProgress: + * @run: Time we have been running + * @eta: Estimated seconds of computation left + * @tpels: Number of pels we expect to calculate + * @npels: Number of pels calculated so far + * @percent: Percent complete + * @start: Start time + * + * A structure available to eval callbacks giving information on evaluation + * progress. See im_add_eval_callback(). + */ + +/** + * VipsImage: + * @Xsize: image width, in pixels + * @Ysize: image height, in pixels + * @Bands: number of image bands + * @BandFmt: #VipsFormat describing the pixel type + * @Coding: #VipsCoding describing the pixel coding type + * @Type: a #VipsType hinting how the image pixels should be interpreted + * @Xres: horizontal pixels per millimetre + * @Yres: vertical pixels per millimetre + * @Xoffset: a hint giving the position of the origin in the image + * @Yoffset: a hint giving the position of the origin in the image + * @filename: the disc file associated with this image, or %NULL + * @data: the pixel data associated with this image, or %NULL + * @time: the evaluation progress associated with this image, or %NULL + * @kill: set this to non-zero to block evaluation of this image + * + * An image. These can represent an image on disc, a memory buffer, an image + * in the process of being written to disc or a partially evaluated image + * in memory. + */ + +/** + * IM_IMAGE_SIZEOF_ELEMENT: + * @I: a #VipsImage + * + * Returns: sizeof() a band element. + **/ + +/** + * IM_IMAGE_SIZEOF_PEL: + * @I: a #VipsImage + * + * Returns: sizeof() a pixel. + **/ + +/** + * IM_IMAGE_SIZEOF_LINE: + * @I: a #VipsImage + * + * Returns: sizeof() a scanline of pixels. + **/ + +/** + * IM_IMAGE_N_ELEMENTS: + * @I: a #VipsImage + * + * Returns: the number of band elements in a scanline. + **/ + +/** + * IM_IMAGE_ADDR: + * @I: a #VipsImage + * @X: x coordinate + * @Y: y coordinate + * + * This macro returns a pointer to a pixel in an image. It only works for + * images which are fully available in memory, so memory buffers and small + * mapped images only. + * + * If DEBUG is defined, you get a version that checks bounds for you. + * + * Returns: the address of pixel (x,y) in the image. + **/ + +/* 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*/ + +/* Our XML namespace. + */ +#define NAMESPACE "http://www.vips.ecs.soton.ac.uk/vips" + +/* 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. Shared with im_binfile(). + */ +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_RAD: + case IM_CODING_NONE: + psize = (gint64) IM_IMAGE_SIZEOF_LINE( im ) * im->Ysize; + break; + + default: + psize = im->Length; + break; + } + + return( psize + im->sizeof_header ); +} + +/* 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; + + /* Always write the magic number MSB first. + */ + magic = im_amiMSBfirst() ? IM_MAGIC_SPARC : IM_MAGIC_INTEL; + to[0] = magic >> 24; + to[1] = magic >> 16; + to[2] = magic >> 8; + to[3] = magic; + q = to + 4; + + 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 ); +} + +/* 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 ) != (ssize_t) length ) { + im_free( buf ); + im_error( "im_readhist", "%s", _( "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 psize; + + psize = im__image_pixel_length( im ); + g_assert( im->file_length > 0 ); + + return( im->file_length - psize > 0 ); +} + +/* Read everything after the pixels into memory. + */ +void * +im__read_extension_block( IMAGE *im, int *size ) +{ + gint64 psize; + void *buf; + + psize = im__image_pixel_length( im ); + g_assert( im->file_length > 0 ); + if( im->file_length - psize > 10 * 1024 * 1024 ) { + im_error( "im_readhist", + "%s", _( "more than a 10 megabytes of XML? " + "sufferin' succotash!" ) ); + return( NULL ); + } + if( im->file_length - psize == 0 ) + return( NULL ); + if( !(buf = read_chunk( im->fd, psize, im->file_length - psize )) ) + return( NULL ); + if( size ) + *size = im->file_length - psize; + +#ifdef DEBUG + printf( "im__read_extension_block: read %d bytes from %s\n", + (int) (im->file_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", + "%s", _( "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", + "%s", _( "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_typeof( 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. + */ +static int +im__readhist( IMAGE *im ) +{ + /* Junk any old xml meta. + */ + if( im_header_get_typeof( 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", "%s", + _( "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", + "%s", _( "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", "%s", _( "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", "%s", _( "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", "%s", _( "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 ); +} + +/* Open the filename, read the header, some sanity checking. + */ +static 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 psize; + gint64 rsize; + + 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( (rsize = im_file_length( image->fd )) == -1 ) + return( -1 ); + image->file_length = rsize; + if( psize > rsize ) + im_warn( "im_openin", _( "unable to read data for \"%s\", %s" ), + image->filename, _( "file has been truncated" ) ); + + /* 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. This is old and deprecated API, use im_vips_open() + * in preference. + */ +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 ); + + /* Make sure we can map the whole thing without running over the VM + * limit or running out of file. + */ + size = (gint64) IM_IMAGE_SIZEOF_LINE( image ) * image->Ysize + + image->sizeof_header; + if( size < im__mmap_limit && + (gint64) image->file_length >= size ) { + 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. This is old and deprecated API, use + * im_vips_open() in preference. + */ +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 ); +} + +/* Open a VIPS image for reading and byte-swap the image data if necessary. A + * ":w" at the end of the filename means we open read-write. + */ +IMAGE * +im_open_vips( const char *filename ) +{ + char name[FILENAME_MAX]; + char mode[FILENAME_MAX]; + IMAGE *im; + + im_filename_split( filename, name, mode ); + + if( !(im = im_init( name )) ) + return( NULL ); + if( mode[0] == 'w' ) { + 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_vips", "%s", + _( "open for read-write for " + "native format images only" ) ); + return( NULL ); + } + } + else { + if( im_openin( im ) ) { + im_close( im ); + return( NULL ); + } + } + + /* Not in native format? + */ + if( im_isMSBfirst( im ) != im_amiMSBfirst() ) { + /* Does it need swapping? + */ + switch( im->Coding ) { + case IM_CODING_LABQ: + case IM_CODING_RAD: + break; + + case IM_CODING_NONE: + if( im->BandFmt != IM_BANDFMT_CHAR && + im->BandFmt != IM_BANDFMT_UCHAR ) { + IMAGE *im2; + + /* Needs swapping :( make a little pipeline up + * to do this for us. + */ + if( !(im2 = im_open( filename, "p" )) ) { + im_close( im ); + 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 ); + } + im = im2; + } + break; + + default: + im_close( im ); + im_error( "im_open", "%s", _( "unknown coding type" ) ); + return( NULL ); + } + } + + return( im ); +} + +IMAGE * +im_openout( const char *filename ) +{ + IMAGE *image; + + if( !(image = im_init( filename )) ) + return( NULL ); + image->dtype = IM_OPENOUT; + + return( image ); +} + diff --git a/libvips/iofuncs/im_partial.c b/libvips/iofuncs/im_partial.c new file mode 100644 index 00000000..d0492abc --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_piocheck.c b/libvips/iofuncs/im_piocheck.c new file mode 100644 index 00000000..5e4b63ea --- /dev/null +++ b/libvips/iofuncs/im_piocheck.c @@ -0,0 +1,196 @@ +/* @(#) 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 ) +{ + g_assert( !im_image_sanity( im ) ); + +#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/libvips/iofuncs/im_prepare.c b/libvips/iofuncs/im_prepare.c new file mode 100644 index 00000000..7aa751a9 --- /dev/null +++ b/libvips/iofuncs/im_prepare.c @@ -0,0 +1,388 @@ +/* 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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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/libvips/iofuncs/im_printdesc.c b/libvips/iofuncs/im_printdesc.c new file mode 100644 index 00000000..f8960965 --- /dev/null +++ b/libvips/iofuncs/im_printdesc.c @@ -0,0 +1,355 @@ +/* @(#) 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", + "RGB_COMPRESSED", + "LUM_COMPRESSED", + "IM_CODING_RAD" +}; + +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", "%s", _( 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( "generate function attached\n" ); + if( image->closefns ) + printf( "close callbacks attached\n" ); + if( image->evalfns ) + printf( "eval callbacks attached\n" ); + if( image->evalendfns ) + printf( "evalend callbacks attached\n" ); + if( image->evalstartfns ) + printf( "evalstart callbacks attached\n" ); + if( image->preclosefns ) + printf( "preclose callbacks attached\n" ); + if( image->invalidatefns ) + printf( "invalidate 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/libvips/iofuncs/im_printlines.c b/libvips/iofuncs/im_printlines.c new file mode 100644 index 00000000..4f2c5caf --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_render.c b/libvips/iofuncs/im_render.c new file mode 100644 index 00000000..224e4cd1 --- /dev/null +++ b/libvips/iofuncs/im_render.c @@ -0,0 +1,1096 @@ +/* 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 + * 14/3/08 + * - oop, still making fade threads even when not fading + * - more instrumenting + * 23/4/08 + * - oop, broken for mask == NULL + * 5/3/09 + * - remove all the fading stuff, a bit useless and it adds + * complexity + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_PAINT +#define DEBUG_TG +#define DEBUG_MAKE + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ + +#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*/ + +#ifdef DEBUG_TG +static int threadgroup_count = 0; +static int threadgroup_active = 0; +#endif /*DEBUG_TG*/ + +/* 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 /* 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 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 */ + + /* 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 ); + + 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 ); + + g_mutex_free( render->ref_count_lock ); + g_mutex_free( render->dirty_lock ); + g_mutex_free( render->read_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 and fill with + * pixels. + */ +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 ); + threadgroup_count += 1; + printf( "render_paint_tile: %d\n", threadgroup_count ); + threadgroup_active += 1; + printf( "render_dirty_put: %d active\n", + threadgroup_active ); +#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*/ + + /* All done. + */ + tile->state = TILE_PAINTED; + im__region_no_ownership( tile->region ); + if( render->notify ) + render->notify( render->out, + &tile->area, render->client ); + } +} + +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 ); + threadgroup_active -= 1; + printf( "render_dirty_put: %d active\n", threadgroup_active ); +#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()) ) { + /* Loop here if this is the only dirty render, rather + * than bouncing back to _put()/_get(). We don't + * lock before testing since this is just a trivial + * optimisation and does not affect integrity. + */ + do { + render_dirty_process( render ); + } while( !render_dirty_all && render->dirty ); + + 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", + "%s", _( "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*/ + +static Render * +render_new( IMAGE *in, IMAGE *out, IMAGE *mask, + int width, int height, int max, + 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->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->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 ); + } + + 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 ); + + 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. + */ + 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 ) { +#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, void *a, void *b ) +{ + Render *render = (Render *) a; + 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. + */ +static void +tile_paint_mask( Tile *tile, REGION *to ) +{ + int mask = tile->state == TILE_PAINTED ? 255 : 0; + + 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; + + 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, void *a, void *b ) +{ + Render *render = (Render *) a; + 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 ); +} + +/* fps and steps are there for backwards compat only and no longer do + * anything. + */ +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 ) { + im_error( "im_render", "%s", _( "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 ) ) + return( -1 ); + if( mask && + im_demand_hint( mask, IM_SMALLTILE, in, NULL ) ) + return( -1 ); + + if( !(render = render_new( in, out, mask, + width, height, max, 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/libvips/iofuncs/im_setbox.c b/libvips/iofuncs/im_setbox.c new file mode 100644 index 00000000..1ba8fde5 --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_setbuf.c b/libvips/iofuncs/im_setbuf.c new file mode 100644 index 00000000..43f3b091 --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_setupout.c b/libvips/iofuncs/im_setupout.c new file mode 100644 index 00000000..fefcc883 --- /dev/null +++ b/libvips/iofuncs/im_setupout.c @@ -0,0 +1,163 @@ +/* @(#) + * @(#) 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 ) +{ + g_assert( !im_image_sanity( im ) ); + + if( im->Xsize <= 0 || im->Ysize <= 0 || im->Bands <= 0 ) { + im_error( "im_setupout", + "%s", _( "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", + "%s", _( "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", + "%s", _( "bad image descriptor" ) ); + return( -1 ); + } + + return( 0 ); +} diff --git a/libvips/iofuncs/im_unmapfile.c b/libvips/iofuncs/im_unmapfile.c new file mode 100644 index 00000000..1c5c6df2 --- /dev/null +++ b/libvips/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/libvips/iofuncs/im_updatehist.c b/libvips/iofuncs/im_updatehist.c new file mode 100644 index 00000000..20244c68 --- /dev/null +++ b/libvips/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]; + VipsBuf buf; + + vips_buf_init_static( &buf, txt, IM_MAX_LINE ); + vips_buf_appends( &buf, name ); + + for( i = 0; i < argc; i++ ) { + vips_buf_appends( &buf, " " ); + vips_buf_appends( &buf, argv[i] ); + } + + if( im_histlin( out, "%s", vips_buf_all( &buf ) ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/iofuncs/im_wbuffer.c b/libvips/iofuncs/im_wbuffer.c new file mode 100644 index 00000000..3bc984dc --- /dev/null +++ b/libvips/iofuncs/im_wbuffer.c @@ -0,0 +1,428 @@ +/* Double-buffered write. + * + * 2/11/07 + * - cut from im_generate + * 7/11/07 + * - trigger start/end eval callbacks + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* 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 */ + im_wbuffer_fn write_fn; /* BG write with this */ + void *a; /* Client data */ + void *b; +} 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 ) +{ + wbuffer->write_errno = wbuffer->write_fn( wbuffer->region, + &wbuffer->area, wbuffer->a, wbuffer->b ); + +#ifdef DEBUG + printf( "wbuffer_write: %d bytes from wbuffer %p\n", + wbuffer->region->bpl * wbuffer->area.height, wbuffer ); +#endif /*DEBUG*/ +} + +#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, im_wbuffer_fn write_fn, void *a, void *b ) +{ + 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; + wbuffer->write_fn = write_fn; + wbuffer->a = a; + wbuffer->b = b; + + 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", "%s", _( "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 + printf( "wbuffer_fill: starting for wbuffer %p at line %d\n", + wbuffer, area->top ); +#endif /*DEBUG*/ + + 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 + printf( "wbuffer_fill: starting for tile at %d x %d\n", + x, y ); +#endif /*DEBUG*/ + + /* 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 and + * check for errors. + */ + if( im__handle_eval( tg->im, tg->pw, tg->ph ) || + 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 + int nstrips; + + nstrips = 0; + printf( "wbuffer_eval_to_file: partial image output to file\n" ); +#endif /*DEBUG*/ + + /* 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", + "%s", _( "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 + nstrips++; +#endif /*DEBUG*/ + } + + /* 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", "%s", _( "write failed" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "wbuffer_eval_to_file: success! %d strips written\n", nstrips ); +#endif /*DEBUG*/ + + return( 0 ); +} + +int +im_wbuffer( im_threadgroup_t *tg, + im_wbuffer_fn write_fn, void *a, void *b ) +{ + WriteBuffer *b1, *b2; + int result; + + if( im__start_eval( tg->im ) ) + return( -1 ); + + result = 0; + + b1 = wbuffer_new( tg, write_fn, a, b ); + b2 = wbuffer_new( tg, write_fn, a, b ); + + if( !b1 || !b2 || wbuffer_eval_to_file( b1, b2 ) ) + result = -1; + + im__end_eval( tg->im ); + wbuffer_free( b1 ); + wbuffer_free( b2 ); + + return( result ); +} diff --git a/libvips/iofuncs/im_wrapmany.c b/libvips/iofuncs/im_wrapmany.c new file mode 100644 index 00000000..f250a5f0 --- /dev/null +++ b/libvips/iofuncs/im_wrapmany.c @@ -0,0 +1,259 @@ +/* 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 + * 23/1/08 + * - do im_wrapone() in terms of this + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 */ +} Bundle; + +/* Maximum number of input images -- why not? + */ +#define IM_MAX_INPUT_IMAGES (64) + +/* Convert a REGION. + */ +static int +process_region( REGION *or, void *seq, void *a, void *b ) +{ + REGION **ir = (REGION **) seq; + Bundle *bun = (Bundle *) b; + + 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 ) +{ + Bundle *bun = IM_NEW( out, Bundle ); + int i, n; + + /* Count input images. + */ + for( n = 0; in[n]; n++ ) + ; + if( n >= IM_MAX_INPUT_IMAGES - 1 ) { + im_error( "im_wrapmany", "%s", _( "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", + "%s", _( "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 ); +} + +static void +wrapone_gen( void **ins, void *out, int width, Bundle *bun, void *dummy ) +{ + ((im_wrapone_fn) (bun->fn)) (ins[0], out, width, bun->a, bun->b ); +} + +int +im_wrapone( IMAGE *in, IMAGE *out, im_wrapone_fn fn, void *a, void *b ) +{ + Bundle *bun = IM_NEW( out, Bundle ); + IMAGE *invec[2]; + + /* Heh, yuk. We cast back above. + */ + bun->fn = (im_wrapmany_fn) fn; + bun->a = a; + bun->b = b; + invec[0] = in; invec[1] = NULL; + + return( im_wrapmany( invec, out, + (im_wrapmany_fn) wrapone_gen, bun, NULL ) ); +} + +/* + + commented out for now ... replace im_wraptwo with this? + +static void +wraptwo_gen( void **ins, void *out, int width, Bundle *bun, void *dummy ) +{ + ((im_wraptwo_fn) (bun->fn)) (ins[0], ins[1], or, + width, bun->a, bun->b ); +} + +int +im_wraptwo( IMAGE *in1, IMAGE *in2, IMAGE *out, + im_wraptwo_fn fn, void *a, void *b ) +{ + Bundle *bun = IM_NEW( out, Bundle ); + IMAGE *invec[3]; + + bun->fn = (im_wrapmany_fn) fn; + bun->a = a; + bun->b = b; + invec[0] = in1; invec[1] = in2; invec[2] = NULL; + + return( im_wrapmany( invec, out, + (im_wrapmany_fn) wraptwo_gen, bun, NULL ) ); +} + + */ diff --git a/libvips/iofuncs/im_wraptwo.c b/libvips/iofuncs/im_wraptwo.c new file mode 100644 index 00000000..df4166e8 --- /dev/null +++ b/libvips/iofuncs/im_wraptwo.c @@ -0,0 +1,103 @@ +/* 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +typedef struct { + im_wraptwo_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, void *seq, void *unrequired, void *b ) +{ + if( im_prepare_many( (REGION**)seq, & or-> valid )) + return -1; + { + void *out= IM_REGION_ADDR_TOPLEFT( or ); + void *in1= IM_REGION_ADDR( ((REGION**)seq)[0], or-> valid. left, or-> valid. top ); + void *in2= IM_REGION_ADDR( ((REGION**)seq)[1], or-> valid. left, or-> valid. top ); + size_t out_skip= IM_REGION_LSKIP( or ); + size_t in1_skip= IM_REGION_LSKIP( ((REGION**)seq)[0] ); + size_t in2_skip= IM_REGION_LSKIP( ((REGION**)seq)[1] ); + void *out_stop= out + out_skip * or-> valid. height; + + for( ; out < out_stop; out+= out_skip, in1+= in1_skip, in2+= in2_skip ) + ((UserBundle*) b)-> fn( in1, in2, out, or-> valid. width, ((UserBundle*) b)-> a, ((UserBundle*) b)-> b ); + + return 0; + } +} + +/* Wrap up as a partial. + */ +int +im_wraptwo( IMAGE *in1, IMAGE *in2, IMAGE *out, im_wraptwo_fn fn, void *a, void *b ) +{ + if( im_pincheck( in1 ) || im_pincheck( in2 ) || im_poutcheck( out )) + return -1; + { + UserBundle *bun= IM_NEW( out, UserBundle ); + IMAGE **ins= im_allocate_input_array( out, in1, in2, NULL ); + + if( ! bun || ! ins ) + return -1; + + bun-> fn= fn; + bun-> a= a; + bun-> b= b; + + return im_demand_hint( out, IM_THINSTRIP, in1, in2, NULL ) + || im_generate( out, im_start_many, process_region, im_stop_many, (void*) ins, (void*) bun ); + } +} diff --git a/libvips/iofuncs/im_writeline.c b/libvips/iofuncs/im_writeline.c new file mode 100644 index 00000000..6212dbf2 --- /dev/null +++ b/libvips/iofuncs/im_writeline.c @@ -0,0 +1,128 @@ +/* @(#) 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 + * 7/11/07 + * - add eval start/stop + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 *im, PEL *linebuffer ) +{ + int linesize = IM_IMAGE_SIZEOF_LINE( im ); + char *tmp; + + /* Is this the start of eval? + */ + if( ypos == 0 ) + im__start_eval( im ); + + /* Possible cases for output: FILE or SETBUF. + */ + switch( im->dtype ) { + case IM_SETBUF: + case IM_SETBUF_FOREIGN: + tmp = im->data + ypos * linesize; + memcpy( tmp, linebuffer, linesize ); + + break; + + case IM_OPENOUT: + /* Don't use ypos for this. + */ + if( im__write( im->fd, linebuffer, linesize ) ) + return( -1 ); + + break; + + default: + im_error( "im_writeline", + _( "unable to output to a %s image" ), + im_dtype2char( im->dtype ) ); + return( -1 ); + } + + /* Trigger evaluation callbacks for this image. + */ + if( im__handle_eval( im, im->Xsize, 1 ) ) + return( -1 ); + if( im__test_kill( im ) ) + return( -1 ); + + /* Is this the end of eval? + */ + if( ypos == im->Ysize - 1 ) + im__end_eval( im ); + + return( 0 ); +} diff --git a/libvips/iofuncs/memory.c b/libvips/iofuncs/memory.c new file mode 100644 index 00000000..2b9bd068 --- /dev/null +++ b/libvips/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/libvips/iofuncs/meta.c b/libvips/iofuncs/meta.c new file mode 100644 index 00000000..9a529f3a --- /dev/null +++ b/libvips/iofuncs/meta.c @@ -0,0 +1,942 @@ +/* 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 + * 24/1/07 + * - oop, im_save_string_setf() was leaking + * 26/8/08 + * - added string <-> refstring transforms + * 29/8/09 + * - im_meta_get_type() renamed as im_meta_get_typeof() to prevent + * confusion with GObject-style type definers + */ + +/* + + 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 + +#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_typeof( 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 ); + g_free( 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 and back. + */ +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 ) ); +} + +static void +transform_g_string_ref_string( const GValue *src_value, GValue *dest_value ) +{ + im_ref_string_set( dest_value, g_value_get_string( 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( G_TYPE_STRING, type, + transform_g_string_ref_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/libvips/iofuncs/object.c b/libvips/iofuncs/object.c new file mode 100644 index 00000000..4cf840e9 --- /dev/null +++ b/libvips/iofuncs/object.c @@ -0,0 +1,1046 @@ +/* abstract base class for all vips objects + * + * Edited from nip's base class, 15/10/08 + */ + +/* + + 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Properties. + */ +enum { + PROP_NICKNAME = 1,/* Class properties as object props */ + PROP_DESCRIPTION, + PROP_LAST +}; + +G_DEFINE_ABSTRACT_TYPE( VipsObject, vips_object, G_TYPE_OBJECT ); + +int +vips_object_build( VipsObject *object ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); + +#ifdef DEBUG + printf( "vips_object_build: " ); + vips_object_print( object ); +#endif /*DEBUG*/ + + return( object_class->build( object ) ); +} + +void +vips_object_print_class( VipsObjectClass *class ) +{ + VipsBuf buf; + char str[1000]; + + vips_buf_init_static( &buf, str, 1000 ); + class->print_class( class, &buf ); + printf( "%s\n", vips_buf_all( &buf ) ); +} + +void +vips_object_print( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsBuf buf; + char str[1000]; + + vips_object_print_class( class ); + vips_buf_init_static( &buf, str, 1000 ); + class->print( object, &buf ); + printf( "\n%s (%p)\n", vips_buf_all( &buf ), object ); +} + +/* Extra stuff we track for properties to do our argument handling. + */ + +/* Free a VipsArgumentInstance ... VipsArgumentClass can just be g_free()d. + */ +static void +vips_argument_instance_free( VipsArgumentInstance *argument_instance ) +{ + if( argument_instance->destroy_id ) { + g_signal_handler_disconnect( argument_instance->object, + argument_instance->destroy_id ); + argument_instance->destroy_id = 0; + } + g_free( argument_instance ); +} + +VipsArgument * +vips__argument_table_lookup( VipsArgumentTable *table, GParamSpec *pspec ) +{ + return( g_hash_table_lookup( table, pspec ) ); +} + +static void +vips_argument_table_replace( VipsArgumentTable *table, VipsArgument *argument ) +{ + g_hash_table_replace( table, argument->pspec, argument ); +} + +static void +vips_argument_table_destroy( VipsArgumentTable *table ) +{ + g_hash_table_destroy( table ); +} + +/* Loop over the vips_arguments to an object. + */ +void * +vips_argument_map( VipsObject *object, + VipsArgumentMapFn fn, void *a, void *b ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + GSList *p; + + for( p = class->argument_table_traverse; p; p = p->next ) { + VipsArgumentClass *argument_class = + (VipsArgumentClass *) p->data; + VipsArgument *argument = (VipsArgument *) argument_class; + GParamSpec *pspec = argument->pspec; + VipsArgumentInstance *argument_instance = + vips__argument_get_instance( argument_class, object ); + + /* We have many props on the arg table ... filter out the ones + * for this class. + */ + if( g_object_class_find_property( G_OBJECT_CLASS( class ), + pspec->name ) == pspec ) { + void *result; + + if( (result = fn( object, pspec, + argument_class, argument_instance, a, b )) ) + return( result ); + } + } + + return( NULL ); +} + +static void * +vips_argument_init2( VipsObject *object, GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance, + void *a, void *b ) +{ + VipsArgument *argument; + +#ifdef DEBUG + printf( "vips_argument_init_sub: adding instance argument for %s\n", + pspec->name ); +#endif /*DEBUG*/ + + /* argument_instance should be NULL since we've not set it yet. + */ + g_assert( argument_instance == NULL ); + + argument_instance = g_new( VipsArgumentInstance, 1 ); + argument = (VipsArgument *) argument_instance; + + argument->pspec = ((VipsArgument *) argument_class)->pspec; + argument_instance->object = object; + argument_instance->assigned = FALSE; + argument_instance->destroy_id = 0; + + vips_argument_table_replace( object->argument_table, argument ); + + return( NULL ); +} + +/* Create a VipsArgumentInstance for each installed argument property. Ideally + * we'd do this during _init() but g_object_class_find_property() does not seem + * to work then :-( so we have to delay it until first access. See + * vips__argument_get_instance(). + */ +static void +vips_argument_init( VipsObject *object ) +{ + if( !object->argument_table ) { + object->argument_table = g_hash_table_new_full( g_direct_hash, + g_direct_equal, NULL, + (GDestroyNotify) vips_argument_instance_free ); + + /* Make a VipsArgumentInstance for each installed argument + * property. + */ + vips_argument_map( object, vips_argument_init2, NULL, NULL ); + } +} + +/* Convenience ... given the VipsArgumentClass, get the VipsArgumentInstance. + */ +VipsArgumentInstance * +vips__argument_get_instance( VipsArgumentClass *argument_class, + VipsObject *object ) +{ + /* Make sure the instance args are built. + */ + vips_argument_init( object ); + + return( (VipsArgumentInstance *) + vips__argument_table_lookup( object->argument_table, + ((VipsArgument *) argument_class)->pspec ) ); +} + +static void +vips_object_clear_object( VipsObject *object, GParamSpec *pspec ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsArgumentClass *argument_class = (VipsArgumentClass *) + vips__argument_table_lookup( class->argument_table, pspec ); + VipsArgumentInstance *argument_instance = + vips__argument_get_instance( argument_class, object ); + GObject **member = &G_STRUCT_MEMBER( GObject *, object, + argument_class->offset ); + + if( *member ) { + if( argument_class->flags & VIPS_ARGUMENT_INPUT ) { +#ifdef DEBUG_REF + printf( "vips_object_clear_object: vips object: " ); + vips_object_print( object ); + printf( " no longer refers to gobject %s (%p)\n", + G_OBJECT_TYPE_NAME( *member ), *member ); + printf( " count down to %d\n", + G_OBJECT( *member )->ref_count - 1 ); +#endif /*DEBUG_REF*/ + + /* We reffed the object. + */ + g_object_unref( *member ); + } + else if( argument_class->flags & VIPS_ARGUMENT_OUTPUT ) { +#ifdef DEBUG_REF + printf( "vips_object_clear_object: gobject %s (%p)\n", + G_OBJECT_TYPE_NAME( *member ), *member ); + printf( " no longer refers to vips object: " ); + vips_object_print( object ); + printf( " count down to %d\n", + G_OBJECT( object )->ref_count - 1 ); +#endif /*DEBUG_REF*/ + + /* The object reffed us. Stop listening link to the + * object's "destroy" signal. We can come here from + * object being destroyed, in which case the handler + * will already have been disconnected for us. + */ + if( g_signal_handler_is_connected( object, + argument_instance->destroy_id ) ) + g_signal_handler_disconnect( object, + argument_instance->destroy_id ); + argument_instance->destroy_id = 0; + *member = NULL; + + g_object_unref( object ); + } + + *member = NULL; + } +} + +/* Free any args which are holding resources. + */ +static void * +vips_object_dispose_argument( VipsObject *object, GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance, + void *a, void *b ) +{ +#ifdef DEBUG + printf( "vips_object_dispose_argument: " ); + vips_object_print( object ); + printf( ".%s\n", pspec->name ); +#endif /*DEBUG*/ + + g_assert( ((VipsArgument *) argument_class)->pspec == pspec ); + g_assert( ((VipsArgument *) argument_instance)->pspec == pspec ); + + if( G_IS_PARAM_SPEC_STRING( pspec ) ) { + char **member = &G_STRUCT_MEMBER( char *, object, + argument_class->offset ); + + IM_FREE( *member ); + } + else if( G_IS_PARAM_SPEC_OBJECT( pspec ) ) + vips_object_clear_object( object, pspec ); + else if( G_IS_PARAM_SPEC_BOXED( pspec ) ) { + gpointer *member = &G_STRUCT_MEMBER( gpointer, object, + argument_class->offset ); + + if( *member ) { + g_boxed_free( G_PARAM_SPEC_VALUE_TYPE( pspec ), + *member ); + *member = NULL; + } + } + + return( NULL ); +} + +static void +vips_object_dispose( GObject *gobject ) +{ + VipsObject *object = VIPS_OBJECT( gobject ); + +#ifdef DEBUG + printf( "vips_object_dispose: " ); + vips_object_print( object ); +#endif /*DEBUG*/ + + /* Clear all our arguments: they may be holding refs we should drop. + */ + vips_argument_map( object, vips_object_dispose_argument, NULL, NULL ); + + G_OBJECT_CLASS( vips_object_parent_class )->dispose( gobject ); +} + +static void +vips_object_finalize( GObject *gobject ) +{ + VipsObject *object = VIPS_OBJECT( gobject ); + +#ifdef DEBUG + printf( "vips_object_finalize: " ); + vips_object_print( object ); +#endif /*DEBUG*/ + + IM_FREEF( vips_argument_table_destroy, object->argument_table ); + + G_OBJECT_CLASS( vips_object_parent_class )->finalize( gobject ); +} + +static void +vips_object_arg_destroy( GObject *argument, + VipsArgumentInstance *argument_instance ) +{ + VipsObject *object = argument_instance->object; + GParamSpec *pspec = ((VipsArgument *) argument_instance)->pspec; + + /* Argument had reffed us ... now it's being destroyed, so we unref. + */ + vips_object_clear_object( object, pspec ); +} + +static void +vips_object_set_object( VipsObject *object, GParamSpec *pspec, + GObject *argument ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsArgumentClass *argument_class = (VipsArgumentClass *) + vips__argument_table_lookup( class->argument_table, pspec ); + VipsArgumentInstance *argument_instance = + vips__argument_get_instance( argument_class, object ); + GObject **member = &G_STRUCT_MEMBER( GObject *, object, + argument_class->offset ); + + g_assert( !*member ); + + *member = argument; + + if( *member ) { + if( argument_class->flags & VIPS_ARGUMENT_INPUT ) { +#ifdef DEBUG_REF + printf( "vips_object_set_object: vips object: " ); + vips_object_print( object ); + printf( " refers to gobject %s (%p)\n", + G_OBJECT_TYPE_NAME( *member ), *member ); + printf( " count up to %d\n", + G_OBJECT( *member )->ref_count ); +#endif /*DEBUG_REF*/ + + /* Ref the argument. + */ + g_object_ref( *member ); + } + else if( argument_class->flags & VIPS_ARGUMENT_OUTPUT ) { +#ifdef DEBUG_REF + printf( "vips_object_set_object: gobject %s (%p)\n", + G_OBJECT_TYPE_NAME( *member ), *member ); + printf( " refers to vips object: " ); + vips_object_print( object ); + printf( " count up to %d\n", + G_OBJECT (object)->ref_count ); +#endif /*DEBUG_REF*/ + + /* The argument reffs us. + */ + g_object_ref( object ); + argument_instance->destroy_id = + g_signal_connect( *member, "destroy", + G_CALLBACK( vips_object_arg_destroy ), + argument_instance ); + } + } +} + +/* Also used by subclasses, so not static. + */ +void +vips_object_set_property( GObject *gobject, + guint property_id, const GValue *value, GParamSpec *pspec ) +{ + VipsObject *object = VIPS_OBJECT( gobject ); + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gobject ); + VipsArgumentClass *argument_class = (VipsArgumentClass *) + vips__argument_table_lookup( class->argument_table, pspec ); + VipsArgumentInstance *argument_instance = + vips__argument_get_instance( argument_class, object ); + + if( !argument_class ) { + G_OBJECT_WARN_INVALID_PROPERTY_ID( gobject, + property_id, pspec ); + return; + } + +#ifdef DEBUG +{ + char *str_value; + + str_value = g_strdup_value_contents( value ); + printf( "vips_object_set_property: " ); + vips_object_print( object ); + printf( ".%s = %s\n", g_param_spec_get_name( pspec ), str_value ); + g_free( str_value ); +} +#endif /*DEBUG*/ + + g_assert( ((VipsArgument *) argument_class)->pspec == pspec ); + g_assert( ((VipsArgument *) argument_instance)->pspec == pspec ); + + /* If this is a construct-only argument, we can only set before we've + * built. + */ + if( argument_class->flags & VIPS_ARGUMENT_CONSTRUCT && + object->constructed ) { + g_warning( "%s: %s can't assign '%s' after construct", + G_STRLOC, + G_OBJECT_TYPE_NAME( gobject ), + g_param_spec_get_name( pspec ) ); + return; + } + + /* If this is a set-once argument, check we've not set it before. + */ + if( argument_class->flags & VIPS_ARGUMENT_SET_ONCE && + argument_instance->assigned ) { + g_warning( "%s: %s can only assign '%s' once", + G_STRLOC, + G_OBJECT_TYPE_NAME( gobject ), + g_param_spec_get_name( pspec ) ); + return; + } + + if( G_IS_PARAM_SPEC_STRING( pspec ) ) { + char **member = &G_STRUCT_MEMBER( char *, object, + argument_class->offset ); + + IM_SETSTR( *member, g_value_get_string( value ) ); + } + else if( G_IS_PARAM_SPEC_OBJECT( pspec ) ) { + /* Remove any old object. + */ + vips_object_clear_object( object, pspec ); + + /* Install the new object. + */ + vips_object_set_object( object, pspec, + g_value_get_object( value ) ); + } + else if( G_IS_PARAM_SPEC_INT( pspec ) ) { + int *member = &G_STRUCT_MEMBER( int, object, + argument_class->offset ); + + *member = g_value_get_int( value ); + } + else if( G_IS_PARAM_SPEC_BOOLEAN( pspec ) ) { + gboolean *member = &G_STRUCT_MEMBER( gboolean, object, + argument_class->offset ); + + *member = g_value_get_boolean( value ); + } + else if( G_IS_PARAM_SPEC_ENUM( pspec ) ) { + int *member = &G_STRUCT_MEMBER( int, object, + argument_class->offset ); + + *member = g_value_get_enum( value ); + } + else if( G_IS_PARAM_SPEC_POINTER( pspec ) ) { + gpointer *member = &G_STRUCT_MEMBER( gpointer, object, + argument_class->offset ); + + *member = g_value_get_pointer( value ); + } + else if( G_IS_PARAM_SPEC_DOUBLE( pspec ) ) { + double *member = &G_STRUCT_MEMBER( double, object, + argument_class->offset ); + + *member = g_value_get_double( value ); + } + else if( G_IS_PARAM_SPEC_BOXED( pspec ) ) { + gpointer *member = &G_STRUCT_MEMBER( gpointer, object, + argument_class->offset ); + + if( *member ) { + g_boxed_free( G_PARAM_SPEC_VALUE_TYPE( pspec ), + *member ); + *member = NULL; + } + + /* Copy the boxed into our pointer (will use eg. + * vips__object_vector_dup ()). + */ + *member = g_value_dup_boxed( value ); + } + else { + g_warning( "%s: %s unimplemented property type %s", + G_STRLOC, + G_OBJECT_TYPE_NAME( gobject ), + g_type_name( G_PARAM_SPEC_VALUE_TYPE( pspec ) ) ); + } + + /* Note that it's now been set. + */ + argument_instance->assigned = TRUE; +} + +/* Also used by subclasses, so not static. + */ +void +vips_object_get_property( GObject *gobject, + guint property_id, GValue *value, GParamSpec *pspec ) +{ + VipsObject *object = VIPS_OBJECT( gobject ); + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gobject ); + VipsArgumentClass *argument_class = (VipsArgumentClass *) + vips__argument_table_lookup( class->argument_table, pspec ); + + if( !argument_class ) { + G_OBJECT_WARN_INVALID_PROPERTY_ID( gobject, + property_id, pspec ); + return; + } + + g_assert( ((VipsArgument *) argument_class)->pspec == pspec ); + + if( G_IS_PARAM_SPEC_STRING( pspec ) ) { + char *member = G_STRUCT_MEMBER( char *, object, + argument_class->offset ); + + g_value_set_string( value, member ); + } + else if( G_IS_PARAM_SPEC_OBJECT( pspec ) ) { + GObject **member = &G_STRUCT_MEMBER( GObject *, object, + argument_class->offset ); + + g_value_set_object( value, *member ); + } + else if( G_IS_PARAM_SPEC_INT( pspec ) ) { + int *member = &G_STRUCT_MEMBER( int, object, + argument_class->offset ); + + g_value_set_int( value, *member ); + } + else if( G_IS_PARAM_SPEC_BOOLEAN( pspec ) ) { + gboolean *member = &G_STRUCT_MEMBER( gboolean, object, + argument_class->offset ); + + g_value_set_boolean( value, *member ); + } + else if( G_IS_PARAM_SPEC_ENUM( pspec ) ) { + int *member = &G_STRUCT_MEMBER( int, object, + argument_class->offset ); + + g_value_set_enum( value, *member ); + } + else if( G_IS_PARAM_SPEC_POINTER( pspec ) ) { + gpointer *member = &G_STRUCT_MEMBER( gpointer, object, + argument_class->offset ); + + g_value_set_pointer( value, *member ); + } + else if( G_IS_PARAM_SPEC_DOUBLE( pspec ) ) { + double *member = &G_STRUCT_MEMBER( double, object, + argument_class->offset ); + + g_value_set_double( value, *member ); + } + else if( G_IS_PARAM_SPEC_BOXED( pspec ) ) { + gpointer *member = &G_STRUCT_MEMBER( gpointer, object, + argument_class->offset ); + + /* Copy the boxed into our pointer (will use eg. + * vips__object_vector_dup ()). + */ + g_value_set_boxed( value, *member ); + } + else { + g_warning( "%s: %s unimplemented property type %s", + G_STRLOC, + G_OBJECT_TYPE_NAME( gobject ), + g_type_name( G_PARAM_SPEC_VALUE_TYPE( pspec ) ) ); + } +} + +static void * +vips_object_check_required( VipsObject *object, GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance, + void *a, void *b ) +{ + int *result = (int *) a; + + if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) && + (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) && + !argument_instance->assigned ) { + im_error( "check_required", + _( "required construct param %s to %s not set" ), + g_param_spec_get_name( pspec ), + G_OBJECT_TYPE_NAME( object ) ); + *result = -1; + } + + return( NULL ); +} + +static int +vips_object_real_build( VipsObject *object ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); + + int result; + + g_assert( !object->constructed ); + + /* Check all required arguments have been supplied, don't stop on 1st + * error. + */ + result = 0; + (void) vips_argument_map( object, + vips_object_check_required, &result, NULL ); + + /* ... more checks go here. + */ + object->constructed = TRUE; + + /* It'd be nice if this just copied a pointer rather than did a + * strdup(). Set these here rather than in object_init, so that the + * class gets a chance to set them. + */ + g_object_set( object, + "nickname", object_class->nickname, + "description", object_class->description, NULL ); + + return( result ); +} + +static void +vips_object_real_print_class( VipsObjectClass *class, VipsBuf *buf ) +{ + vips_buf_appendf( buf, "%s", G_OBJECT_CLASS_NAME( class ) ); + if( class->nickname ) + vips_buf_appendf( buf, " (%s)", class->nickname ); + if( class->description ) + vips_buf_appendf( buf, ", %s", class->description ); +} + +static void +vips_object_real_print( VipsObject *object, VipsBuf *buf ) +{ + vips_buf_appendf( buf, " (%p)", object ); +} + +static void +transform_string_double( const GValue *src_value, GValue *dest_value ) +{ + g_value_set_double( dest_value, + g_ascii_strtod( g_value_get_string( src_value ), NULL ) ); +} + +static void +vips_object_class_init( VipsObjectClass *object_class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( object_class ); + + GParamSpec *pspec; + + gobject_class->dispose = vips_object_dispose; + gobject_class->finalize = vips_object_finalize; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->build = vips_object_real_build; + object_class->print_class = vips_object_real_print_class; + object_class->print = vips_object_real_print; + object_class->nickname = "object"; + object_class->description = _( "VIPS base class" ); + + /* Table of VipsArgumentClass ... we can just g_free() them. + */ + object_class->argument_table = g_hash_table_new_full( + g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_free ); + object_class->argument_table_traverse = NULL; + + /* For setting double arguments from the command-line. + */ + g_value_register_transform_func( G_TYPE_STRING, G_TYPE_DOUBLE, + transform_string_double ); + + /* Create properties. + */ + pspec = g_param_spec_string( "nickname", + _( "Nickname" ), + _( "Class nickname" ), + "", + (GParamFlags) G_PARAM_READWRITE ); + g_object_class_install_property( gobject_class, + PROP_NICKNAME, pspec ); + vips_object_class_install_argument( object_class, pspec, + VIPS_ARGUMENT_SET_ONCE, + G_STRUCT_OFFSET( VipsObject, nickname ) ); + + pspec = g_param_spec_string( "description", + _( "Description" ), + _( "Class description" ), + "", + (GParamFlags) G_PARAM_READWRITE ); + g_object_class_install_property( gobject_class, + PROP_DESCRIPTION, pspec ); + vips_object_class_install_argument( object_class, pspec, + VIPS_ARGUMENT_SET_ONCE, + G_STRUCT_OFFSET( VipsObject, description ) ); + +} + +static void +vips_object_init( VipsObject *object ) +{ +#ifdef DEBUG + printf( "vips_object_init: " ); + vips_object_print( object ); +#endif /*DEBUG*/ +} + +/* Add a vipsargument ... automate some stuff with this. + */ +void +vips_object_class_install_argument( VipsObjectClass *object_class, + GParamSpec *pspec, VipsArgumentFlags flags, guint offset ) +{ + VipsArgumentClass *argument_class = g_new( VipsArgumentClass, 1 ); + +#ifdef DEBUG + printf( "vips_object_class_install_argument: %s\n", pspec->name ); +#endif /*DEBUG*/ + + /* Must be a new one. + */ + g_assert( !vips__argument_table_lookup( object_class->argument_table, + pspec ) ); + + /* Mustn't have INPUT and OUTPUT both set. + */ + g_assert( !( + (flags & VIPS_ARGUMENT_INPUT) && + (flags & VIPS_ARGUMENT_OUTPUT)) ); + + ((VipsArgument *) argument_class)->pspec = pspec; + argument_class->object_class = object_class; + argument_class->flags = flags; + argument_class->offset = offset; + + vips_argument_table_replace( object_class->argument_table, + (VipsArgument *) argument_class ); + object_class->argument_table_traverse = g_slist_append( + object_class->argument_table_traverse, argument_class ); +} + +/* Set a named arg from a string. + */ +static int +vips_object_set_arg( VipsObject *object, const char *name, const char *value ) +{ + GValue gvalue = { 0 }; + + g_value_init( &gvalue, G_TYPE_STRING ); + g_value_set_string( &gvalue, value ); + g_object_set_property( G_OBJECT( object ), name, &gvalue ); + g_value_unset( &gvalue ); + + return( 0 ); +} + +static void * +vips_object_set_required_test( VipsObject *object, + GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance, + void *a, void *b ) +{ + if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) && + (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) && + !argument_instance->assigned ) + return( pspec ); + + return( NULL ); +} + +/* Set the first unassigned required arg to the string. + */ +static int +vips_object_set_required( VipsObject *object, const char *value ) +{ + GParamSpec *pspec; + + if( !(pspec = vips_argument_map( object, + vips_object_set_required_test, NULL, NULL )) ) { + im_error( "vips_object_set_required", + _( "no unset required arguments for %s" ), value ); + return( -1 ); + } + + if( vips_object_set_arg( object, pspec->name, value ) ) + return( -1 ); + + return( 0 ); +} + +/* Set object args from a string. We've seen the '(', we need to check for the + * closing ')' and make sure there's no extra stuff. + */ +static int +vips_object_set_args( VipsObject *object, const char *p ) +{ + VipsToken token; + char string[PATH_MAX]; + char string2[PATH_MAX]; + + do { + if( !(p = vips__token_need( p, VIPS_TOKEN_STRING, + string, PATH_MAX )) ) + return( -1 ); + + /* We have to look for a '=', ')' or a ',' to see if string is + * a param name or a value. + */ + if( !(p = vips__token_must( p, &token, string2, PATH_MAX )) ) + return( -1 ); + if( token == VIPS_TOKEN_EQUALS ) { + if( !(p = vips__token_need( p, VIPS_TOKEN_STRING, + string2, PATH_MAX )) ) + return( -1 ); + if( vips_object_set_arg( object, string, string2 ) ) + return( -1 ); + if( !(p = vips__token_must( p, &token, + string2, PATH_MAX )) ) + return( -1 ); + } + else { + if( vips_object_set_required( object, string ) ) + return( -1 ); + } + + /* Now must be a , or a ). + */ + if( token != VIPS_TOKEN_RIGHT && token != VIPS_TOKEN_COMMA ) { + im_error( "set_args", "%s", + _( "not , or ) after parameter" ) ); + return( -1 ); + } + } while( token != VIPS_TOKEN_RIGHT ); + + if( (p = vips__token_get( p, &token, string, PATH_MAX )) ) { + im_error( "set_args", "%s", + _( "extra tokens after ')'" ) ); + return( -1 ); + } + + return( 0 ); +} + +VipsObject * +vips_object_new( GType type, VipsObjectSetArguments set, void *a, void *b ) +{ + VipsObject *object; + + object = VIPS_OBJECT( g_object_new( type, NULL ) ); + + if( set && set( object, a, b ) ) { + g_object_unref( object ); + return( NULL ); + } + + if( vips_object_build( object ) ) { + g_object_unref( object ); + return( NULL ); + } + + return( object ); +} + +static void * +vips_object_new_from_string_set( VipsObject *object, void *a, void *b ) +{ + const char *p = (const char *) a; + VipsToken token; + char string[PATH_MAX]; + + if( (p = vips__token_get( p, &token, string, PATH_MAX )) ) { + if( token == VIPS_TOKEN_LEFT && + vips_object_set_args( object, p ) ) { + im_error( "object_new", "%s", + _( "bad object arguments" ) ); + return( object ); + } + } + + return( NULL ); +} + +VipsObject * +vips_object_new_from_string( const char *basename, const char *p ) +{ + char string[PATH_MAX]; + GType type; + + if( !(p = vips__token_need( p, VIPS_TOKEN_STRING, string, PATH_MAX )) || + !(type = vips_type_find( basename, string )) ) + return( NULL ); + + return( vips_object_new( type, + vips_object_new_from_string_set, (void *) p, NULL ) ); +} + +static void +vips_object_print_arg( VipsObject *object, GParamSpec *pspec, VipsBuf *buf ) +{ + GType type = G_PARAM_SPEC_VALUE_TYPE( pspec ); + const char *name = g_param_spec_get_name( pspec ); + GValue value = { 0 }; + char *str_value; + + g_value_init( &value, type ); + g_object_get_property( G_OBJECT( object ), name, &value ); + str_value = g_strdup_value_contents( &value ); + vips_buf_appends( buf, str_value ); + g_free( str_value ); + g_value_unset( &value ); + +} + +static void * +vips_object_to_string_required( VipsObject *object, + GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance, + void *a, void *b ) +{ + VipsBuf *buf = (VipsBuf *) a; + gboolean *first = (gboolean *) b; + + if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) ) { + if( *first ) { + vips_buf_appends( buf, "(" ); + *first = FALSE; + } + else { + vips_buf_appends( buf, "," ); + } + + vips_object_print_arg( object, pspec, buf ); + } + + return( NULL ); +} + +static void * +vips_object_to_string_optional( VipsObject *object, + GParamSpec *pspec, + VipsArgumentClass *argument_class, + VipsArgumentInstance *argument_instance, + void *a, void *b ) +{ + VipsBuf *buf = (VipsBuf *) a; + gboolean *first = (gboolean *) b; + + if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) && + argument_instance->assigned ) { + if( *first ) { + vips_buf_appends( buf, "(" ); + *first = FALSE; + } + else { + vips_buf_appends( buf, "," ); + } + + vips_buf_appends( buf, g_param_spec_get_name( pspec ) ); + vips_buf_appends( buf, "=" ); + vips_object_print_arg( object, pspec, buf ); + } + + return( NULL ); +} + +/* The inverse of vips_object_new_from_string(): turn an object into eg. + * "VipsInterpolateYafrsmooth(sharpening=12)". + */ +void +vips_object_to_string( VipsObject *object, VipsBuf *buf ) +{ + gboolean first; + + /* Nicknames are not guaranteed to be unique, so use the full type + * name. + */ + vips_buf_appends( buf, G_OBJECT_TYPE_NAME( object ) ); + first = TRUE; + (void) vips_argument_map( object, + vips_object_to_string_required, buf, &first ); + (void) vips_argument_map( object, + vips_object_to_string_optional, buf, &first ); + if( !first ) + vips_buf_appends( buf, ")" ); +} diff --git a/libvips/iofuncs/package.c b/libvips/iofuncs/package.c new file mode 100644 index 00000000..f27369f2 --- /dev/null +++ b/libvips/iofuncs/package.c @@ -0,0 +1,1089 @@ +/* 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 + * 20/5/08 + * - note_dependencies() does IMAGEVEC as well as IMAGE + * 5/8/08 + * - silent success in loading plugins if the dir isn't there + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_SYS_PARAM_H +#include +#endif /*HAVE_SYS_PARAM_H*/ +#include +#include +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Standard VIPS packages. + */ +extern im_package im__arithmetic; +extern im_package im__cimg; +extern im_package im__boolean; +extern im_package im__colour; +extern im_package im__conversion; +extern im_package im__convolution; +extern im_package im__format; +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__resample; +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_guess_libdir() args. + */ +static im_arg_desc guess_libdir_args[] = { + IM_INPUT_STRING( "argv0" ), + IM_INPUT_STRING( "env_name" ), + IM_OUTPUT_STRING( "LIBDIR" ) +}; + +/* Call im_guess_libdir() via arg vector. + */ +static int +guess_libdir_vec( im_object *argv ) +{ + const char *libdir = im_guess_libdir( argv[0], argv[1] ); + + if( !libdir ) { + argv[2] = NULL; + return( -1 ); + } + + argv[2] = im_strdup( NULL, libdir ); + + return( 0 ); +} + +/* Description of im_guess_libdir. + */ +static im_function guess_libdir_desc = { + "im_guess_libdir", /* Name */ + "guess library area", /* Description */ + 0, /* Flags */ + guess_libdir_vec, /* Dispatch function */ + IM_NUMBER( guess_libdir_args ), /* Size of arg list */ + guess_libdir_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_typeof() args. + */ +static im_arg_desc header_get_typeof_args[] = { + IM_INPUT_STRING( "field" ), + IM_INPUT_IMAGE( "image" ), + IM_OUTPUT_INT( "gtype" ) +}; + +/* Call im_header_get_typeof() via arg vector. + */ +static int +header_get_typeof_vec( im_object *argv ) +{ + int *out = ((int *) argv[2]); + + *out = im_header_get_typeof( (IMAGE *) argv[1], + (const char *) argv[0] ); + + return( 0 ); +} + +/* Description of im_header_get_typeof(). + */ +static im_function header_get_typeof_desc = { + "im_header_get_typeof", /* Name */ + "return field type", /* Description */ + 0, /* Flags */ + header_get_typeof_vec, /* Dispatch function */ + IM_NUMBER( header_get_typeof_args ),/* Size of arg list */ + header_get_typeof_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, + &guess_libdir_desc, + &header_get_typeof_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, +#ifdef WITH_CIMG + &im__cimg, +#endif /*WITH_CIMG*/ + &im__colour, + &im__conversion, + &im__convolution, + &im__format, + &im__freq_filt, + &im__histograms_lut, + &im__inplace, + &im__iofuncs, + &im__matrix, + &im__morphology, + &im__mosaicing, + &im__other, + &im__relational, + &im__resample, + &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", + "%s", _( "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 )) ) + /* Silent success for dir not there. + */ + return( 0 ); + + 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; + + /* Destroy 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", + "%s", _( "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", "%s", _( "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 ); +} + +/* vargv[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) ) { + if( strcmp( type->type, IM_TYPE_IMAGE ) == 0 ) { + if( region_local_image( vargv[i], vargv[j] ) ) + return( -1 ); + } + else if( strcmp( type->type, IM_TYPE_IMAGEVEC ) == 0 ) { + im_imagevec_object *iv = vargv[j]; + int k; + + for( k = 0; k < iv->n; k++ ) + if( region_local_image( vargv[i], + iv->vec[k] ) ) + 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", "%s", _( "flag not 0,1,2" ) ); + return( -1 ); + } +} diff --git a/libvips/iofuncs/predicate.c b/libvips/iofuncs/predicate.c new file mode 100644 index 00000000..c8042f83 --- /dev/null +++ b/libvips/iofuncs/predicate.c @@ -0,0 +1,358 @@ +/* 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 + * 22/5/08 + * - image format stuff broken out + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_PARAM_H +#include +#endif /*HAVE_SYS_PARAM_H*/ +#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 + +#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 ); + } +} + +/* 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 ) + n++; + + /* Should be just one set bit. + */ + if( n == 1 ) + /* Return position of bit. + */ + return( i ); + else + return( 0 ); +} + +int +im_isvips( const char *filename ) +{ + unsigned char buf[4]; + + if( im__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 ); +} diff --git a/libvips/iofuncs/rect.c b/libvips/iofuncs/rect.c new file mode 100644 index 00000000..7eb59222 --- /dev/null +++ b/libvips/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/libvips/iofuncs/region.c b/libvips/iofuncs/region.c new file mode 100644 index 00000000..b7a140df --- /dev/null +++ b/libvips/iofuncs/region.c @@ -0,0 +1,631 @@ +/* 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) + * 23/7/08 + * - added im_region_print() + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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; + + g_assert( !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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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 ); +} + +/* Handy for debug. + */ +void +im_region_print( REGION *region ) +{ + printf( "REGION: %p, ", region ); + printf( "im = %p, ", region->im ); + printf( "valid.left = %d, ", region->valid.left ); + printf( "valid.top = %d, ", region->valid.top ); + printf( "valid.width = %d, ", region->valid.width ); + printf( "valid.height = %d, ", region->valid.height ); + printf( "type = %d, ", region->type ); + printf( "data = %p, ", region->data ); + printf( "bpl = %d, ", region->bpl ); + printf( "seq = %p, ", region->seq ); + printf( "thread = %p, ", region->thread ); + printf( "window = %p, ", region->window ); + printf( "buffer = %p\n", region->buffer ); +} diff --git a/libvips/iofuncs/semaphore.c b/libvips/iofuncs/semaphore.c new file mode 100644 index 00000000..69219d6e --- /dev/null +++ b/libvips/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/libvips/iofuncs/threadgroup.c b/libvips/iofuncs/threadgroup.c new file mode 100644 index 00000000..4ff19cef --- /dev/null +++ b/libvips/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 ); + + g_assert( tg->idle ); + g_assert( g_slist_length( tg->idle ) == (unsigned int) 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", + "%s", _( "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/libvips/iofuncs/time.c b/libvips/iofuncs/time.c new file mode 100644 index 00000000..555ff08c --- /dev/null +++ b/libvips/iofuncs/time.c @@ -0,0 +1,177 @@ +/* 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 + + */ + +/* +#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*/ + +int +im__time_destroy( IMAGE *im ) +{ + if( im->time ) { +#ifdef DEBUG + printf( "im__time_destroy: %s\n", im->filename ); +#endif /*DEBUG*/ + + g_timer_destroy( im->time->start ); + im_free( im->time ); + im->time = NULL; + } + + return( 0 ); +} + +/* Attach a new time struct and fill in start values. + */ +int +time_add( IMAGE *im ) +{ + im_time_t *time; + + if( im__time_destroy( im ) || + !(time = IM_NEW( NULL, im_time_t )) ) + return( -1 ); + +#ifdef DEBUG + printf( "time_add: %s\n", im->filename ); +#endif /*DEBUG*/ + + time->im = im; + time->start = g_timer_new(); + time->run = 0; + time->eta = 0; + time->tpels = (gint64) im->Xsize * im->Ysize; + time->npels = 0; + time->percent = 0; + im->time = time; + + return( 0 ); +} + +/* A new tile has been computed. Update time_info. + */ +static int +update_time( im_time_t *time, int w, int h ) +{ + float prop; + + time->run = g_timer_elapsed( time->start, NULL ); + time->npels += w * h; + prop = (float) time->npels / (float) time->tpels; + time->percent = 100 * prop; + if( prop > 0 ) + time->eta = (1.0 / prop) * time->run - time->run; + + return( 0 ); +} + +int +im__start_eval( IMAGE *im ) +{ + g_assert( !im_image_sanity( im ) ); + + if( im->progress ) { +#ifdef DEBUG + printf( "im__start_eval: %s\n", im->filename ); +#endif /*DEBUG*/ + + g_assert( !im_image_sanity( im->progress ) ); + + if( time_add( im->progress ) ) + return( -1 ); + + if( im__trigger_callbacks( im->progress->evalstartfns ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Handle eval callbacks. w and h are the size of the tile we made this time. + * We signal progress on the ->progress IMAGE, see im_add_eval_callback(). We + * assume there's no geometry change between adding the feedback request and + * evaling the image. + */ +int +im__handle_eval( IMAGE *im, int w, int h ) +{ + if( im->progress ) { + /* Need to test ->time, it may have been shut down. + */ + if( im->progress->time ) { + if( update_time( im->progress->time, w, h ) ) + return( -1 ); + } + + if( im__trigger_callbacks( im->progress->evalfns ) ) + return( -1 ); + } + + return( 0 ); +} + +int +im__end_eval( IMAGE *im ) +{ + g_assert( !im_image_sanity( im ) ); + + if( im->progress ) { +#ifdef DEBUG + printf( "im__end_eval: %s\n", im->filename ); +#endif /*DEBUG*/ + + g_assert( !im_image_sanity( im->progress ) ); + + if( im__trigger_callbacks( im->progress->evalendfns ) ) + return( -1 ); + + im__time_destroy( im->progress ); + } + + return( 0 ); +} diff --git a/libvips/iofuncs/type.c b/libvips/iofuncs/type.c new file mode 100644 index 00000000..d3ca9e51 --- /dev/null +++ b/libvips/iofuncs/type.c @@ -0,0 +1,352 @@ +/* Define built-in VIPS 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 +#include + +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Keep types in a GHashTable, indexed by name + type_param. + */ +static GHashTable *im_type_table = NULL; + +/* Keep operations in a GHashTable, indexed by name. + */ +static GHashTable *im_operation_table = NULL; + +static unsigned int +im_type_hash( im_type_t *type ) +{ + return( g_str_hash( type->name ) | + GPOINTER_TO_UINT( type->type_param ) ); +} + +static gboolean +im_type_equal( im_type_t *type1, im_type_t *type2 ) +{ + return( type1->type_param == type2->type_param && + g_str_equal( type1->name, type2->name ) ); +} + +im_type_t * +im_type_register( const char *name, + im_type_t *type_param, size_t size ) +{ + im_type_t *type; + + if( im_type_lookup( name, type_param ) ) { + im_error( "im_type_register", + _( "type name already registered" ) ); + return( NULL ); + } + + if( !(type = IM_NEW( NULL, im_type_t )) ) + return( NULL ); + type->name = name; + type->type_param = type_param; + type->size = size; + + if( !im_type_table ) + im_type_table = g_hash_table_new( + (GHashFunc) im_type_hash, + (GEqualFunc) im_type_equal ); + g_hash_table_insert( im_type_table, type, type ); + + return( type ); +} + +void * +im_type_map( VSListMap2Fn fn, void *a, void *b ) +{ + return( im_hash_table_map( im_type_table, fn, a, b ) ); +} + +im_type_t * +im_type_lookup( const char *name, im_type_t *type_param ) +{ + im_type_t tmp; + im_type_t *type; + + /* Look for this exact type. + */ + tmp.name = name; + tmp.type_param = type_param; + if( !(type = (im_type_t *) + g_hash_table_lookup( im_type_table, &tmp )) ) { + /* Not found. Look for just the name type, eg. "array". + */ + tmp.type_param = NULL; + if( (type = (im_type_t *) + g_hash_table_lookup( im_type_table, &tmp )) ) + /* Found it. Register a new compound type with this + * param. + */ + type = im_type_register( name, type_param, type->size ); + } + + return( type ); +} + +/* Register the base VIPS types. + */ +void +im__type_init( void ) +{ + im_type_register( IM_TYPE_NAME_DOUBLE, NULL, sizeof( double ) ); + im_type_register( IM_TYPE_NAME_INT, NULL, sizeof( int ) ); + im_type_register( IM_TYPE_NAME_COMPLEX, NULL, 2 * sizeof( double ) ); + im_type_register( IM_TYPE_NAME_STRING, NULL, 0 ); + im_type_register( IM_TYPE_NAME_IMASK, NULL, sizeof( im_value_mask_t ) ); + im_type_register( IM_TYPE_NAME_DMASK, NULL, sizeof( im_value_mask_t ) ); + im_type_register( IM_TYPE_NAME_IMAGE, NULL, 0 ); + im_type_register( IM_TYPE_NAME_DISPLAY, NULL, 0 ); + im_type_register( IM_TYPE_NAME_GVALUE, NULL, sizeof( GValue ) ); + im_type_register( IM_TYPE_NAME_ARRAY, + NULL, sizeof( im_value_array_t ) ); +} + +/* Allocate an im_value_t. + */ +static im_value_t * +im_value_new( im_type_t *type ) +{ + im_value_t *value; + + if( type->size ) { + if( !(value = im_malloc( NULL, type->size )) ) + return( NULL ); + memset( value, 0, type->size ); + } + else + value = NULL; + + return( value ); +} + +/* Free an im_value_t. + */ +static void +im_value_free( im_value_t *value, im_type_t *type ) +{ + if( type->size ) + IM_FREE( value ); +} + +/* Convenience functions to build and free various values. + */ +void +im_value_imask_free( im_value_mask_t *value ) +{ + IM_FREE( value->name ); + IM_FREEF( im_free_imask, value->mask ); +} + +void +im_value_dmask_free( im_value_mask_t *value ) +{ + IM_FREE( value->name ); + IM_FREEF( im_free_dmask, value->mask ); +} + +void +im_value_gvalue_free( GValue *value ) +{ + g_value_unset( value ); +} + +void +im_value_array_free( im_value_array_t *value, im_type_t *type ) +{ + int i; + + for( i = 0; i < value->n; i++ ) + im_value_free( value->array[i], type->type_param ); +} + +gboolean +im_value_mask_output_init( im_value_mask_t *value, const char *name ) +{ + if( !(value->name = im_strdup( NULL, name )) ) + return( FALSE ); + + return( TRUE ); +} + +gboolean +im_value_imask_input_init( im_value_mask_t *value, const char *name ) +{ + INTMASK *mask; + + if( !(mask = im_read_imask( name )) ) + return( FALSE ); + value->mask = (void *) mask; + if( !(value->name = im_strdup( NULL, name )) ) { + im_value_imask_free( value ); + return( FALSE ); + } + + return( TRUE ); +} + +/* Create arguments. + */ +im_argument_t * +im_argument_new( const char *name, im_type_t *type, im_argument_flags flags ) +{ + im_argument_t *argument; + + if( !(argument = IM_NEW( NULL, im_argument_t )) ) + return( NULL ); + argument->name = name; + argument->type = type; + argument->flags = flags; + + return( argument ); +} + +void +im_argument_free( im_argument_t *argument ) +{ + im_free( argument ); +} + +/* Register/iterate/lookup operations. + */ +void +im_operation_unregister( im_operation_t *operation ) +{ + int i; + + g_hash_table_remove( im_operation_table, operation->name ); + for( i = 0; i < operation->argc; i++ ) + IM_FREEF( im_argument_free, operation->argv[i] ); + IM_FREE( operation ); +} + +im_operation_t * +im_operation_register( const char *name, const char *desc, + im_operation_flags flags, im_operation_dispatch_fn disp, int argc ) +{ + im_operation_t *operation; + + if( im_operation_lookup( name ) ) { + im_error( "im_operation_register", + _( "operation name already registered" ) ); + return( NULL ); + } + + if( !(operation = IM_NEW( NULL, im_operation_t )) ) + return( NULL ); + operation->name = name; + operation->desc = desc; + operation->flags = flags; + operation->disp = disp; + operation->argc = argc; + operation->argv = NULL; + + if( !(operation->argv = IM_ARRAY( NULL, argc, im_argument_t * )) ) { + im_operation_unregister( operation ); + return( NULL ); + } + memset( operation->argv, 0, argc * sizeof( im_argument_t * ) ); + + if( !im_operation_table ) + im_operation_table = g_hash_table_new( + g_str_hash, g_str_equal ); + g_hash_table_insert( im_operation_table, (char *) name, operation ); + + return( operation ); +} + +im_operation_t * +im_operation_registerv( const char *name, const char *desc, + im_operation_flags flags, im_operation_dispatch_fn disp, ... ) +{ + im_operation_t *operation; + im_argument_t *argument; + va_list ap; + int argc; + int i; + + va_start( ap, disp ); + for( argc = 0; va_arg( ap, im_argument_t * ); argc++ ) + ; + va_end( ap ); + + operation = im_operation_register( name, desc, flags, disp, argc ); + va_start( ap, disp ); + for( i = 0; (argument = va_arg( ap, im_argument_t * )); i++ ) + operation->argv[i] = argument; + va_end( ap ); + + return( operation ); +} + +void * +im_operation_map( VSListMap2Fn fn, void *a, void *b ) +{ + return( im_hash_table_map( im_operation_table, fn, a, b ) ); +} + +im_operation_t * +im_operation_lookup( const char *name ) +{ + return( (im_operation_t *) + g_hash_table_lookup( im_operation_table, name ) ); +} + +static int +add_vec( im_value_t **argv ) +{ + return( im_add( argv[0], argv[1], argv[2] ) ); +} + +/* Make a sample operation. + */ +void +im__operation_init( void ) +{ + im_operation_registerv( "im_add", _( "add two images" ), + IM_FN_PIO | IM_FN_PTOP, + add_vec, + im_argument_new( "in1", IM_TYPE_IM, 0 ), + im_argument_new( "in2", IM_TYPE_IM, 0 ), + im_argument_new( "out", IM_TYPE_IM, IM_ARGUMENT_OUTPUT ), + NULL ); +} diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c new file mode 100644 index 00000000..fad79985 --- /dev/null +++ b/libvips/iofuncs/util.c @@ -0,0 +1,1393 @@ +/* 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 + +#ifdef OS_WIN32 +#include +#endif /*OS_WIN32*/ + +#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 ); +} + +typedef struct { + void *a; + void *b; + VSListMap2Fn fn; + void *result; +} Pair; + +static gboolean +im_hash_table_predicate( const char *key, void *value, Pair *pair ) +{ + return( (pair->result == pair->fn( value, pair->a, pair->b )) ); +} + +/* Like slist map, but for a hash table. + */ +void * +im_hash_table_map( GHashTable *hash, VSListMap2Fn fn, void *a, void *b ) +{ + Pair pair; + + pair.a = a; + pair.b = b; + pair.fn = fn; + pair.result = NULL; + + g_hash_table_find( hash, (GHRFunc) im_hash_table_predicate, &pair ); + + return( pair.result ); +} + +/* Map over all a type's children. + */ +void * +vips_type_map( GType base, VipsTypeMap2 fn, void *a, void *b ) +{ + GType *child; + guint n_children; + unsigned int i; + void *result; + + child = g_type_children( base, &n_children ); + result = NULL; + for( i = 0; i < n_children && !result; i++ ) + result = fn( child[i], a, b ); + g_free( child ); + + return( result ); +} + +/* Loop over all the concrete subtypes of a base type. + */ +void * +vips_type_map_concrete_all( GType base, VipsTypeMap fn, void *a ) +{ + void *result; + + result = NULL; + if( !G_TYPE_IS_ABSTRACT( base ) ) + result = fn( base, a ); + if( !result ) + result = vips_type_map( base, + (VipsTypeMap2) vips_type_map_concrete_all, fn, a ); + + return( result ); +} + +/* Loop over all the subclasses of a base type. + */ +void * +vips_class_map_concrete_all( GType type, VipsClassMap fn, void *a ) +{ + void *result; + + result = NULL; + if( !G_TYPE_IS_ABSTRACT( type ) ) { + GTypeClass *class; + + /* Does this class exist? Try to create if not. + */ + if( !(class = g_type_class_peek( type )) ) + /* We don't unref, so the class is never finalized. + * This will make the peek work next time around and + * save us from constantly building and destroying + * classes. + */ + if( !(class = g_type_class_ref( type )) ) { + im_error( "vips_class_map_concrete_all", + "%s", _( "unable to build class" ) ); + return( NULL ); + } + + result = fn( VIPS_OBJECT_CLASS( class ), a ); + } + if( !result ) + result = vips_type_map( type, + (VipsTypeMap2) vips_class_map_concrete_all, fn, a ); + + return( result ); +} + +static void * +test_name( VipsObjectClass *class, const char *nickname ) +{ + if( strcasecmp( class->nickname, nickname ) == 0 ) + return( class ); + + /* Check the class name too, why not. + */ + if( strcasecmp( G_OBJECT_CLASS_NAME( class ), nickname ) == 0 ) + return( class ); + + return( NULL ); +} + +/* Find a class ... search below base, return the first match on a nickname or + * a name. + */ +VipsObjectClass * +vips_class_find( const char *basename, const char *nickname ) +{ + VipsObjectClass *class; + GType base; + + if( !(base = g_type_from_name( basename )) ) { + im_error( "vips_class_find", + _( "base class \"%s\" not found" ), basename ); + return( NULL ); + } + + if( !(class = vips_class_map_concrete_all( base, + (VipsClassMap) test_name, (void *) nickname )) ) { + im_error( "vips_class_find", + _( "class \"%s\" not found" ), nickname ); + return( NULL ); + } + + return( class ); +} + +GType +vips_type_find( const char *basename, const char *nickname ) +{ + VipsObjectClass *class; + + if( !(class = vips_class_find( "VipsInterpolate", nickname )) ) + return( 0 ); + + /* FIXME ... we've not supposed to use G_TYPE_FROM_CLASS(), I think. + * I'm not * sure what the alternative is. + */ + return( G_TYPE_FROM_CLASS( class ) ); +} + +/* 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. Include the "." character, if any. + */ +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 ); + 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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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 ); +} + +FILE * +im__file_open_read( const char *filename ) +{ + FILE *fp; + +#ifdef BINARY_OPEN + if( !(fp = fopen( filename, "rb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( filename, "r" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im__file_open_read", + _( "unable to open file \"%s\" for reading" ), + filename ); + return( NULL ); + } + + return( fp ); +} + +FILE * +im__file_open_write( const char *filename ) +{ + FILE *fp; + +#ifdef BINARY_OPEN + if( !(fp = fopen( filename, "wb" )) ) { +#else /*BINARY_OPEN*/ + if( !(fp = fopen( filename, "w" )) ) { +#endif /*BINARY_OPEN*/ + im_error( "im__file_open_write", + _( "unable to open file \"%s\" for writing" ), + filename ); + return( NULL ); + } + + return( fp ); +} + +/* Load from a filename as a string. + */ +char * +im__file_read_name( const char *name, unsigned int *length_out ) +{ + FILE *fp; + char *buffer; + + if( !(fp = im__file_open_read( 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; + } + + g_assert( (size_t) (q - all) == length ); + + return( all ); +} + +/* Need our own seek(), since lseek() on win32 can't do long files. + */ +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", + "%s", _( "unable to seek" ) ); + return( -1 ); + } +} +#else /*!OS_WIN32*/ + if( lseek( fd, pos, SEEK_SET ) == (off_t) -1 ) { + im_error( "im__seek", "%s", _( "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 + + */ +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", + "%s", _( "unable to truncate" ) ); + return( -1 ); + } +} +#else /*!OS_WIN32*/ + if( ftruncate( fd, pos ) ) { + im_error_system( errno, "im__ftruncate", + "%s", _( "unable to truncate" ) ); + return( -1 ); + } +#endif /*OS_WIN32*/ + + return( 0 ); +} + +/* Like fwrite(), but returns non-zero on error and sets error message. + */ +int +im__file_write( void *data, size_t size, size_t nmemb, FILE *stream ) +{ + size_t n; + + if( !data ) + return( 0 ); + + if( (n = fwrite( data, size, nmemb, stream )) != nmemb ) { + im_error( "im__file_write", + _( "writing error (%zd out of %zd blocks written) " + "... disc full?" ), n, nmemb ); + return( -1 ); + } + + return( 0 ); +} + +/* Check whether arch corresponds to native byte order. + */ +gboolean +im_isnative( im_arch_type arch ) +{ + switch( arch ) { + case IM_ARCH_NATIVE: + return( TRUE ); + case IM_ARCH_BYTE_SWAPPED: + return( FALSE ); + case IM_ARCH_LSB_FIRST: + return( !im_amiMSBfirst() ); + case IM_ARCH_MSB_FIRST: + return( im_amiMSBfirst() ); + + default: + g_assert( 0 ); + } +} + +/* Read a few bytes from the start of a file. For sniffing file types. + */ +int +im__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 ); +} + +/* Break a command-line argument into tokens separated by whitespace. Strings + * can't be adjacent, so "hello world" (without quotes) is a single string. + * Strings are written (with \" escaped) into string, which must be size + * characters large. + */ +const char * +vips__token_get( const char *p, VipsToken *token, char *string, int size ) +{ + const char *q; + int ch; + int n; + + /* Parse this token with p. + */ + if( !p ) + return( NULL ); + + /* Skip initial whitespace. + */ + p += strspn( p, " \t\n\r" ); + if( !p[0] ) + return( NULL ); + + switch( (ch = p[0]) ) { + case '{': + case '[': + case '(': + *token = VIPS_TOKEN_LEFT; + p += 1; + break; + + case ')': + case ']': + case '}': + *token = VIPS_TOKEN_RIGHT; + p += 1; + break; + + case '=': + *token = VIPS_TOKEN_EQUALS; + p += 1; + break; + + case ',': + *token = VIPS_TOKEN_COMMA; + p += 1; + break; + + case '"': + case '\'': + /* Parse a quoted string. Copy up to ", interpret any \", + * error if no closing ". + */ + *token = VIPS_TOKEN_STRING; + + do { + /* Number of characters until the next quote + * character or end of string. + */ + if( (q = strchr( p + 1, ch )) ) + n = q - p + 1; + else + n = strlen( p + 1 ); + + g_assert( size > n + 1 ); + memcpy( string, p + 1, n ); + string[n] = '\0'; + + /* p[n + 1] might not be " if there's no closing ". + */ + if( p[n + 1] == ch && p[n] == '\\' ) + /* An escaped ": overwrite the '\' with '"' + */ + string[n - 1] = ch; + + string += n; + size -= n; + p += n + 1; + } while( p[0] && p[-1] == '\\' ); + + p += 1; + + break; + + default: + /* It's an unquoted string: read up to the next non-string + * character. We don't allow two strings next to each other, + * so the next break must be bracket, equals, comma. + */ + *token = VIPS_TOKEN_STRING; + n = strcspn( p, "[{()}]=," ); + g_assert( size > n + 1 ); + memcpy( string, p, n ); + string[n] = '\0'; + p += n; + + /* We remove leading whitespace, so we trim trailing + * whitespace from unquoted strings too. + */ + while( isspace( string[n - 1] ) ) { + string[n - 1] = '\0'; + n -= 1; + } + + break; + } + + return( p ); +} + +/* We expect a token. + */ +const char * +vips__token_must( const char *p, VipsToken *token, + char *string, int size ) +{ + if( !(p = vips__token_get( p, token, string, size )) ) { + im_error( "get_token", "%s", _( "unexpected end of string" ) ); + return( NULL ); + } + + return( p ); +} + +/* Turn a VipsToken into a string. + */ +static const char * +vips__token_string( VipsToken token ) +{ + switch( token ) { + case VIPS_TOKEN_LEFT: return( _( "opening brace" ) ); + case VIPS_TOKEN_RIGHT: return( _( "closing brace" ) ); + case VIPS_TOKEN_STRING: return( _( "string" ) ); + case VIPS_TOKEN_EQUALS: return( "=" ); + case VIPS_TOKEN_COMMA: return( "," ); + + default: + g_assert( 0 ); + } +} + +/* We expect a certain token. + */ +const char * +vips__token_need( const char *p, VipsToken need_token, + char *string, int size ) +{ + VipsToken token; + + if( !(p = vips__token_must( p, &token, string, size )) ) + return( NULL ); + if( token != need_token ) { + im_error( "get_token", _( "expected %s, saw %s" ), + vips__token_string( need_token ), + vips__token_string( token ) ); + return( NULL ); + } + + return( p ); +} diff --git a/libvips/iofuncs/window.c b/libvips/iofuncs/window.c new file mode 100644 index 00000000..3da2a90f --- /dev/null +++ b/libvips/iofuncs/window.c @@ -0,0 +1,399 @@ +/* Manage sets of mmap buffers on an image. + * + * 30/10/06 + * - from region.c + * 19/3/09 + * - block mmaps of nodata 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 + + */ + +/* +#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; + + /* Make sure we have enough file. + */ + if( end > (gint64) window->im->file_length ) { + im_error( "im_window_set", + _( "unable to read data for \"%s\", %s" ), + window->im->filename, _( "file has been truncated" ) ); + return( -1 ); + } + + 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/libvips/matrix/Makefile.am b/libvips/matrix/Makefile.am new file mode 100644 index 00000000..6fb5d15e --- /dev/null +++ b/libvips/matrix/Makefile.am @@ -0,0 +1,11 @@ +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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/matrix/im_matcat.c b/libvips/matrix/im_matcat.c new file mode 100644 index 00000000..78e837dd --- /dev/null +++ b/libvips/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/libvips/matrix/im_matinv.c b/libvips/matrix/im_matinv.c new file mode 100644 index 00000000..cded8686 --- /dev/null +++ b/libvips/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/libvips/matrix/im_matmul.c b/libvips/matrix/im_matmul.c new file mode 100644 index 00000000..98c9eb59 --- /dev/null +++ b/libvips/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/libvips/matrix/im_mattrn.c b/libvips/matrix/im_mattrn.c new file mode 100644 index 00000000..fd6549c2 --- /dev/null +++ b/libvips/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/libvips/matrix/matalloc.c b/libvips/matrix/matalloc.c new file mode 100644 index 00000000..e1e11219 --- /dev/null +++ b/libvips/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/libvips/matrix/matrix_dispatch.c b/libvips/matrix/matrix_dispatch.c new file mode 100644 index 00000000..e6608fda --- /dev/null +++ b/libvips/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/libvips/morphology/Makefile.am b/libvips/morphology/Makefile.am new file mode 100644 index 00000000..f4e1d03a --- /dev/null +++ b/libvips/morphology/Makefile.am @@ -0,0 +1,10 @@ +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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/morphology/im_cntlines.c b/libvips/morphology/im_cntlines.c new file mode 100644 index 00000000..f02b3355 --- /dev/null +++ b/libvips/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/libvips/morphology/im_dilate.c b/libvips/morphology/im_dilate.c new file mode 100644 index 00000000..2f242d22 --- /dev/null +++ b/libvips/morphology/im_dilate.c @@ -0,0 +1,329 @@ +/* @(#) 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 + * 21/4/08 + * - only rebuild the buffer offsets if bpl changes + * - small 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 + + */ + +/* +#define DEBUG + */ + +#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 */ + int last_bpl; /* Avoid recalcing offsets, if we can */ +} SeqInfo; + +/* Stop function. + */ +static int +dilate_stop( void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Start function. + */ +static void * +dilate_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + INTMASK *msk = (INTMASK *) b; + int sz = msk->xsize * msk->ysize; + SeqInfo *seq; + + if( !(seq = IM_NEW( out, SeqInfo )) ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->soff = NULL; + seq->ss = 0; + seq->coff = NULL; + seq->cs = 0; + seq->last_bpl = -1; + + /* 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 ) { + dilate_stop( seq, in, NULL ); + return( NULL ); + } + + return( seq ); +} + +/* Dilate! + */ +static int +dilate_gen( REGION *or, void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + INTMASK *msk = (INTMASK *) b; + 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 result, 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 ); + +#ifdef DEBUG + printf( "erode_gen: preparing %dx%d pixels\n", s.width, s.height ); +#endif /*DEBUG*/ + + /* Scan mask, building offsets we check when processing. Only do this + * if the bpl has changed since the previous im_prepare(). + */ + if( seq->last_bpl != IM_REGION_LSKIP( ir ) ) { + seq->last_bpl = IM_REGION_LSKIP( ir ); + + 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_error( "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. + */ + result = 0; + for( i = 0; i < seq->ss; i++ ) + if( p[soff[i]] ) { + /* Found a match! + */ + result = 255; + break; + } + + /* No set pixels ... search for a hit in the clear + * pixels. + */ + if( !result ) + for( i = 0; i < seq->cs; i++ ) + if( !p[coff[i]] ) { + /* Found a match! + */ + result = 255; + break; + } + + *q = result; + + } + } + + 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_error( "im_dilate", "%s", _( "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_error( "im_dilate", "%s", _( "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_error( "im_dilate", "%s", _( "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, dilate_start, dilate_gen, dilate_stop, 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/libvips/morphology/im_erode.c b/libvips/morphology/im_erode.c new file mode 100644 index 00000000..3320b902 --- /dev/null +++ b/libvips/morphology/im_erode.c @@ -0,0 +1,325 @@ +/* @(#) 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 + * 21/4/08 + * - only rebuild the buffer offsets if bpl changes + * - small 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 + + */ + +/* +#define DEBUG + */ + +#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 */ + int last_bpl; /* Avoid recalcing offsets, if we can */ +} SeqInfo; + +/* Stop function. + */ +static int +erode_stop( void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + + IM_FREEF( im_region_free, seq->ir ); + + return( 0 ); +} + +/* Start function. + */ +static void * +erode_start( IMAGE *out, void *a, void *b ) +{ + IMAGE *in = (IMAGE *) a; + INTMASK *msk = (INTMASK *) b; + SeqInfo *seq; + int sz = msk->xsize * msk->ysize; + + if( !(seq = IM_NEW( out, SeqInfo )) ) + return( NULL ); + + /* Init! + */ + seq->ir = NULL; + seq->soff = NULL; + seq->ss = 0; + seq->coff = NULL; + seq->cs = 0; + seq->last_bpl = -1; + + /* 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 ) { + erode_stop( seq, in, NULL ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* Erode! + */ +static int +erode_gen( REGION *or, void *vseq, void *a, void *b ) +{ + SeqInfo *seq = (SeqInfo *) vseq; + INTMASK *msk = (INTMASK *) b; + 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 result, 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 ); + +#ifdef DEBUG + printf( "erode_gen: preparing %dx%d pixels\n", s.width, s.height ); +#endif /*DEBUG*/ + + /* Scan mask, building offsets we check when processing. Only do this + * if the bpl has changed since the previous im_prepare(). + */ + if( seq->last_bpl != IM_REGION_LSKIP( ir ) ) { + seq->last_bpl = IM_REGION_LSKIP( ir ); + + 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_error( "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. + */ + result = 255; + for( i = 0; i < seq->ss; i++ ) + if( !p[soff[i]] ) { + /* Found a mismatch! + */ + result = 0; + break; + } + + /* Check all clear pixels are clear. + */ + if( result ) + for( i = 0; i < seq->cs; i++ ) + if( p[coff[i]] ) { + result = 0; + break; + } + + *q = result; + } + } + + 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_error( "im_erode", "%s", _( "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_error( "im_erode", "%s", _( "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_error( "im_erode", "%s", _( "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, erode_start, erode_gen, erode_stop, 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/libvips/morphology/im_profile.c b/libvips/morphology/im_profile.c new file mode 100644 index 00000000..6d7dd700 --- /dev/null +++ b/libvips/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/libvips/morphology/morph_dispatch.c b/libvips/morphology/morph_dispatch.c new file mode 100644 index 00000000..86d1e5b4 --- /dev/null +++ b/libvips/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/libvips/mosaicing/Makefile.am b/libvips/mosaicing/Makefile.am new file mode 100644 index 00000000..b404ba52 --- /dev/null +++ b/libvips/mosaicing/Makefile.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES = libmosaicing.la + +libmosaicing_la_SOURCES = \ + im_align_bands.c \ + match.c \ + mosaic1.c \ + mosaicing_dispatch.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_maxpos_subpel.c \ + im_tbcalcon.c \ + im_tbmerge.c \ + im_remosaic.c \ + im_tbmosaic.c \ + merge.h \ + global_balance.h \ + mosaic.h + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/mosaicing/global_balance.c b/libvips/mosaicing/global_balance.c new file mode 100644 index 00000000..6d47ba16 --- /dev/null +++ b/libvips/mosaicing/global_balance.c @@ -0,0 +1,1750 @@ +/* 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 + +#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", "%s", _( "no matching '>'" ) ); + return( -1 ); + } + + *p = '\0'; + line = p + 1; + } + + if( i == MAX_ITEMS ) { + im_error( "break_files", "%s", _( "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_error_clear(); + } + + 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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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_close_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", "%s", _( "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/libvips/mosaicing/global_balance.h b/libvips/mosaicing/global_balance.h new file mode 100644 index 00000000..2d33d809 --- /dev/null +++ b/libvips/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/libvips/mosaicing/im_align_bands.c b/libvips/mosaicing/im_align_bands.c new file mode 100644 index 00000000..64cfe821 --- /dev/null +++ b/libvips/mosaicing/im_align_bands.c @@ -0,0 +1,87 @@ +/* + * Brute force align the bands of an image. + * + * Copyright: Nottingham Trent University + * Author: Tom Vajzovic + * Written on: 2008-02-04 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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_align_bands( IMAGE *in, IMAGE *out ){ +#define FUNCTION_NAME "im_align_bands" + if( im_piocheck( in, out )) + return -1; + + if( 1 == in-> Bands ) + return im_copy( in, out ); + { + IMAGE **bands= IM_ARRAY( out, 2 * in-> Bands, IMAGE* ); + IMAGE **wrapped_bands= bands + in-> Bands; + double x= 0.0; + double y= 0.0; + int i; + + if( ! bands || im_open_local_array( out, bands, in-> Bands, FUNCTION_NAME ": bands", "p" ) + || im_open_local_array( out, wrapped_bands + 1, in-> Bands - 1, FUNCTION_NAME ": wrapped_bands", "p" )) + return -1; + + for( i= 0; i < in-> Bands; ++i ) + if( im_extract_band( in, bands[i], i )) + return -1; + + wrapped_bands[ 0 ]= bands[0]; + + for( i= 1; i < in-> Bands; ++i ){ + IMAGE *temp= im_open( FUNCTION_NAME ": temp", "t" ); + double this_x, this_y, val; + + if( ! temp || im_phasecor_fft( bands[i-1], bands[i], temp ) + || im_maxpos_avg( temp, & this_x, & this_y, & val ) || im_close( temp )) + return -1; + + x+= this_x; + y+= this_y; + + if( im_wrap( bands[i], wrapped_bands[i], (int) x, (int) y )) + return -1; + } + return im_gbandjoin( wrapped_bands, out, in-> Bands ); + } +#undef FUNCTION_NAME +} diff --git a/libvips/mosaicing/im_avgdxdy.c b/libvips/mosaicing/im_avgdxdy.c new file mode 100644 index 00000000..e152d553 --- /dev/null +++ b/libvips/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/libvips/mosaicing/im_chkpair.c b/libvips/mosaicing/im_chkpair.c new file mode 100644 index 00000000..13547a87 --- /dev/null +++ b/libvips/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/libvips/mosaicing/im_clinear.c b/libvips/mosaicing/im_clinear.c new file mode 100644 index 00000000..0f180da7 --- /dev/null +++ b/libvips/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/libvips/mosaicing/im_improve.c b/libvips/mosaicing/im_improve.c new file mode 100644 index 00000000..3b1ca9a7 --- /dev/null +++ b/libvips/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/libvips/mosaicing/im_initialize.c b/libvips/mosaicing/im_initialize.c new file mode 100644 index 00000000..8ab66af0 --- /dev/null +++ b/libvips/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/libvips/mosaicing/im_lrcalcon.c b/libvips/mosaicing/im_lrcalcon.c new file mode 100644 index 00000000..368e0aeb --- /dev/null +++ b/libvips/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/libvips/mosaicing/im_lrmerge.c b/libvips/mosaicing/im_lrmerge.c new file mode 100644 index 00000000..e3dcd4b9 --- /dev/null +++ b/libvips/mosaicing/im_lrmerge.c @@ -0,0 +1,1138 @@ +/* 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 + +#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_error( "im_lrmerge", "%s", _( "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_error( "im_lrmerge", "%s", _( "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_error( "im_lrmerge", "%s", _( "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_error( "im_lr/tbmerge", + "%s", _( "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_error( "im_lr/tbmerge", "%s", _( "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_error( "im_lrmerge", "%s", _( "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_error( "im_lrmerge", "%s", _( "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, void *seq, void *a, void *b ) +{ + MergeInfo *inf = (MergeInfo *) seq; + Overlapping *ovlap = (Overlapping *) a; + 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( void *seq, void *a, void *b ) +{ + MergeInfo *inf = (MergeInfo *) seq; + + 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, void *a, void *b ) +{ + Overlapping *ovlap = (Overlapping *) a; + MergeInfo *inf; + + if( !(inf = IM_NEW( NULL, MergeInfo )) ) + 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, NULL, NULL ); + 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, NULL, NULL ); + 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_error( "im_lrmerge", + "%s", _( "input images incompatible" ) ); + return( -1 ); + } + if( ref->Coding != IM_CODING_NONE && ref->Coding != IM_CODING_LABQ ) { + im_error( "im_lrmerge", + "%s", _( "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/libvips/mosaicing/im_lrmosaic.c b/libvips/mosaicing/im_lrmosaic.c new file mode 100644 index 00000000..59cdde03 --- /dev/null +++ b/libvips/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/libvips/mosaicing/im_maxpos_subpel.c b/libvips/mosaicing/im_maxpos_subpel.c new file mode 100644 index 00000000..51c0c5fc --- /dev/null +++ b/libvips/mosaicing/im_maxpos_subpel.c @@ -0,0 +1,157 @@ +/* This function implements: + * "Extension of Phase Correlation to Subpixel Registration" + * by H. Foroosh, from IEEE trans. Im. Proc. 11(3), 2002. + * + * If the best three matches in the correlation are aranged: + * + * 02 or 01 + * 1 2 + * + * then we return a subpixel match using the ratio of correlations in the + * vertical and horizontal dimension. + * + * ( xs[0], ys[0] ) is the best integer alignment + * ( xs[ use_x ], ys[ use_x ] ) is equal in y and (+/-)1 off in x + * ( xs[ use_y ], ys[ use_y ] ) is equal in x and (+/-)1 off in y + * + * + * Alternatively if the best four matches in the correlation are aranged in + * a square: + * + * 01 or 03 or 02 or 03 + * 32 12 31 21 + * + * then we return a subpixel match weighting with the sum the two on each + * side over the sum of all four, but only if all four of them are very + * close to the best, and the fifth is nowhere near. + * + * This alternative method is not described by Foroosh, but is often the + * case where the match is close to n-and-a-half pixels in both dimensions. + * + * Copyright: 2008, Nottingham Trent University + * + * Author: Tom Vajzovic + * + * Written on: 2008-02-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 + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H */ +#include + +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC */ + + +#define MOST_OF( A, B ) ( (A) > 0.9 * (B) ) +#define LITTLE_OF( A, B ) ( (B) < 0.1 * (B) ) + +int im_maxpos_subpel( IMAGE *in, double *x, double *y ){ +#define FUNCTION_NAME "im_maxpos_subpel" + + int xs[5]; + int ys[5]; + double vals[5]; + int xa, ya, xb, yb; + double vxa, vya, vxb, vyb; + + if( im_maxpos_vec( in, xs, ys, vals, 5 )) + return -1; + +#define WRAP_TEST_RETURN() \ + \ + /* wrap around if we have alignment -1 < d <= 0 */ \ + /* (change it to: size - 1 <= d < size ) */ \ + \ + if( ! xa && in-> Xsize - 1 == xb ) \ + xa= in-> Xsize; \ + \ + else if( ! xb && in-> Xsize - 1 == xa ) \ + xb= in-> Xsize; \ + \ + if( ! ya && in-> Ysize - 1 == yb ) \ + ya= in-> Ysize; \ + \ + else if( ! yb && in-> Ysize - 1 == ya ) \ + yb= in-> Ysize; \ + \ + if( 1 == abs( xb - xa ) && 1 == abs( yb - ya )){ \ + *x= ((double)xa) + ((double)( xb - xa )) * ( vxb / ( vxa + vxb )); \ + *y= ((double)ya) + ((double)( yb - ya )) * ( vyb / ( vya + vyb )); \ + return 0; \ + } + +#define TEST3( A, B ) \ + if( xs[0] == xs[A] && ys[0] == ys[B] ){ \ + xa= xs[0]; \ + ya= ys[0]; \ + xb= xs[B]; \ + yb= ys[A]; \ + vxa= vals[0]; \ + vya= vals[0]; \ + vxb= vals[B]; \ + vyb= vals[A]; \ + WRAP_TEST_RETURN() \ + } + + TEST3( 1, 2 ) + TEST3( 2, 1 ) + + if( MOST_OF( vals[1], vals[0] ) && MOST_OF( vals[2], vals[0] ) + && MOST_OF( vals[3], vals[0] ) && LITTLE_OF( vals[4], vals[0] )){ + +#define TEST4( A, B, C, D, E, F, G, H ) \ + if( xs[A] == xs[B] && xs[C] == xs[D] && ys[E] == ys[F] && ys[G] == ys[H] ){ \ + xa= xs[A]; \ + xb= xs[C]; \ + ya= ys[E]; \ + yb= ys[G]; \ + vxa= vals[A] + vals[B]; \ + vxb= vals[C] + vals[D]; \ + vya= vals[E] + vals[F]; \ + vyb= vals[G] + vals[H]; \ + WRAP_TEST_RETURN() \ + } + + TEST4( 0, 3, 1, 2, 0, 1, 2, 3 ) + TEST4( 0, 1, 2, 3, 0, 3, 1, 2 ) + TEST4( 0, 3, 1, 2, 0, 2, 1, 3 ) + TEST4( 0, 2, 1, 3, 0, 3, 1, 2 ) + } + + im_warn( FUNCTION_NAME, "registration performed to nearest pixel only: correlation does not have the expected distribution for sub-pixel registration" ); + *x= (double) xs[0]; + *y= (double) ys[0]; + return 0; +} diff --git a/libvips/mosaicing/im_remosaic.c b/libvips/mosaicing/im_remosaic.c new file mode 100644 index 00000000..6bbf86e8 --- /dev/null +++ b/libvips/mosaicing/im_remosaic.c @@ -0,0 +1,137 @@ +/* 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 + +#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/libvips/mosaicing/im_tbcalcon.c b/libvips/mosaicing/im_tbcalcon.c new file mode 100644 index 00000000..b444fa43 --- /dev/null +++ b/libvips/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/libvips/mosaicing/im_tbmerge.c b/libvips/mosaicing/im_tbmerge.c new file mode 100644 index 00000000..42cf85f7 --- /dev/null +++ b/libvips/mosaicing/im_tbmerge.c @@ -0,0 +1,734 @@ +/* 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 + +#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", "%s", _( "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", "%s", _( "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", "%s", _( "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", "%s", _( "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", "%s", _( "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", + "%s", _( "input images incompatible" ) ); + return( -1 ); + } + if( ref->Coding != IM_CODING_NONE && ref->Coding != IM_CODING_LABQ ) { + im_error( "im_tbmerge", + "%s", _( "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/libvips/mosaicing/im_tbmosaic.c b/libvips/mosaicing/im_tbmosaic.c new file mode 100644 index 00000000..75a423d6 --- /dev/null +++ b/libvips/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/libvips/mosaicing/match.c b/libvips/mosaicing/match.c new file mode 100644 index 00000000..24b38788 --- /dev/null +++ b/libvips/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/libvips/mosaicing/merge.h b/libvips/mosaicing/merge.h new file mode 100644 index 00000000..bf980d51 --- /dev/null +++ b/libvips/mosaicing/merge.h @@ -0,0 +1,111 @@ +/* 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 + +#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" ); + VipsBuf 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. + */ + vips_buf_init_static( &buf, text, 1024 ); + vips_buf_appendf( &buf, "#LRROTSCALE <%s> <%s> <%s> <", + ref->filename, sec->filename, out->filename ); + vips_buf_appendg( &buf, a ); + vips_buf_appendf( &buf, "> <" ); + vips_buf_appendg( &buf, b ); + vips_buf_appendf( &buf, "> <" ); + vips_buf_appendg( &buf, dx ); + vips_buf_appendf( &buf, "> <" ); + vips_buf_appendg( &buf, dy ); + vips_buf_appendf( &buf, "> <%d>", mwidth ); + if( im_histlin( out, "%s", vips_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" ); + VipsBuf 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. + */ + vips_buf_init_static( &buf, text, 1024 ); + vips_buf_appendf( &buf, "#TBROTSCALE <%s> <%s> <%s> <", + ref->filename, sec->filename, out->filename ); + vips_buf_appendg( &buf, a ); + vips_buf_appendf( &buf, "> <" ); + vips_buf_appendg( &buf, b ); + vips_buf_appendf( &buf, "> <" ); + vips_buf_appendg( &buf, dx ); + vips_buf_appendf( &buf, "> <" ); + vips_buf_appendg( &buf, dy ); + vips_buf_appendf( &buf, "> <%d>", mwidth ); + if( im_histlin( out, "%s", vips_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_point( &trn, xs1, ys1, &xs3, &ys3 ); + im__transform_forward_point( &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_invert_point( &trn, xs5, ys5, &xs7, &ys7 ); + im__transform_invert_point( &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/libvips/mosaicing/mosaicing_dispatch.c b/libvips/mosaicing/mosaicing_dispatch.c new file mode 100644 index 00000000..777c878f --- /dev/null +++ b/libvips/mosaicing/mosaicing_dispatch.c @@ -0,0 +1,760 @@ +/* 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 +#include + +#include "merge.h" + +#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 */ +}; + +/* 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 */ +}; + +/* 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 */ +}; + +static int align_bands_vec( im_object *argv ){ + return im_align_bands( (IMAGE*)argv[0], (IMAGE*)argv[1] ); +} + +static im_arg_desc align_bands_arg_types[]= { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +static im_function align_bands_desc= { + "im_align_bands", + "align the bands of an image", + 0, + align_bands_vec, + IM_NUMBER( align_bands_arg_types ), + align_bands_arg_types +}; + +static int maxpos_subpel_vec( im_object *argv ){ + return im_maxpos_subpel( (IMAGE*)argv[0], (double*)argv[1], (double*)argv[2] ); +} + +static im_arg_desc maxpos_subpel_arg_types[]= { + IM_INPUT_IMAGE( "im" ), + IM_OUTPUT_DOUBLE( "x" ), + IM_OUTPUT_DOUBLE( "y" ) +}; + +static im_function maxpos_subpel_desc= { + "im_maxpos_subpel", + "subpixel position of maximum of (phase correlation) image", + IM_FN_PIO, + maxpos_subpel_vec, + IM_NUMBER( maxpos_subpel_arg_types ), + maxpos_subpel_arg_types +}; + +/* Package up all these functions. + */ +static im_function *mos_list[] = { + &align_bands_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, + &maxpos_subpel_desc, + &remosaic_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/libvips/other/Makefile.am b/libvips/other/Makefile.am new file mode 100644 index 00000000..d5dc6088 --- /dev/null +++ b/libvips/other/Makefile.am @@ -0,0 +1,18 @@ +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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/other/cooc_funcs.c b/libvips/other/cooc_funcs.c new file mode 100644 index 00000000..5f3d9f41 --- /dev/null +++ b/libvips/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/libvips/other/glds_funcs.c b/libvips/other/glds_funcs.c new file mode 100644 index 00000000..51b82efd --- /dev/null +++ b/libvips/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/libvips/other/im_benchmark.c b/libvips/other/im_benchmark.c new file mode 100644 index 00000000..bd337013 --- /dev/null +++ b/libvips/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/libvips/other/im_dif_std.c b/libvips/other/im_dif_std.c new file mode 100644 index 00000000..60b1e57c --- /dev/null +++ b/libvips/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/libvips/other/im_grey.c b/libvips/other/im_grey.c new file mode 100644 index 00000000..388b1c2a --- /dev/null +++ b/libvips/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, void *seq, void *a, void *b ) +{ + 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/libvips/other/im_make_xy.c b/libvips/other/im_make_xy.c new file mode 100644 index 00000000..705ed5a2 --- /dev/null +++ b/libvips/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, void *seq, void *a, void *b ) +{ + 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/libvips/other/im_meanstd.c b/libvips/other/im_meanstd.c new file mode 100644 index 00000000..2edbfeff --- /dev/null +++ b/libvips/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/libvips/other/im_spatres.c b/libvips/other/im_spatres.c new file mode 100644 index 00000000..f8535f15 --- /dev/null +++ b/libvips/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/libvips/other/im_zone.c b/libvips/other/im_zone.c new file mode 100644 index 00000000..59477461 --- /dev/null +++ b/libvips/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/libvips/other/other_dispatch.c b/libvips/other/other_dispatch.c new file mode 100644 index 00000000..33bf5f5b --- /dev/null +++ b/libvips/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/libvips/relational/Makefile.am b/libvips/relational/Makefile.am new file mode 100644 index 00000000..9eca7265 --- /dev/null +++ b/libvips/relational/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = librelational.la + +librelational_la_SOURCES = \ + im_ifthenelse.c \ + im_blend.c \ + relational.c \ + relational_dispatch.c + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/relational/im_blend.c b/libvips/relational/im_blend.c new file mode 100644 index 00000000..b7639fb7 --- /dev/null +++ b/libvips/relational/im_blend.c @@ -0,0 +1,366 @@ +/* @(#) 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, void *seq, void *client1, void *client2 ) +{ + REGION **ir = (REGION **) seq; + 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", "%s", _( "images not uncoded" ) ); + return( -1 ); + } + if( a->BandFmt != b->BandFmt || + a->Bands != b->Bands ) { + im_error( "im_blend", + "%s", _( "size and format of then and else " + "must match" ) ); + return( -1 ); + } + if( c->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_blend", + "%s", _( "conditional image must be uchar" ) ); + return( -1 ); + } + if( c->Bands != 1 && c->Bands != a->Bands ) { + im_error( "im_blend", + "%s", _( "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/libvips/relational/im_ifthenelse.c b/libvips/relational/im_ifthenelse.c new file mode 100644 index 00000000..9eb01cb4 --- /dev/null +++ b/libvips/relational/im_ifthenelse.c @@ -0,0 +1,213 @@ +/* @(#) 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, void *seq, void *client1, void *client2 ) +{ + REGION **ir = (REGION **) seq; + 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", + "%s", _( "then image must be uncoded or labpack" ) ); + return( -1 ); + } + if( b->Coding != IM_CODING_NONE && b->Coding != IM_CODING_LABQ ) { + im_error( "im_ifthenelse", + "%s", _( "else image must be uncoded or labpack" ) ); + return( -1 ); + } + if( c->Coding != IM_CODING_NONE ) { + im_error( "im_ifthenelse", + "%s", _( "condition image must be uncoded" ) ); + return( -1 ); + } + if( a->BandFmt != b->BandFmt || + a->Bands != b->Bands ) { + im_error( "im_ifthenelse", + "%s", _( "size and format of then and else " + "must match" ) ); + return( -1 ); + } + if( c->BandFmt != IM_BANDFMT_UCHAR ) { + im_error( "im_ifthenelse", + "%s", _( "conditional image must be uchar" ) ); + return( -1 ); + } + if( c->Bands != 1 && c->Bands != a->Bands ) { + im_error( "im_ifthenelse", + "%s", _( "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/libvips/relational/relational.c b/libvips/relational/relational.c new file mode 100644 index 00000000..ec9c7542 --- /dev/null +++ b/libvips/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/libvips/relational/relational_dispatch.c b/libvips/relational/relational_dispatch.c new file mode 100644 index 00000000..b57c5b86 --- /dev/null +++ b/libvips/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/libvips/resample/Makefile.am b/libvips/resample/Makefile.am new file mode 100644 index 00000000..e041942c --- /dev/null +++ b/libvips/resample/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libresample.la + +libresample_la_SOURCES = \ + im_affine.c \ + bicubic.cpp \ + similarity.c \ + interpolate.c \ + yafrsmooth.cpp \ + nohalo1.cpp \ + snohalo1.cpp \ + nohalo2.cpp \ + templates.h \ + transform.c \ + resample_dispatch.c + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/resample/bicubic.cpp b/libvips/resample/bicubic.cpp new file mode 100644 index 00000000..691ac92c --- /dev/null +++ b/libvips/resample/bicubic.cpp @@ -0,0 +1,457 @@ +/* bicubic ... catmull-rom interpolator + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Bicubic (catmull-rom) interpolator derived from Nicolas Robidoux's YAFR + * resampler with permission and thanks. + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "templates.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define VIPS_TYPE_INTERPOLATE_BICUBIC \ + (vips_interpolate_bicubic_get_type()) +#define VIPS_INTERPOLATE_BICUBIC( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE_BICUBIC, VipsInterpolateBicubic )) +#define VIPS_INTERPOLATE_BICUBIC_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE_BICUBIC, VipsInterpolateBicubicClass)) +#define VIPS_IS_INTERPOLATE_BICUBIC( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_BICUBIC )) +#define VIPS_IS_INTERPOLATE_BICUBIC_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_BICUBIC )) +#define VIPS_INTERPOLATE_BICUBIC_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE_BICUBIC, VipsInterpolateBicubicClass )) + +typedef struct _VipsInterpolateBicubic { + VipsInterpolate parent_object; + +} VipsInterpolateBicubic; + +typedef struct _VipsInterpolateBicubicClass { + VipsInterpolateClass parent_class; + + /* Precalculated 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. + */ + + /* We could keep a large set of 2d 4x4 matricies, but this actually + * works out slower, since for many resizes the thing will no longer + * fit in L1. + */ + int matrixi[VIPS_TRANSFORM_SCALE + 1][4]; + double matrixf[VIPS_TRANSFORM_SCALE + 1][4]; +} VipsInterpolateBicubicClass; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsInterpolateBicubic, vips_interpolate_bicubic, + VIPS_TYPE_INTERPOLATE ); +} + +/* Pointers to write to / read from, number of bands, + * how many bytes to add to move down a line. + */ + +/* T is the type of pixels we are reading and writing. + */ + +/* Fixed-point version, for 8 and 16-bit types. + */ +template +static void inline +bicubic_int_tab( PEL *pout, const PEL *pin, + const int bands, const int lskip, + const int *cx, const int *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + const int l1_plus_b1 = l1 + b1; + const int l1_plus_b2 = l1 + b2; + const int l1_plus_b3 = l1 + b3; + const int l2_plus_b1 = l2 + b1; + const int l2_plus_b2 = l2 + b2; + const int l2_plus_b3 = l2 + b3; + const int l3_plus_b1 = l3 + b1; + const int l3_plus_b2 = l3 + b2; + const int l3_plus_b3 = l3 + b3; + + for( int z = 0; z < bands; z++ ) { + const T uno_one = in[0]; + const T uno_two = in[b1]; + const T uno_thr = in[b2]; + const T uno_fou = in[b3]; + + const T dos_one = in[l1]; + const T dos_two = in[l1_plus_b1]; + const T dos_thr = in[l1_plus_b2]; + const T dos_fou = in[l1_plus_b3]; + + const T tre_one = in[l2]; + const T tre_two = in[l2_plus_b1]; + const T tre_thr = in[l2_plus_b2]; + const T tre_fou = in[l2_plus_b3]; + + const T qua_one = in[l3]; + const T qua_two = in[l3_plus_b1]; + const T qua_thr = in[l3_plus_b2]; + const T qua_fou = in[l3_plus_b3]; + + int bicubic = bicubic_int( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cx, cy ); + + if( bicubic < min_value ) + bicubic = min_value; + else if( bicubic > max_value ) + bicubic = max_value; + + out[z] = bicubic; + + in += 1; + } +} + +/* Floating-point version, for int/float types. + */ +template +static void inline +bicubic_float_tab( PEL *pout, const PEL *pin, + const int bands, const int lskip, + const double *cx, const double *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + const int l1_plus_b1 = l1 + b1; + const int l1_plus_b2 = l1 + b2; + const int l1_plus_b3 = l1 + b3; + const int l2_plus_b1 = l2 + b1; + const int l2_plus_b2 = l2 + b2; + const int l2_plus_b3 = l2 + b3; + const int l3_plus_b1 = l3 + b1; + const int l3_plus_b2 = l3 + b2; + const int l3_plus_b3 = l3 + b3; + + for( int z = 0; z < bands; z++ ) { + const T uno_one = in[0]; + const T uno_two = in[b1]; + const T uno_thr = in[b2]; + const T uno_fou = in[b3]; + + const T dos_one = in[l1]; + const T dos_two = in[l1_plus_b1]; + const T dos_thr = in[l1_plus_b2]; + const T dos_fou = in[l1_plus_b3]; + + const T tre_one = in[l2]; + const T tre_two = in[l2_plus_b1]; + const T tre_thr = in[l2_plus_b2]; + const T tre_fou = in[l2_plus_b3]; + + const T qua_one = in[l3]; + const T qua_two = in[l3_plus_b1]; + const T qua_thr = in[l3_plus_b2]; + const T qua_fou = in[l3_plus_b3]; + + const T bicubic = bicubic_float( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cx, cy ); + + out[z] = bicubic; + + in += 1; + } +} + +/* Ultra-high-quality version for double images. + */ +template +static void inline +bicubic_notab( PEL *pout, const PEL *pin, + const int bands, const int lskip, + double x, double y ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + const int l1_plus_b1 = l1 + b1; + const int l1_plus_b2 = l1 + b2; + const int l1_plus_b3 = l1 + b3; + const int l2_plus_b1 = l2 + b1; + const int l2_plus_b2 = l2 + b2; + const int l2_plus_b3 = l2 + b3; + const int l3_plus_b1 = l3 + b1; + const int l3_plus_b2 = l3 + b2; + const int l3_plus_b3 = l3 + b3; + + double cx[4]; + double cy[4]; + + calculate_coefficients_catmull( x, cx ); + calculate_coefficients_catmull( y, cy ); + + for( int z = 0; z < bands; z++ ) { + const T uno_one = in[0]; + const T uno_two = in[b1]; + const T uno_thr = in[b2]; + const T uno_fou = in[b3]; + + const T dos_one = in[l1]; + const T dos_two = in[l1_plus_b1]; + const T dos_thr = in[l1_plus_b2]; + const T dos_fou = in[l1_plus_b3]; + + const T tre_one = in[l2]; + const T tre_two = in[l2_plus_b1]; + const T tre_thr = in[l2_plus_b2]; + const T tre_fou = in[l2_plus_b3]; + + const T qua_one = in[l3]; + const T qua_two = in[l3_plus_b1]; + const T qua_thr = in[l3_plus_b2]; + const T qua_fou = in[l3_plus_b3]; + + const T bicubic = bicubic_float( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cx, cy ); + + out[z] = bicubic; + + in += 1; + } +} + +static void +vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate, + PEL *out, REGION *in, double x, double y ) +{ + VipsInterpolateBicubicClass *bicubic_class = + VIPS_INTERPOLATE_BICUBIC_GET_CLASS( interpolate ); + + /* Scaled int. + */ + const double sx = x * VIPS_TRANSFORM_SCALE; + const double sy = y * VIPS_TRANSFORM_SCALE; + const int sxi = FAST_PSEUDO_FLOOR( sx ); + const int syi = FAST_PSEUDO_FLOOR( sy ); + + /* Get index into interpolation table and unscaled integer + * position. + */ + const int tx = sxi & (VIPS_TRANSFORM_SCALE - 1); + const int ty = syi & (VIPS_TRANSFORM_SCALE - 1); + const int ix = sxi >> VIPS_TRANSFORM_SHIFT; + const int iy = syi >> VIPS_TRANSFORM_SHIFT; + + /* Look up the tables we need. + */ + const int *cxi = bicubic_class->matrixi[tx]; + const int *cyi = bicubic_class->matrixi[ty]; + const double *cxf = bicubic_class->matrixf[tx]; + const double *cyf = bicubic_class->matrixf[ty]; + + /* Back and up one to get the top-left of the 4x4. + */ + const PEL *p = (PEL *) IM_REGION_ADDR( in, ix - 1, iy - 1 ); + + /* Pel size and line size. + */ + const int bands = in->im->Bands; + const int lskip = IM_REGION_LSKIP( in ); + +#ifdef DEBUG + printf( "vips_interpolate_bicubic_interpolate: %g %g\n", x, y ); + printf( "\tleft=%d, top=%d, width=%d, height=%d\n", + ix - 1, iy - 1, 4, 4 ); +#endif /*DEBUG*/ + + switch( in->im->BandFmt ) { + case IM_BANDFMT_UCHAR: + bicubic_int_tab( + out, p, bands, lskip, + cxi, cyi ); + /* + + Handy for benchmarking + + bicubic_float_tab( + out, p, bands, lskip, + cxf, cyf ); + bicubic_notab( + out, p, bands, lskip, + x - ix, y - iy ); + + */ + break; + + case IM_BANDFMT_CHAR: + bicubic_int_tab( + out, p, bands, lskip, + cxi, cyi ); + break; + + case IM_BANDFMT_USHORT: + bicubic_int_tab( + out, p, bands, lskip, + cxi, cyi ); + break; + + case IM_BANDFMT_SHORT: + bicubic_int_tab( + out, p, bands, lskip, + cxi, cyi ); + break; + + case IM_BANDFMT_UINT: + bicubic_float_tab( out, p, bands, lskip, + cxf, cyf ); + break; + + case IM_BANDFMT_INT: + bicubic_float_tab( out, p, bands, lskip, + cxf, cyf ); + break; + + case IM_BANDFMT_FLOAT: + bicubic_float_tab( out, p, bands, lskip, + cxf, cyf ); + break; + + case IM_BANDFMT_DOUBLE: + bicubic_notab( out, p, bands, lskip, + x - ix, y - iy ); + break; + + case IM_BANDFMT_COMPLEX: + bicubic_float_tab( out, p, bands * 2, lskip, + cxf, cyf ); + break; + + case IM_BANDFMT_DPCOMPLEX: + bicubic_notab( out, p, bands * 2, lskip, + x - ix, y - iy ); + break; + + default: + break; + } +} + +static void +vips_interpolate_bicubic_class_init( VipsInterpolateBicubicClass *iclass ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( iclass ); + VipsInterpolateClass *interpolate_class = + VIPS_INTERPOLATE_CLASS( iclass ); + + object_class->nickname = "bicubic"; + object_class->description = _( "Bicubic interpolation (Catmull-Rom)" ); + + interpolate_class->interpolate = vips_interpolate_bicubic_interpolate; + interpolate_class->window_size = 4; + + /* Build the tables of pre-computed coefficients. + */ + for( int x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) { + calculate_coefficients_catmull( + (float) x / VIPS_TRANSFORM_SCALE, + iclass->matrixf[x] ); + + for( int i = 0; i < 4; i++ ) + iclass->matrixi[x][i] = + iclass->matrixf[x][i] * VIPS_INTERPOLATE_SCALE; + } +} + +static void +vips_interpolate_bicubic_init( VipsInterpolateBicubic *bicubic ) +{ +#ifdef DEBUG + printf( "vips_interpolate_bicubic_init: " ); + vips_object_print( VIPS_OBJECT( bicubic ) ); +#endif /*DEBUG*/ + +} + diff --git a/libvips/resample/im_affine.c b/libvips/resample/im_affine.c new file mode 100644 index 00000000..66eaea4c --- /dev/null +++ b/libvips/resample/im_affine.c @@ -0,0 +1,520 @@ +/* @(#) im_affine() ... affine transform with a supplied interpolator. + * @(#) + * @(#) int im_affinei(in, out, interpolate, a, b, c, d, dx, dy, w, h, x, y) + * @(#) + * @(#) IMAGE *in, *out; + * @(#) VipsInterpolate *interpolate; + * @(#) 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 + * 20/10/08 + * - version with interpolate parameter, from im_affine() + * 30/10/08 + * - allow complex image types + * 4/11/08 + * - take an interpolator as a param + * - replace im_affine with this, provide an im_affine() compat wrapper + * - break transform stuff out to transform.c + * - revise clipping / transform stuff, again + * - now do corner rather than centre: this way the identity transform + * returns the input exactly + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* "fast" floor() ... on my laptop, anyway. + */ +#define FLOOR( V ) ((V) >= 0 ? (int)(V) : (int)((V) - 1)) + +/* Per-call state. + */ +typedef struct _Affine { + IMAGE *in; + IMAGE *out; + VipsInterpolate *interpolate; + Transformation trn; +} Affine; + +static int +affine_free( Affine *affine ) +{ + IM_FREEF( g_object_unref, affine->interpolate ); + + return( 0 ); +} + +/* We have five (!!) coordinate systems. Working forward through them, there + * are: + * + * 1. The original input image + * + * 2. This is embedded in a larger image to provide borders for the + * interpolator. iarea->left/top give the offset. These are the coordinates we + * pass to IM_REGION_ADDR()/im_prepare() for the input image. + * + * 3. We need point (0, 0) in (1) to be at (0, 0) for the transformation. So + * shift everything up and left to make the displaced input image. This is the + * space that the transformation maps from, and can have negative pixels + * (up and left of the image, for interpolation). + * + * 4. Output transform space. This is the where the transform maps to. Pixels + * can be negative, since a rotated image can go up and left of the origin. + * + * 5. Output image space. This is the wh of the xywh passed to im_affine() + * below. These are the coordinates we pass to IM_REGION_ADDR() for the + * output image, and that affinei_gen() is asked for. + */ + +static int +affinei_gen( REGION *or, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + const IMAGE *in = (IMAGE *) a; + const Affine *affine = (Affine *) b; + const int window_size = + vips_interpolate_get_window_size( affine->interpolate ); + const int half_window_size = window_size / 2; + const VipsInterpolateMethod interpolate = + vips_interpolate_get_method( affine->interpolate ); + + /* Area we generate in the output image. + */ + const Rect *r = &or->valid; + const int le = r->left; + const int ri = IM_RECT_RIGHT( r ); + const int to = r->top; + const int bo = IM_RECT_BOTTOM( r ); + + const Rect *iarea = &affine->trn.iarea; + const Rect *oarea = &affine->trn.oarea; + + int ps = IM_IMAGE_SIZEOF_PEL( in ); + int x, y, z; + + Rect image, want, need, clipped; + +#ifdef DEBUG + printf( "affine: generating left=%d, top=%d, width=%d, height=%d\n", + r->left, + r->top, + r->width, + r->height ); +#endif /*DEBUG*/ + + /* We are generating this chunk of the transformed image. + */ + want = *r; + want.left += oarea->left; + want.top += oarea->top; + + /* Find the area of the input image we need. + */ + im__transform_invert_rect( &affine->trn, &want, &need ); + + /* Now go to space (2) above. + */ + need.left += iarea->left; + need.top += iarea->top; + + /* Add a border for interpolation. Plus one for rounding errors. + */ + im_rect_marginadjust( &need, half_window_size + 1 ); + + /* Clip against the size of (2). + */ + image.left = 0; + image.top = 0; + image.width = in->Xsize; + image.height = in->Ysize; + 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*/ + + /* Resample! x/y loop over pixels in the output image (5). + */ + for( y = to; y < bo; y++ ) { + /* Input clipping rectangle. + */ + const int ile = iarea->left; + const int ito = iarea->top; + const int iri = iarea->left + iarea->width; + const int ibo = iarea->top + iarea->height; + + /* Derivative of matrix. + */ + const double ddx = affine->trn.ia; + const double ddy = affine->trn.ic; + + /* Continuous cods in transformed space. + */ + const double ox = le + oarea->left - affine->trn.dx; + const double oy = y + oarea->top - affine->trn.dy; + + /* Continuous cods in input space. + */ + double ix, iy; + + PEL *q; + + /* To (3). + */ + ix = affine->trn.ia * ox + affine->trn.ib * oy; + iy = affine->trn.ic * ox + affine->trn.id * oy; + + /* Now move to (2). + */ + ix += iarea->left; + iy += iarea->top; + + q = (PEL *) IM_REGION_ADDR( or, le, y ); + + for( x = le; x < ri; x++ ) { + int fx, fy; + + fx = FLOOR( ix ); + fy = FLOOR( iy ); + + /* Clipping! + */ + if( fx < ile || fx >= iri || fy < ito || fy >= ibo ) { + for( z = 0; z < ps; z++ ) + q[z] = 0; + } + else { + interpolate( affine->interpolate, + q, ir, ix, iy ); + } + + ix += ddx; + iy += ddy; + q += ps; + } + } + + return( 0 ); +} + +static int +affinei( IMAGE *in, IMAGE *out, + VipsInterpolate *interpolate, Transformation *trn ) +{ + Affine *affine; + double edge; + + /* Make output image. + */ + if( im_piocheck( in, out ) ) + return( -1 ); + if( im_cp_desc( out, in ) ) + return( -1 ); + + /* Need a copy of the params for the lifetime of out. + */ + if( !(affine = IM_NEW( out, Affine )) ) + return( -1 ); + affine->interpolate = NULL; + if( im_add_close_callback( out, + (im_callback_fn) affine_free, affine, NULL ) ) + return( -1 ); + affine->in = in; + affine->out = out; + affine->interpolate = interpolate; + g_object_ref( interpolate ); + affine->trn = *trn; + + if( im__transform_calc_inverse( &affine->trn ) ) + return( -1 ); + + out->Xsize = affine->trn.oarea.width; + out->Ysize = affine->trn.oarea.height; + + /* Normally SMALLTILE ... except if this is a size up/down affine. + */ + if( affine->trn.b == 0.0 && affine->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 / VIPS_TRANSFORM_SCALE; + if( affine->trn.oarea.left < -edge || affine->trn.oarea.top < -edge || + IM_RECT_RIGHT( &affine->trn.oarea ) > edge || + IM_RECT_BOTTOM( &affine->trn.oarea ) > edge ) { + im_error( "im_affinei", + "%s", _( "output coordinates out of range" ) ); + return( -1 ); + } + + /* Generate! + */ + if( im_generate( out, + im_start_one, affinei_gen, im_stop_one, in, affine ) ) + return( -1 ); + + return( 0 ); +} + +/* As above, but do IM_CODING_LABQ too. And embed the input. + */ +static int +im__affinei( IMAGE *in, IMAGE *out, + VipsInterpolate *interpolate, Transformation *trn ) +{ + IMAGE *t3 = im_open_local( out, "im_affine:3", "p" ); + const int window_size = vips_interpolate_get_window_size( interpolate ); + Transformation trn2; + + /* Add new pixels around the input so we can interpolate at the edges. + */ + if( !t3 || + im_embed( in, t3, 1, + window_size / 2, window_size / 2, + in->Xsize + window_size, in->Ysize + window_size ) ) + return( -1 ); + + /* Set iarea so we know what part of the input we can take. + */ + trn2 = *trn; + trn2.iarea.left += window_size / 2; + trn2.iarea.top += window_size / 2; + +#ifdef DEBUG_GEOMETRY + printf( "im__affinei: %s\n", in->filename ); + im__transform_print( &trn2 ); +#endif /*DEBUG_GEOMETRY*/ + + if( in->Coding == IM_CODING_LABQ ) { + IMAGE *t[2]; + + if( im_open_local_array( out, t, 2, "im_affine:2", "p" ) || + im_LabQ2LabS( t3, t[0] ) || + affinei( t[0], t[1], interpolate, &trn2 ) || + im_LabS2LabQ( t[1], out ) ) + return( -1 ); + } + else if( in->Coding == IM_CODING_NONE ) { + if( affinei( t3, out, interpolate, &trn2 ) ) + return( -1 ); + } + else { + im_error( "im_affinei", "%s", _( "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_affinei( IMAGE *in, IMAGE *out, VipsInterpolate *interpolate, + double a, double b, double c, double d, double dx, double dy, + int ox, int oy, int ow, int oh ) +{ + Transformation trn; + + trn.iarea.left = 0; + trn.iarea.top = 0; + trn.iarea.width = in->Xsize; + trn.iarea.height = in->Ysize; + + trn.oarea.left = ox; + trn.oarea.top = oy; + trn.oarea.width = ow; + trn.oarea.height = oh; + + trn.a = a; + trn.b = b; + trn.c = c; + trn.d = d; + trn.dx = dx; + trn.dy = dy; + + return( im__affinei( in, out, interpolate, &trn ) ); +} + +int +im_affinei_all( IMAGE *in, IMAGE *out, VipsInterpolate *interpolate, + double a, double b, double c, double d, 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 = c; + trn.d = d; + trn.dx = dx; + trn.dy = dy; + + im__transform_set_area( &trn ); + + return( im__affinei( in, out, interpolate, &trn ) ); +} + +/* Provide the old im__affine()/im_affine() as bilinear affinei. + */ + +int +im__affine( IMAGE *in, IMAGE *out, Transformation *trn ) +{ + return( im__affinei( in, out, + vips_interpolate_bilinear_static(), trn ) ); +} + +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 ) +{ + return( im_affinei( in, out, + vips_interpolate_bilinear_static(), + a, b, c, d, dx, dy, + ox, oy, ow, oh ) ); +} diff --git a/libvips/resample/interpolate.c b/libvips/resample/interpolate.c new file mode 100644 index 00000000..f87d47c0 --- /dev/null +++ b/libvips/resample/interpolate.c @@ -0,0 +1,493 @@ +/* vipsinterpolate ... abstract base class for various interpolators + * + * J. Cupitt, 15/10/08 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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*/ + +/* + * FAST_PSEUDO_FLOOR is a floor and floorf replacement which has been + * found to be faster on several linux boxes than the library + * version. It returns the floor of its argument unless the argument + * is a negative integer, in which case it returns one less than the + * floor. For example: + * + * FAST_PSEUDO_FLOOR(0.5) = 0 + * + * FAST_PSEUDO_FLOOR(0.) = 0 + * + * FAST_PSEUDO_FLOOR(-.5) = -1 + * + * as expected, but + * + * FAST_PSEUDO_FLOOR(-1.) = -2 + * + * The locations of the discontinuities of FAST_PSEUDO_FLOOR are the + * same as floor and floorf; it is just that at negative integers the + * function is discontinuous on the right instead of the left. + */ +#define FAST_PSEUDO_FLOOR(x) ( (int)(x) - ( (x) < 0. ) ) + +G_DEFINE_ABSTRACT_TYPE( VipsInterpolate, vips_interpolate, VIPS_TYPE_OBJECT ); + +#ifdef DEBUG +static void +vips_interpolate_finalize( GObject *gobject ) +{ + printf( "vips_interpolate_finalize: " ); + vips_object_print( VIPS_OBJECT( gobject ) ); + + G_OBJECT_CLASS( vips_interpolate_parent_class )->finalize( gobject ); +} +#endif /*DEBUG*/ + +static int +vips_interpolate_real_get_window_size( VipsInterpolate *interpolate ) +{ + VipsInterpolateClass *class = VIPS_INTERPOLATE_GET_CLASS( interpolate ); + + g_assert( class->window_size != -1 ); + + return( class->window_size ); +} + +static void +vips_interpolate_class_init( VipsInterpolateClass *class ) +{ +#ifdef DEBUG + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); +#endif /*DEBUG*/ + +#ifdef DEBUG + gobject_class->finalize = vips_interpolate_finalize; +#endif /*DEBUG*/ + class->interpolate = NULL; + class->get_window_size = vips_interpolate_real_get_window_size; + class->window_size = -1; +} + +static void +vips_interpolate_init( VipsInterpolate *interpolate ) +{ +#ifdef DEBUG + printf( "vips_interpolate_init: " ); + vips_object_print( VIPS_OBJECT( interpolate ) ); +#endif /*DEBUG*/ +} + +/* Set the point out_x, out_y in REGION out to be the point interpolated at + * in_x, in_y in REGION in. Don't do this as a signal for speed. + */ +void +vips_interpolate( VipsInterpolate *interpolate, + PEL *out, REGION *in, double x, double y ) +{ + VipsInterpolateClass *class = VIPS_INTERPOLATE_GET_CLASS( interpolate ); + + g_assert( class->interpolate ); + class->interpolate( interpolate, out, in, x, y ); +} + +/* As above, but return the function pointer. Use this to cache method + * dispatch. + */ +VipsInterpolateMethod +vips_interpolate_get_method( VipsInterpolate *interpolate ) +{ + VipsInterpolateClass *class = VIPS_INTERPOLATE_GET_CLASS( interpolate ); + + g_assert( class->interpolate ); + + return( class->interpolate ); +} + +/* Get this interpolator's required window size. + */ +int +vips_interpolate_get_window_size( VipsInterpolate *interpolate ) +{ + VipsInterpolateClass *class = VIPS_INTERPOLATE_GET_CLASS( interpolate ); + + g_assert( class->get_window_size ); + return( class->get_window_size( interpolate ) ); +} + +/* VipsInterpolateNearest class + */ + +#define VIPS_TYPE_INTERPOLATE_NEAREST (vips_interpolate_nearest_get_type()) +#define VIPS_INTERPOLATE_NEAREST( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE_NEAREST, VipsInterpolateNearest )) +#define VIPS_INTERPOLATE_NEAREST_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE_NEAREST, VipsInterpolateNearestClass)) +#define VIPS_IS_INTERPOLATE_NEAREST( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_NEAREST )) +#define VIPS_IS_INTERPOLATE_NEAREST_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_NEAREST )) +#define VIPS_INTERPOLATE_NEAREST_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE_NEAREST, VipsInterpolateNearestClass )) + +/* No new members. + */ +typedef VipsInterpolate VipsInterpolateNearest; +typedef VipsInterpolateClass VipsInterpolateNearestClass; + +G_DEFINE_TYPE( VipsInterpolateNearest, vips_interpolate_nearest, + VIPS_TYPE_INTERPOLATE ); + +static void +vips_interpolate_nearest_interpolate( VipsInterpolate *interpolate, + PEL *out, REGION *in, double x, double y ) +{ + /* Pel size and line size. + */ + const int ps = IM_IMAGE_SIZEOF_PEL( in->im ); + int z; + + /* Top left corner we interpolate from. + */ + const int xi = FAST_PSEUDO_FLOOR( x ); + const int yi = FAST_PSEUDO_FLOOR( y ); + + const PEL *p = (PEL *) IM_REGION_ADDR( in, xi, yi ); + + for( z = 0; z < ps; z++ ) + out[z] = p[z]; +} + +static void +vips_interpolate_nearest_class_init( VipsInterpolateNearestClass *class ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + VipsInterpolateClass *interpolate_class = + VIPS_INTERPOLATE_CLASS( class ); + + object_class->nickname = "nearest"; + object_class->description = _( "Nearest-neighbour interpolation" ); + + interpolate_class->interpolate = vips_interpolate_nearest_interpolate; + interpolate_class->window_size = 1; +} + +static void +vips_interpolate_nearest_init( VipsInterpolateNearest *nearest ) +{ +#ifdef DEBUG + printf( "vips_interpolate_nearest_init: " ); + vips_object_print( VIPS_OBJECT( nearest ) ); +#endif /*DEBUG*/ +} + +VipsInterpolate * +vips_interpolate_nearest_new( void ) +{ + + return( VIPS_INTERPOLATE( vips_object_new( + VIPS_TYPE_INTERPOLATE_NEAREST, NULL, NULL, NULL ) ) ); +} + +/* Convenience: return a static nearest you don't need to free. + */ +VipsInterpolate * +vips_interpolate_nearest_static( void ) +{ + static VipsInterpolate *interpolate = NULL; + + if( !interpolate ) + interpolate = vips_interpolate_nearest_new(); + + return( interpolate ); +} + +/* VipsInterpolateBilinear class + */ + +#define VIPS_TYPE_INTERPOLATE_BILINEAR (vips_interpolate_bilinear_get_type()) +#define VIPS_INTERPOLATE_BILINEAR( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE_BILINEAR, VipsInterpolateBilinear )) +#define VIPS_INTERPOLATE_BILINEAR_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE_BILINEAR, VipsInterpolateBilinearClass)) +#define VIPS_IS_INTERPOLATE_BILINEAR( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_BILINEAR )) +#define VIPS_IS_INTERPOLATE_BILINEAR_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_BILINEAR )) +#define VIPS_INTERPOLATE_BILINEAR_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE_BILINEAR, VipsInterpolateBilinearClass )) + +typedef VipsInterpolate VipsInterpolateBilinear; + +typedef struct _VipsInterpolateBilinearClass { + VipsInterpolateClass parent_class; + + /* Precalculated interpolation matricies. int (used for pel sizes up + * to short), and float (for all others). We go to scale + 1, so + * we can round-to-nearest safely. Don't bother with double, since + * this is an approximation anyway. + */ + int matrixi[VIPS_TRANSFORM_SCALE + 1][VIPS_TRANSFORM_SCALE + 1][4]; + float matrixd[VIPS_TRANSFORM_SCALE + 1][VIPS_TRANSFORM_SCALE + 1][4]; +} VipsInterpolateBilinearClass; + +G_DEFINE_TYPE( VipsInterpolateBilinear, vips_interpolate_bilinear, + VIPS_TYPE_INTERPOLATE ); + +/* in this class, name vars in the 2x2 grid as eg. + * p1 p2 + * p3 p4 + */ + +/* Interpolate a section ... int8/16 types. + */ +#define BILINEAR_INT( TYPE ) { \ + TYPE *tq = (TYPE *) out; \ + \ + const int c1 = class->matrixi[xi][yi][0]; \ + const int c2 = class->matrixi[xi][yi][1]; \ + const int c3 = class->matrixi[xi][yi][2]; \ + const int c4 = class->matrixi[xi][yi][3]; \ + \ + const TYPE *tp1 = (TYPE *) p1; \ + const TYPE *tp2 = (TYPE *) p2; \ + const TYPE *tp3 = (TYPE *) p3; \ + const TYPE *tp4 = (TYPE *) p4; \ + \ + for( z = 0; z < b; z++ ) \ + tq[z] = (c1 * tp1[z] + c2 * tp2[z] + \ + c3 * tp3[z] + c4 * tp4[z]) >> VIPS_INTERPOLATE_SHIFT; \ +} + +/* Interpolate a pel ... int32 and float types. + */ +#define BILINEAR_FLOAT( TYPE ) { \ + TYPE *tq = (TYPE *) out; \ + \ + const double c1 = class->matrixd[xi][yi][0]; \ + const double c2 = class->matrixd[xi][yi][1]; \ + const double c3 = class->matrixd[xi][yi][2]; \ + const double c4 = class->matrixd[xi][yi][3]; \ + \ + const TYPE *tp1 = (TYPE *) p1; \ + const TYPE *tp2 = (TYPE *) p2; \ + const TYPE *tp3 = (TYPE *) p3; \ + const TYPE *tp4 = (TYPE *) p4; \ + \ + for( z = 0; z < b; z++ ) \ + tq[z] = c1 * tp1[z] + c2 * tp2[z] + \ + c3 * tp3[z] + c4 * tp4[z]; \ +} + +/* Expand for band types. with a fixed-point interpolator and a float + * interpolator. + */ +#define SWITCH_INTERPOLATE( FMT, INT, FLOAT ) { \ + switch( (FMT) ) { \ + case IM_BANDFMT_UCHAR: INT( unsigned char ); break; \ + case IM_BANDFMT_CHAR: INT( char ); break; \ + case IM_BANDFMT_USHORT: INT( unsigned short ); break; \ + case IM_BANDFMT_SHORT: INT( short ); break; \ + case IM_BANDFMT_UINT: FLOAT( unsigned int ); break; \ + case IM_BANDFMT_INT: FLOAT( int ); break; \ + case IM_BANDFMT_FLOAT: FLOAT( float ); break; \ + case IM_BANDFMT_DOUBLE: FLOAT( double ); break; \ + default: \ + g_assert( FALSE ); \ + } \ +} + +static void +vips_interpolate_bilinear_interpolate( VipsInterpolate *interpolate, + PEL *out, REGION *in, double x, double y ) +{ + VipsInterpolateBilinearClass *class = + VIPS_INTERPOLATE_BILINEAR_GET_CLASS( interpolate ); + + /* Pel size and line size. + */ + const int ps = IM_IMAGE_SIZEOF_PEL( in->im ); + const int ls = IM_REGION_LSKIP( in ); + const int b = in->im->Bands; + + /* Now go to scaled int. + */ + const double sx = x * VIPS_TRANSFORM_SCALE; + const double sy = y * VIPS_TRANSFORM_SCALE; + const int sxi = FAST_PSEUDO_FLOOR( sx ); + const int syi = FAST_PSEUDO_FLOOR( sy ); + + /* Get index into interpolation table and unscaled integer + * position. + */ + const int xi = sxi & (VIPS_TRANSFORM_SCALE - 1); + const int yi = syi & (VIPS_TRANSFORM_SCALE - 1); + const int x_int = sxi >> VIPS_TRANSFORM_SHIFT; + const int y_int = syi >> VIPS_TRANSFORM_SHIFT; + + const PEL *p1 = (PEL *) IM_REGION_ADDR( in, x_int, y_int ); + const PEL *p2 = p1 + ps; + const PEL *p3 = p1 + ls; + const PEL *p4 = p3 + ps; + + int z; + + SWITCH_INTERPOLATE( in->im->BandFmt, + BILINEAR_INT, BILINEAR_FLOAT ); +} + +static void +vips_interpolate_bilinear_class_init( VipsInterpolateBilinearClass *class ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + VipsInterpolateClass *interpolate_class = + (VipsInterpolateClass *) class; + int x, y; + + object_class->nickname = "bilinear"; + object_class->description = _( "Bilinear interpolation" ); + + interpolate_class->interpolate = vips_interpolate_bilinear_interpolate; + interpolate_class->window_size = 2; + + /* Calculate the interpolation matricies. + */ + for( x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) + for( y = 0; y < VIPS_TRANSFORM_SCALE + 1; y++ ) { + double X, Y, Xd, Yd; + double c1, c2, c3, c4; + + /* Interpolation errors. + */ + X = (double) x / VIPS_TRANSFORM_SCALE; + Y = (double) y / VIPS_TRANSFORM_SCALE; + Xd = 1.0 - X; + Yd = 1.0 - Y; + + /* Weights. + */ + c1 = Xd * Yd; + c2 = X * Yd; + c3 = Xd * Y; + c4 = X * Y; + + class->matrixd[x][y][0] = c1; + class->matrixd[x][y][1] = c2; + class->matrixd[x][y][2] = c3; + class->matrixd[x][y][3] = c4; + + class->matrixi[x][y][0] = c1 * VIPS_INTERPOLATE_SCALE; + class->matrixi[x][y][1] = c2 * VIPS_INTERPOLATE_SCALE; + class->matrixi[x][y][2] = c3 * VIPS_INTERPOLATE_SCALE; + class->matrixi[x][y][3] = c4 * VIPS_INTERPOLATE_SCALE; + } +} + +static void +vips_interpolate_bilinear_init( VipsInterpolateBilinear *bilinear ) +{ +#ifdef DEBUG + printf( "vips_interpolate_bilinear_init: " ); + vips_object_print( VIPS_OBJECT( bilinear ) ); +#endif /*DEBUG*/ + +} + +VipsInterpolate * +vips_interpolate_bilinear_new( void ) +{ + return( VIPS_INTERPOLATE( vips_object_new( + VIPS_TYPE_INTERPOLATE_BILINEAR, NULL, NULL, NULL ) ) ); +} + +/* Convenience: return a static bilinear you don't need to free. + */ +VipsInterpolate * +vips_interpolate_bilinear_static( void ) +{ + static VipsInterpolate *interpolate = NULL; + + if( !interpolate ) + interpolate = vips_interpolate_bilinear_new(); + + return( interpolate ); +} + +/* Called on startup: register the base vips interpolators. + */ +void +vips__interpolate_init( void ) +{ + extern GType vips_interpolate_bicubic_get_type( void ); + extern GType vips_interpolate_yafrsmooth_get_type( void ); + extern GType vips_interpolate_nohalo1_get_type( void ); + extern GType vips_interpolate_snohalo1_get_type( void ); + extern GType vips_interpolate_nohalo2_get_type( void ); + + vips_interpolate_nearest_get_type(); + vips_interpolate_bilinear_get_type(); + vips_interpolate_bicubic_get_type(); + vips_interpolate_yafrsmooth_get_type(); + vips_interpolate_nohalo1_get_type(); + vips_interpolate_snohalo1_get_type(); + vips_interpolate_nohalo2_get_type(); +} + +/* Make an interpolator from a nickname. + */ +VipsInterpolate * +vips_interpolate_new( const char *nickname ) +{ + GType type; + + if( !(type = vips_type_find( "VipsInterpolate", nickname )) ) + return( NULL ); + + return( VIPS_INTERPOLATE( vips_object_new( type, NULL, NULL, NULL ) ) ); +} diff --git a/libvips/resample/nohalo1.cpp b/libvips/resample/nohalo1.cpp new file mode 100644 index 00000000..e540f042 --- /dev/null +++ b/libvips/resample/nohalo1.cpp @@ -0,0 +1,665 @@ +/* nohalo level 1 interpolator + * + * Hacked for vips by J. Cupitt, 20/1/09 + * + * Rename as nohalo1 and move "restrict" support to configure by + * J. Cupitt, 16/3/09 + * + * Tweaks by N. Robidoux and J. Cupitt 4-17/3/09 + * + * Tweaks by N. Robidoux 25-28/5/09 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + * 2009 (c) Nicolas Robidoux + * + * Nicolas thanks John Cupitt, Geert Jordaens, Øyvind KolÃ¥s, Ralf + * Meyer, Minglun Gong, and Sven Neumann for useful comments and code. + * + * Nicolas Robidoux's research on Nohalo funded in part by an NSERC + * (National Science and Engineering Research Council of Canada) + * Discovery Grant. + */ + +/* + * ================ + * NOHALO RESAMPLER + * ================ + * + * "Nohalo" is a family of parameterized resamplers with a mission: + * smoothly straightening oblique lines without undesirable + * side-effects. In particular, without much blurring and with + * absolutely no added haloing. + * + * The key parameter, which may be described as a "quality" parameter, + * is an integer which specifies the number of "levels" of binary + * subdivision which are performed. level = 0 can be thought of as + * being plain vanilla bilinear resampling; level = 1 is then the + * first "non-classical" method of the familiy. + * + * Although it increases computational cost, additional levels + * increase the quality of the resampled pixel value unless the + * resampled location happens to be exactly where a subdivided grid + * point (for this level) is located, in which case further levels do + * not change the answer, and consequently do not increase its + * quality. + * + * =================================================== + * THIS CODE ONLY IMPLEMENTS THE LOWEST QUALITY NOHALO + * =================================================== + * + * This code implement nohalo for (quality) level = 1. Nohalo for + * higher quality levels will be implemented later. + * + * Key properties: + * + * ======================= + * Nohalo is interpolatory + * ======================= + * + * That is, nohalo preserves point values: If asked for the value at + * the center of an input pixel, the sampler returns the corresponding + * value, unchanged. In addition, because nohalo is continuous, if + * asked for a value at a location "very close" to the center of an + * input pixel, then the sampler returns a value "very close" to + * it. (Nohalo is not smoothing like, say, B-Spline + * pseudo-interpolation.) + * + * ================================================================== + * Nohalo is co-monotone (this is why it's called "nohalo" = no halo) + * ================================================================== + * + * What monotonicity more or less means here is that the resampled + * value is in the range of the four closest input values. This + * property is why there is no added haloing. It also implies that + * clamping is unnecessary (provided abyss values are within the range + * of acceptable values, which is always the case). (Note: plain + * vanilla bilinear is also co-monotone.) + * + * Note: If the abyss policy is an extrapolating one---for example, + * linear or bilinear extrapolation---clamping is still unnecessary + * unless one attempts to resample outside of the convex hull of the + * input pixel positions. Consequence: the usual "interpolatory" image + * size convention (oft associated with "pixel center-based + * coordinates") does not require clamping when using linear + * extrapolation abyss policy when performing image resizing, but the + * usual "exact area" image size convention (oft associated with + * "pixel corner-based coordinates) does require clamping at locations + * very close to the boundary when upscaling. If computing values at + * locations outside of the convex hull of the pixel center locations + * of the input image, nearest neighbour abyss policy is most likely + * better anyway, because linear extrapolation produces "streaks" if + * positions far outside the original image boundary are sampled. Note + * that the nearest neighbor abyss policy ("clamp" in GPU parlance) is + * the most common one. Again, for this abyss policy, nohalo is + * monotone through and through and no pixel value clamping is + * necessary. + * + * ======================== + * Nohalo is a local method + * ======================== + * + * The value of the reconstructed intensity surface at any point + * depends on the values of (at most) 12 nearby input values, located + * in a "cross" centered at the closest four input pixel centers. + * + * =========================================================== + * When level = infinity, nohalo's intensity surface is smooth + * =========================================================== + * + * It is conjectured that the intensity surface is infinitely + * differentiable. Consequently, "Mach banding" (primarily caused by + * sharp "ridges" in the reconstructed intensity surface and + * particularly noticeable, for example, when using bilinear + * resampling) is (essentially) absent, even at high magnifications, + * WHEN THE LEVEL IS HIGH (more or less when 2^(level+1) is at least + * the largest local magnification factor, which means that the level + * 1 nohalo does not show much Mach banding up to a magnification of + * about 4). + * + * =============================== + * Nohalo is second order accurate + * =============================== + * + * (Except possibly near the boundary: it is easy to make this + * property carry over everywhere but this requires a tuned abyss + * policy---linear extrapolation, say---or building the boundary + * conditions inside the sampler.) Nohalo is exact on linear + * intensity profiles, meaning that if the input pixel values (in the + * stencil) are obtained from a function of the form f(x,y) = a + b*x + * + c*y (a, b, c constants), then the computed pixel value is exactly + * the value of f(x,y) at the asked-for sampling location. The + * boundary condition which is emulated by VIPS throught the "extend" + * extension of the input image---this corresponds to the nearest + * neighbour abyss policy---does NOT make this resampler exact on + * linears at the boundary. It does, however, guarantee that no + * clamping is required even when resampled values are computed at + * positions outside of the extent of the input image (when + * extrapolation is required). + * + * =================== + * Nohalo is nonlinear + * =================== + * + * In particular, resampling a sum of images may not be the same as + * summing the resamples. (This occurs even without taking into account + * over and underflow issues: images can only take values within a + * banded range, and consequently no sampler is truly linear.) + * + * ==================== + * Weaknesses of nohalo + * ==================== + * + * In some cases, the first level nonlinear computation is wasted: + * + * If a region is bichromatic, the nonlinear component of the level 1 + * nohalo is zero in the interior of the region, and consequently + * nohalo boils down to bilinear. For such images, either stick to + * bilinear, or use a higher level (quality) setting. (There is no + * real harm in using nohalo when it boils down to bilinear if one + * does not mind wasting cycles.) + * + * Low quality levels do NOT produce a continuously differentiable + * intensity surface: + * + * With a "finite" level is used (that is, in practice), the nohalo + * intensity surface is only continuous: there are gradient + * discontinuities because the "final interpolation step" is performed + * with bilinear. (Exception: if the "corner" image size convention is + * used and the magnification factor is 2, that is, if the resampled + * points sit exactly on the binary subdivided grid, then nohalo level + * 1 gives the same result as as level=infinity, and consequently the + * intensity surface can be treated as if smooth.) Note that these + * gradient discontinuities are nearly invisible when the + * magnification ratio is modest. + * + * ============================ + * CONVENTIONS USED IN THE CODE + * ============================ + * + * This code uses the "center-based coordinate convention, for which, + * the very first actual image pixel is understood to be located at + * (0,0), and the last one at (N-1,M-1), where M is the number of + * pixel rows of the input image, and N is its number of pixel + * columns. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "templates.h" + +#define VIPS_TYPE_INTERPOLATE_NOHALO1 \ + (vips_interpolate_nohalo1_get_type()) +#define VIPS_INTERPOLATE_NOHALO1( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE_NOHALO1, VipsInterpolateNohalo1 )) +#define VIPS_INTERPOLATE_NOHALO1_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE_NOHALO1, VipsInterpolateNohalo1Class)) +#define VIPS_IS_INTERPOLATE_NOHALO1( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_NOHALO1 )) +#define VIPS_IS_INTERPOLATE_NOHALO1_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_NOHALO1 )) +#define VIPS_INTERPOLATE_NOHALO1_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE_NOHALO1, VipsInterpolateNohalo1Class )) + +typedef struct _VipsInterpolateNohalo1 { + VipsInterpolate parent_object; + +} VipsInterpolateNohalo1; + +typedef struct _VipsInterpolateNohalo1Class { + VipsInterpolateClass parent_class; + +} VipsInterpolateNohalo1Class; + +static void inline +nohalo1( const double uno_two, + const double uno_thr, + const double dos_one, + const double dos_two, + const double dos_thr, + const double dos_fou, + const double tre_one, + const double tre_two, + const double tre_thr, + const double tre_fou, + const double qua_two, + const double qua_thr, + double* restrict r1, + double* restrict r2, + double* restrict r3 ) +{ + /* + * This function calculates the missing three double density pixel + * values. The caller does bilinear interpolation on them and + * dos_two. + */ + /* + * THE STENCIL OF INPUT VALUES: + * + * Nohalo's stencil is the same as, say, Catmull-Rom, with the + * exception that the four corner values are not used: + * + * (ix,iy-1) (ix+1,iy-1) + * = uno_two = uno_thr + * + * (ix-1,iy) (ix,iy) (ix+1,iy) (ix+2,iy) + * = dos_one = dos_two = dos_thr = dos_fou + * + * (ix-1,iy+1) (ix,iy+1) (ix+1,iy+1) (ix+2,iy+1) + * = tre_one = tre_two = tre_thr = tre_fou + * + * (ix,iy+2) (ix+1,iy+2) + * = qua_two = qua_thr + * + * Here, ix is the floor of the requested left-to-right location, iy + * is the floor of the requested up-to-down location. + * + * Pointer arithmetic is used to implicitly reflect the input + * stencil so that the requested pixel location is closer to + * dos_two, The above consequently corresponds to the case in which + * absolute_x is closer to ix than ix+1, and absolute_y is closer to + * iy than iy+1. For example, if relative_x_is_rite = 1 but + * relative_y_is_down = 0 (see below), then dos_two corresponds to + * (ix+1,iy), dos_thr corresponds to (ix,iy) etc. Consequently, the + * three missing double density values (corresponding to r1, r2 and + * r3) are halfway between dos_two and dos_thr, halfway between + * dos_two and tre_two, and at the average of the four central + * positions. + * + * The following code assumes that the stencil reflection has + * already been performed. + */ + + /* + * Computation of the nonlinear slopes: If two consecutive pixel + * value differences have the same sign, the smallest one (in + * absolute value) is taken to be the corresponding slope; if the + * two consecutive pixel value differences don't have the same sign, + * the corresponding slope is set to 0. In other words, apply minmod + * to comsecutive differences. + */ + /* + * Dos(s) horizontal differences: + */ + const double prem_dos = dos_two - dos_one; + const double deux_dos = dos_thr - dos_two; + const double troi_dos = dos_fou - dos_thr; + /* + * Tre(s) horizontal differences: + */ + const double prem_tre = tre_two - tre_one; + const double deux_tre = tre_thr - tre_two; + const double troi_tre = tre_fou - tre_thr; + /* + * Two vertical differences: + */ + const double prem_two = dos_two - uno_two; + const double deux_two = tre_two - dos_two; + const double troi_two = qua_two - tre_two; + /* + * Thr(ee) vertical differences: + */ + const double prem_thr = dos_thr - uno_thr; + const double deux_thr = tre_thr - dos_thr; + const double troi_thr = qua_thr - tre_thr; + + /* + * Products useful for minmod: + */ + const double deux_prem_dos = deux_dos * prem_dos; + const double deux_deux_dos = deux_dos * deux_dos; + const double deux_troi_dos = deux_dos * troi_dos; + + const double deux_prem_two = deux_two * prem_two; + const double deux_deux_two = deux_two * deux_two; + const double deux_troi_two = deux_two * troi_two; + + const double deux_prem_tre = deux_tre * prem_tre; + const double deux_deux_tre = deux_tre * deux_tre; + const double deux_troi_tre = deux_tre * troi_tre; + + const double deux_prem_thr = deux_thr * prem_thr; + const double deux_deux_thr = deux_thr * deux_thr; + const double deux_troi_thr = deux_thr * troi_thr; + + /* + * Differences useful for minmod: + */ + const double deux_prem_minus_deux_deux_dos = deux_prem_dos - deux_deux_dos; + const double deux_troi_minus_deux_deux_dos = deux_troi_dos - deux_deux_dos; + + const double deux_prem_minus_deux_deux_two = deux_prem_two - deux_deux_two; + const double deux_troi_minus_deux_deux_two = deux_troi_two - deux_deux_two; + + const double deux_prem_minus_deux_deux_tre = deux_prem_tre - deux_deux_tre; + const double deux_troi_minus_deux_deux_tre = deux_troi_tre - deux_deux_tre; + + const double deux_prem_minus_deux_deux_thr = deux_prem_thr - deux_deux_thr; + const double deux_troi_minus_deux_deux_thr = deux_troi_thr - deux_deux_thr; + + /* + * The following terms are computed here to put "space" between the + * computation of components of flag variables and their use: + */ + const double twice_dos_two_plus_dos_thr = ( dos_two + dos_thr ) * 2.; + const double twice_dos_two_plus_tre_two = ( dos_two + tre_two ) * 2.; + const double twice_deux_thr_plus_deux_dos = ( deux_thr + deux_dos ) * 2.; + + /* + * Compute the needed "right" (at the boundary between one input + * pixel areas) double resolution pixel value: + */ + const double four_times_dos_twothr = + twice_dos_two_plus_dos_thr + + + FAST_MINMOD( deux_dos, prem_dos, deux_prem_dos, + deux_prem_minus_deux_deux_dos ) + - + FAST_MINMOD( deux_dos, troi_dos, deux_troi_dos, + deux_troi_minus_deux_deux_dos ); + + /* + * Compute the needed "down" double resolution pixel value: + */ + const double four_times_dostre_two = + twice_dos_two_plus_tre_two + + + FAST_MINMOD( deux_two, prem_two, deux_prem_two, + deux_prem_minus_deux_deux_two ) + - + FAST_MINMOD( deux_two, troi_two, deux_troi_two, + deux_troi_minus_deux_deux_two ); + + /* + * Compute the "diagonal" (at the boundary between four input + * pixel areas) double resolution pixel value: + */ + const double eight_times_dostre_twothr = + twice_deux_thr_plus_deux_dos + + + FAST_MINMOD( deux_tre, prem_tre, deux_prem_tre, + deux_prem_minus_deux_deux_tre ) + - + FAST_MINMOD( deux_tre, troi_tre, deux_troi_tre, + deux_troi_minus_deux_deux_tre ) + + + FAST_MINMOD( deux_thr, prem_thr, deux_prem_thr, + deux_prem_minus_deux_deux_thr ) + - + FAST_MINMOD( deux_thr, troi_thr, deux_troi_thr, + deux_troi_minus_deux_deux_thr ) + + + four_times_dos_twothr + + + four_times_dostre_two; + + /* + * Return the first newly computed double density values: + */ + *r1 = four_times_dos_twothr; + *r2 = four_times_dostre_two; + *r3 = eight_times_dostre_twothr; +} + +/* Call nohalo1 with an interpolator as a parameter. + * It'd be nice to do this with templates somehow :-( but I can't see + * a clean way to do it. + */ +#define NOHALO1_INTER( inter ) \ + template static void inline \ + nohalo1_ ## inter( PEL* restrict pout, \ + const PEL* restrict pin, \ + const int bands, \ + const int lskip, \ + const double relative_x, \ + const double relative_y ) \ + { \ + T* restrict out = (T *) pout; \ + \ + const int relative_x_is_rite = ( relative_x >= 0. ); \ + const int relative_y_is_down = ( relative_y >= 0. ); \ + \ + const int sign_of_relative_x = 2 * relative_x_is_rite - 1; \ + const int sign_of_relative_y = 2 * relative_y_is_down - 1; \ + \ + const int corner_reflection_shift = \ + relative_x_is_rite * bands + relative_y_is_down * lskip; \ + \ + const int shift_back_1_pix = sign_of_relative_x * bands; \ + const int shift_back_1_row = sign_of_relative_y * lskip; \ + \ + const T* restrict in = ( (T *) pin ) + corner_reflection_shift; \ + \ + const int shift_forw_1_pix = -shift_back_1_pix; \ + const int shift_forw_1_row = -shift_back_1_row; \ + \ + const double w = ( 2 * sign_of_relative_x ) * relative_x; \ + const double z = ( 2 * sign_of_relative_y ) * relative_y; \ + \ + const int shift_forw_2_pix = 2 * shift_forw_1_pix; \ + const int shift_forw_2_row = 2 * shift_forw_1_row; \ + \ + const int uno_two_shift = shift_back_1_row; \ + const int uno_thr_shift = shift_forw_1_pix + shift_back_1_row; \ + \ + const double x = 1. - w; \ + const double w_times_z = w * z; \ + \ + const int dos_one_shift = shift_back_1_pix; \ + const int dos_two_shift = 0; \ + const int dos_thr_shift = shift_forw_1_pix; \ + const int dos_fou_shift = shift_forw_2_pix; \ + \ + const int tre_one_shift = shift_back_1_pix + shift_forw_1_row; \ + const int tre_two_shift = shift_forw_1_row; \ + const int tre_thr_shift = shift_forw_1_pix + shift_forw_1_row; \ + const int tre_fou_shift = shift_forw_2_pix + shift_forw_1_row; \ + \ + const double x_times_z = x * z; \ + \ + const int qua_two_shift = shift_forw_2_row; \ + const int qua_thr_shift = shift_forw_1_pix + shift_forw_2_row; \ + \ + const double w_times_y_over_4 = .25 * ( w - w_times_z ); \ + const double x_times_z_over_4 = .25 * x_times_z; \ + const double x_times_y_over_8 = .125 * ( x - x_times_z ); \ + \ + int band = bands; \ + \ + do \ + { \ + double four_times_dos_twothr; \ + double four_times_dostre_two; \ + double eight_times_dostre_twothr; \ + \ + const double dos_two = in[dos_two_shift]; \ + \ + nohalo1( in[uno_two_shift], in[uno_thr_shift], \ + in[dos_one_shift], dos_two, \ + in[dos_thr_shift], in[dos_fou_shift], \ + in[tre_one_shift], in[tre_two_shift], \ + in[tre_thr_shift], in[tre_fou_shift], \ + in[qua_two_shift], in[qua_thr_shift], \ + &four_times_dos_twothr, \ + &four_times_dostre_two, \ + &eight_times_dostre_twothr ); \ + \ + const T result = bilinear_ ## inter( w_times_z, \ + x_times_z_over_4, \ + w_times_y_over_4, \ + x_times_y_over_8, \ + dos_two, \ + four_times_dos_twothr, \ + four_times_dostre_two, \ + eight_times_dostre_twothr ); \ + \ + in++; \ + *out++ = result; \ + } while (--band); \ + } + +NOHALO1_INTER( fptypes ) +NOHALO1_INTER( withsign ) +NOHALO1_INTER( nosign ) + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsInterpolateNohalo1, vips_interpolate_nohalo1, + VIPS_TYPE_INTERPOLATE ); +} + +static void +vips_interpolate_nohalo1_interpolate( VipsInterpolate* restrict interpolate, + PEL* restrict out, + REGION* restrict in, + double absolute_x, + double absolute_y ) +{ + /* + * Unit buffer pointer shifts: + */ + const int actual_bands = in->im->Bands; + const int lskip = IM_REGION_LSKIP( in ) / IM_IMAGE_SIZEOF_ELEMENT( in->im ); + + const double absolute_y_minus_half = absolute_y - .5; + const double absolute_x_minus_half = absolute_x - .5; + /* + * floor's surrogate FAST_PSEUDO_FLOOR is used to make sure that the + * transition through 0 is smooth. If it is known that absolute_x + * and absolute_y will never be less than 0, plain cast---that is, + * const int ix = absolute_x---should be used instead. Actually, + * any function which agrees with floor for non-integer values, and + * picks one of the two possibilities for integer values, can be + * used. FAST_PSEUDO_FLOOR fits the bill. + * + * Then, x is the x-coordinate of the sampling point relative to the + * position of the center of the convex hull of the 2x2 block of + * closest pixels. Similarly for y. Range of values: [-.5,.5). + */ + const int iy = FAST_PSEUDO_FLOOR (absolute_y); + const double relative_y = absolute_y_minus_half - iy; + const int ix = FAST_PSEUDO_FLOOR (absolute_x); + const double relative_x = absolute_x_minus_half - ix; + + /* + * Move the pointer to (the first band of) the top/left pixel of the + * 2x2 group of pixel centers which contains the sampling location + * in its convex hull: + */ + const PEL* restrict p = (PEL *) IM_REGION_ADDR( in, ix, iy ); + + /* + * Double bands for complex images: + */ + const int bands = + ( im_iscomplex( in->im ) ? 2 * actual_bands : actual_bands ); + +#define CALL( T, inter ) \ + nohalo1_ ## inter( out, \ + p, \ + bands, \ + lskip, \ + relative_x, \ + relative_y ); + + switch( in->im->BandFmt ) { + case IM_BANDFMT_UCHAR: + CALL( unsigned char, nosign ); + break; + + case IM_BANDFMT_CHAR: + CALL( signed char, withsign ); + break; + + case IM_BANDFMT_USHORT: + CALL( unsigned short, nosign ); + break; + + case IM_BANDFMT_SHORT: + CALL( signed short, withsign ); + break; + + case IM_BANDFMT_UINT: + CALL( unsigned int, nosign ); + break; + + case IM_BANDFMT_INT: + CALL( signed int, withsign ); + break; + + /* Complex images handled by doubling of bands, see above. + */ + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_COMPLEX: + CALL( float, fptypes ); + break; + + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_DPCOMPLEX: + CALL( double, fptypes ); + break; + + default: + g_assert( 0 ); + break; + } +} + +static void +vips_interpolate_nohalo1_class_init( VipsInterpolateNohalo1Class *klass ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( klass ); + VipsInterpolateClass *interpolate_class = + VIPS_INTERPOLATE_CLASS( klass ); + + object_class->nickname = "nohalo1"; + object_class->description = _( "Edge-enhancing bilinear" ); + + interpolate_class->interpolate = vips_interpolate_nohalo1_interpolate; + interpolate_class->window_size = 4; +} + +static void +vips_interpolate_nohalo1_init( VipsInterpolateNohalo1 *nohalo1 ) +{ +} diff --git a/libvips/resample/nohalo2.cpp b/libvips/resample/nohalo2.cpp new file mode 100644 index 00000000..2dc1d559 --- /dev/null +++ b/libvips/resample/nohalo2.cpp @@ -0,0 +1,1112 @@ +/* nohalo level 2 interpolator + * + * N. Robidoux based on code by N. Robidoux and J. Cupitt 01/4-29/5/09 + * + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + * 2009 (c) Nicolas Robidoux + * + * Nicolas thanks Geert Jordaens, Ralf Meyer, John Cupitt, Øyvind + * KolÃ¥s, Minglun Gong and Sven Neumann for useful comments and code. + * + * Nicolas Robidoux's research on nohalo funded in part by an NSERC + * (National Science and Engineering Research Council of Canada) + * Discovery Grant. + */ + +/* + * ================ + * NOHALO RESAMPLER + * ================ + * + * "Nohalo" is a family of parameterized resamplers with a mission: + * smoothly straightening oblique lines without undesirable + * side-effects. In particular, without much blurring and with + * absolutely no added haloing. + * + * The key parameter, which may be described as a "quality" parameter, + * is an integer which specifies the number of "levels" of binary + * subdivision which are performed. level = 0 can be thought of as + * being plain vanilla bilinear resampling; level = 1 is then the + * first "non-classical" method of the familiy. + * + * Although it increases computational cost, additional levels + * increase the quality of the resampled pixel value unless the + * resampled location happens to be exactly where a subdivided grid + * point (for this level) is located, in which case further levels do + * not change the answer, and consequently do not increase its + * quality. + * + * ========================================================== + * THIS CODE ONLY IMPLEMENTS THE SECOND LOWEST QUALITY NOHALO + * ========================================================== + * + * This code implement nohalo for (quality) level = 2. Nohalo for + * higher quality levels will be implemented later. + * + * Key properties: + * + * ======================= + * Nohalo is interpolatory + * ======================= + * + * That is, nohalo preserves point values: If asked for the value at + * the center of an input pixel, the sampler returns the corresponding + * value, unchanged. In addition, because nohalo is continuous, if + * asked for a value at a location "very close" to the center of an + * input pixel, then the sampler returns a value "very close" to + * it. (Nohalo is not smoothing like, say, B-Spline + * pseudo-interpolation.) + * + * ======================================================== + * Nohalo is co-monotone (this is why it's called "nohalo") + * ======================================================== + * + * What monotonicity more or less means here is that the resampled + * value is in the range of the four closest input values. This + * property is why there is no added haloing. It also implies that + * clamping is unnecessary (provided abyss values are within the range + * of acceptable values, which is always the case). (Note: plain + * vanilla bilinear is also co-monotone.) + * + * Note: If the abyss policy is an extrapolating one---for example, + * linear or bilinear extrapolation---clamping is still unnecessary + * unless one attempts to resample outside of the convex hull of the + * input pixel positions. Consequence: the "corner" image size + * convention does not require clamping when using linear + * extrapolation abyss policy when performing image resizing, but the + * "center" one does, when upscaling, at locations very close to the + * boundary. If computing values at locations outside of the convex + * hull of the pixel locations of the input image, nearest neighbour + * abyss policy is most likely better anyway, because linear + * extrapolation produces "streaks" if positions far outside the + * original image boundary are resampled. + * + * ======================== + * Nohalo is a local method + * ======================== + * + * The value of the reconstructed intensity surface at any point + * depends on the values of (at most) 19 nearby input values. An + * explanatory diagram is found below. + * + * =========================================================== + * When level = infinity, nohalo's intensity surface is smooth + * =========================================================== + * + * It is conjectured that the intensity surface is infinitely + * differentiable. Consequently, "Mach banding" (primarily caused by + * sharp "ridges" in the reconstructed intensity surface and + * particularly noticeable, for example, when using bilinear + * resampling) is (essentially) absent, even at high magnifications, + * WHEN THE LEVEL IS HIGH (more or less when 2^(level+1) is at least + * the largest local magnification factor, which means that the level + * 1 nohalo does not show much Mach banding up to a magnification of + * about 4). + * + * =============================== + * Nohalo is second order accurate + * =============================== + * + * (Except possibly near the boundary: it is easy to make this + * property carry over everywhere but this requires a tuned abyss + * policy---linear extrapolation, say---or building the boundary + * conditions inside the sampler.) Nohalo is exact on linear + * intensity profiles, meaning that if the input pixel values (in the + * stencil) are obtained from a function of the form f(x,y) = a + b*x + * + c*y (a, b, c constants), then the computed pixel value is exactly + * the value of f(x,y) at the asked-for sampling location. The + * boundary condition which is emulated by VIPS throught the "extend" + * extension of the input image---this corresponds to the nearest + * neighbour abyss policy---does NOT make this resampler exact on + * linears at the boundary. It does, however, guarantee that no + * clamping is required even when resampled values are computed at + * positions outside of the extent of the input image (when + * extrapolation is required). + * + * =================== + * Nohalo is nonlinear + * =================== + * + * In particular, resampling a sum of images may not be the same as + * summing the resamples. (This occurs even without taking into account + * over and underflow issues: images can only take values within a + * banded range, and consequently no sampler is truly linear.) + * + * ==================== + * Weaknesses of nohalo + * ==================== + * + * In some cases, the first level nonlinear computation is wasted: + * + * If a region is bichromatic, the nonlinear component of the level 1 + * nohalo is zero in the interior of the region, and consequently + * nohalo boils down to bilinear. For such images, either stick to + * bilinear, or use a higher level (quality) setting. (There is no + * real harm in using nohalo when it boils down to bilinear if one + * does not mind wasting cycles.) + * + * Low quality levels do NOT produce a continuously differentiable + * intensity surface: + * + * With a "finite" level is used (that is, in practice), the nohalo + * intensity surface is only continuous: there are gradient + * discontinuities because the "final interpolation step" is performed + * with bilinear. (Exception: if the "corner" image size convention is + * used and the magnification factor is 4, that is, if the resampled + * points sit exactly on the binary subdivided grid, then nohalo level + * 2 gives the same result as as level=infinity, and consequently the + * intensity surface can be treated as if smooth.) + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +/* + * THIS CODE ALMOST CERTAINLY HAS A SMALL BUG. Nicolas Robidoux May + * 31, 2009. + */ + + +#include +#include + +#include "templates.h" + +#define VIPS_TYPE_INTERPOLATE_NOHALO2 \ + (vips_interpolate_nohalo2_get_type()) +#define VIPS_INTERPOLATE_NOHALO2( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE_NOHALO2, VipsInterpolateNohalo2 )) +#define VIPS_INTERPOLATE_NOHALO2_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE_NOHALO2, VipsInterpolateNohalo2Class)) +#define VIPS_IS_INTERPOLATE_NOHALO2( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_NOHALO2 )) +#define VIPS_IS_INTERPOLATE_NOHALO2_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_NOHALO2 )) +#define VIPS_INTERPOLATE_NOHALO2_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE_NOHALO2, VipsInterpolateNohalo2Class )) + +typedef struct _VipsInterpolateNohalo2 { + VipsInterpolate parent_object; + +} VipsInterpolateNohalo2; + +typedef struct _VipsInterpolateNohalo2Class { + VipsInterpolateClass parent_class; + +} VipsInterpolateNohalo2Class; + +static void inline +nohalo_step2( const double uno_two, + const double uno_thr, + const double dos_one, + const double dos_two, + const double dos_thr, + const double dos_fou, + const double tre_one, + const double tre_two, + const double tre_thr, + const double tre_fou, + const double qua_two, + const double qua_thr, + double* restrict r0, + double* restrict r1, + double* restrict r2, + double* restrict r3 ) +{ + /* + * This function calculates the missing three double density pixel + * values. The caller does bilinear interpolation on them and + * dos_two. + */ + /* + * THE STENCIL OF INPUT VALUES: + * + * Nohalo's stencil is the same as, say, Catmull-Rom, with the + * exception that the four corner values are not used: + * + * (ix,iy-1) (ix+1,iy-1) + * = uno_two = uno_thr + * + * (ix-1,iy) (ix,iy) (ix+1,iy) (ix+2,iy) + * = dos_one = dos_two = dos_thr = dos_fou + * + * (ix-1,iy+1) (ix,iy+1) (ix+1,iy+1) (ix+2,iy+1) + * = tre_one = tre_two = tre_thr = tre_fou + * + * (ix,iy+2) (ix+1,iy+2) + * = qua_two = qua_thr + * + * Here, ix is the floor of the requested left-to-right location, iy + * is the floor of the requested up-to-down location. + * + * Pointer arithmetic is used to implicitly reflect the input + * stencil so that the requested pixel location is closer to + * dos_two, The above consequently corresponds to the case in which + * absolute_x is closer to ix than ix+1, and absolute_y is closer to + * iy than iy+1. For example, if relative_x_is_rite = 1 but + * relative_y_is_down = 0 (see below), then dos_two corresponds to + * (ix+1,iy), dos_thr corresponds to (ix,iy) etc. Consequently, the + * three missing double density values (corresponding to r1, r2 and + * r3) are halfway between dos_two and dos_thr, halfway between + * dos_two and tre_two, and at the average of the four central + * positions. + * + * The following code assumes that the stencil reflection has + * already been performed. + */ + + /* + * Computation of the nonlinear slopes: If two consecutive pixel + * value differences have the same sign, the smallest one (in + * absolute value) is taken to be the corresponding slope; if the + * two consecutive pixel value differences don't have the same sign, + * the corresponding slope is set to 0. In other words, apply minmod + * to comsecutive differences. + */ + /* + * Dos(s) horizontal differences: + */ + const double prem_dos = dos_two - dos_one; + const double deux_dos = dos_thr - dos_two; + const double troi_dos = dos_fou - dos_thr; + /* + * Tre(s) horizontal differences: + */ + const double prem_tre = tre_two - tre_one; + const double deux_tre = tre_thr - tre_two; + const double troi_tre = tre_fou - tre_thr; + /* + * Two vertical differences: + */ + const double prem_two = dos_two - uno_two; + const double deux_two = tre_two - dos_two; + const double troi_two = qua_two - tre_two; + /* + * Thr(ee) vertical differences: + */ + const double prem_thr = dos_thr - uno_thr; + const double deux_thr = tre_thr - dos_thr; + const double troi_thr = qua_thr - tre_thr; + + /* + * Products useful for minmod: + */ + const double deux_prem_dos = deux_dos * prem_dos; + const double deux_deux_dos = deux_dos * deux_dos; + const double deux_troi_dos = deux_dos * troi_dos; + + const double deux_prem_two = deux_two * prem_two; + const double deux_deux_two = deux_two * deux_two; + const double deux_troi_two = deux_two * troi_two; + + const double deux_prem_tre = deux_tre * prem_tre; + const double deux_deux_tre = deux_tre * deux_tre; + const double deux_troi_tre = deux_tre * troi_tre; + + const double deux_prem_thr = deux_thr * prem_thr; + const double deux_deux_thr = deux_thr * deux_thr; + const double deux_troi_thr = deux_thr * troi_thr; + + /* + * Differences useful for minmod: + */ + const double deux_prem_minus_deux_deux_dos = deux_prem_dos - deux_deux_dos; + const double deux_troi_minus_deux_deux_dos = deux_troi_dos - deux_deux_dos; + + const double deux_prem_minus_deux_deux_two = deux_prem_two - deux_deux_two; + const double deux_troi_minus_deux_deux_two = deux_troi_two - deux_deux_two; + + const double deux_prem_minus_deux_deux_tre = deux_prem_tre - deux_deux_tre; + const double deux_troi_minus_deux_deux_tre = deux_troi_tre - deux_deux_tre; + + const double deux_prem_minus_deux_deux_thr = deux_prem_thr - deux_deux_thr; + const double deux_troi_minus_deux_deux_thr = deux_troi_thr - deux_deux_thr; + + /* + * The following terms are computed here to put "space" between the + * computation of components of flag variables and their use: + */ + const double twice_dos_two_plus_dos_thr = ( dos_two + dos_thr ) * 2.; + const double twice_dos_two_plus_tre_two = ( dos_two + tre_two ) * 2.; + const double twice_deux_thr_plus_deux_dos = ( deux_thr + deux_dos ) * 2.; + + /* + * Compute the needed "right" (at the boundary between one input + * pixel areas) double resolution pixel value: + */ + const double four_times_dos_twothr = + twice_dos_two_plus_dos_thr + + + FAST_MINMOD( deux_dos, prem_dos, deux_prem_dos, + deux_prem_minus_deux_deux_dos ) + - + FAST_MINMOD( deux_dos, troi_dos, deux_troi_dos, + deux_troi_minus_deux_deux_dos ); + + /* + * Compute the needed "down" double resolution pixel value: + */ + const double four_times_dostre_two = + twice_dos_two_plus_tre_two + + + FAST_MINMOD( deux_two, prem_two, deux_prem_two, + deux_prem_minus_deux_deux_two ) + - + FAST_MINMOD( deux_two, troi_two, deux_troi_two, + deux_troi_minus_deux_deux_two ); + + /* + * Compute the "diagonal" (at the boundary between four input + * pixel areas) double resolution pixel value: + */ + const double eight_times_dostre_twothr = + twice_deux_thr_plus_deux_dos + + + FAST_MINMOD( deux_tre, prem_tre, deux_prem_tre, + deux_prem_minus_deux_deux_tre ) + - + FAST_MINMOD( deux_tre, troi_tre, deux_troi_tre, + deux_troi_minus_deux_deux_tre ) + + + FAST_MINMOD( deux_thr, prem_thr, deux_prem_thr, + deux_prem_minus_deux_deux_thr ) + - + FAST_MINMOD( deux_thr, troi_thr, deux_troi_thr, + deux_troi_minus_deux_deux_thr ) + + + four_times_dos_twothr + + + four_times_dostre_two; + + /* + * Return the first newly computed double density values: + */ + *r0 = dos_two; + *r1 = four_times_dos_twothr; + *r2 = four_times_dostre_two; + *r3 = eight_times_dostre_twothr; +} + +static void inline +nohalo_step1( const double uno_thr, + const double uno_fou, + const double dos_two, + const double dos_thr, + const double dos_fou, + const double dos_fiv, + const double tre_one, + const double tre_two, + const double tre_thr, + const double tre_fou, + const double tre_fiv, + const double qua_one, + const double qua_two, + const double qua_thr, + const double qua_fou, + const double qua_fiv, + const double cin_two, + const double cin_thr, + const double cin_fou, + double* restrict uno_two_1, + double* restrict uno_thr_1, + double* restrict dos_one_1, + double* restrict dos_two_1, + double* restrict dos_thr_1, + double* restrict dos_fou_1, + double* restrict tre_one_1, + double* restrict tre_two_1, + double* restrict tre_thr_1, + double* restrict tre_fou_1, + double* restrict qua_two_1, + double* restrict qua_thr_1 ) +{ + /* + * This function calculates the missing ten double density pixel + * values, and also returns the "already known" two, for a total of + * twelve, which fills the stencil of nohalo level 1. The caller + * then applies one level of nohalo subdivision to these 12 values, + * prior to applying bilinear interpolation. + */ + /* + * THE STENCIL OF INPUT VALUES: + * + * Pointer arithmetic is used to implicitly reflect the input + * stencil so that the requested pixel location (indicated by X + * below) is closer to tre_thr than the other three points which + * define the convex hull of input pixel positions in which it sits, + * + * The following code and picture assumes that the stencil reflexion + * has already been performed. + * + * + * (ix,iy-2) (ix+1,iy-2) + * = uno_thr = uno_fou + * + * + * + * (ix-1,iy-1) (ix,iy-1) (ix+1,iy-1) (ix+2,iy-1) + * = dos_two = dos_thr = dos_fou = dos_fiv + * + * + * + * (ix-2,iy) (ix-1,iy) (ix,iy) (ix+1,iy) (ix+2,iy) + * = tre_one = tre_two = tre_thr = tre_fou = tre_fiv + * X + * + * + * (ix-2,iy) (ix-1,iy+1) (ix,iy+1) (ix+1,iy+1) (ix+2,iy+1) + * = qua_one = qua_two = qua_thr = qua_fou = qua_fiv + * + * + * + * (ix-1,iy+2) (ix,iy+2) (ix+1,iy+2) + * = cin_two = cin_thr = cin_fou + * + * + * Here, ix is the (pseudo-)floor of the requested left-to-right + * location, iy is the floor of the requested up-to-down location. + * + * The above input pixel values are the ones needed in order to make + * available to the second level the following first (and + * zero=input) level values: + * + * uno_two_1 = uno_thr_1 = + * (ix,iy-1/2) (ix+1/2,iy-1/2) + * + * + * + * + * dos_one_1 = dos_two_1 = dos_thr_1 = dos_fou_1 = + * (ix-1/2,iy) (ix,iy) (ix+1/2,iy) (ix+1,iy) + * + * X + * + * + * tre_one_1 = tre_two_1 = tre_thr_1 = tre_fou_1 = + * (ix-1/2,iy+1/2) (ix,iy+1/2) (ix+1/2,iy+1/2) (ix+1,iy+1/2) + * + * + * + * + * qua_two_1 = qua_thr_1 = + * (ix,iy+1) (ix+1/2,iy+1) + * + * + * to which nohalo level 1 is applied by the caller. + */ + + /* + * Computation of the nonlinear slopes: If two consecutive pixel + * value differences have the same sign, the smallest one (in + * absolute value) is taken to be the corresponding slope; if the + * two consecutive pixel value differences don't have the same sign, + * the corresponding slope is set to 0. In other words, apply minmod + * to comsecutive differences. + */ + /* + * Two vertical simple differences: + */ + const double d_dostre_two = tre_two - dos_two; + const double d_trequa_two = qua_two - tre_two; + const double d_quacin_two = cin_two - qua_two; + /* + * Thr(ee) vertical differences: + */ + const double d_unodos_thr = dos_thr - uno_thr; + const double d_dostre_thr = tre_thr - dos_thr; + const double d_trequa_thr = qua_thr - tre_thr; + const double d_quacin_thr = cin_thr - qua_thr; + /* + * Fou(r) vertical differences: + */ + const double d_unodos_fou = dos_fou - uno_fou; + const double d_dostre_fou = tre_fou - dos_fou; + const double d_trequa_fou = qua_fou - tre_fou; + const double d_quacin_fou = cin_fou - qua_fou; + /* + * Dos horizontal differences: + */ + const double d_dos_twothr = dos_thr - dos_two; + const double d_dos_thrfou = dos_fou - dos_thr; + const double d_dos_foufiv = dos_fiv - dos_fou; + /* + * Tre(s) horizontal differences: + */ + const double d_tre_onetwo = tre_two - tre_one; + const double d_tre_twothr = tre_thr - tre_two; + const double d_tre_thrfou = tre_fou - tre_thr; + const double d_tre_foufiv = tre_fiv - tre_fou; + /* + * Qua(ttro) horizontal differences: + */ + const double d_qua_onetwo = qua_two - qua_one; + const double d_qua_twothr = qua_thr - qua_two; + const double d_qua_thrfou = qua_fou - qua_thr; + const double d_qua_foufiv = qua_fiv - qua_fou; + + /* + * Recyclable vertical products and squares: + */ + const double d_dostre_times_trequa_two = d_dostre_two * d_trequa_two; + const double d_trequa_two_sq = d_trequa_two * d_trequa_two; + const double d_trequa_times_quacin_two = d_trequa_two * d_quacin_two; + + const double d_unodos_times_dostre_thr = d_unodos_thr * d_dostre_thr; + const double d_dostre_thr_sq = d_dostre_thr * d_dostre_thr; + const double d_dostre_times_trequa_thr = d_dostre_thr * d_trequa_thr; + const double d_trequa_times_quacin_thr = d_trequa_thr * d_quacin_thr; + const double d_quacin_thr_sq = d_quacin_thr * d_quacin_thr; + + const double d_unodos_times_dostre_fou = d_unodos_fou * d_dostre_fou; + const double d_dostre_fou_sq = d_dostre_fou * d_dostre_fou; + const double d_dostre_times_trequa_fou = d_dostre_fou * d_trequa_fou; + const double d_trequa_times_quacin_fou = d_trequa_fou * d_quacin_fou; + const double d_quacin_fou_sq = d_quacin_fou * d_quacin_fou; + /* + * Recyclable horizontal products and squares: + */ + const double d_dos_twothr_times_thrfou = d_dos_twothr * d_dos_thrfou; + const double d_dos_thrfou_sq = d_dos_thrfou * d_dos_thrfou; + const double d_dos_thrfou_times_foufiv = d_dos_thrfou * d_dos_foufiv; + + const double d_tre_onetwo_times_twothr = d_tre_onetwo * d_tre_twothr; + const double d_tre_twothr_sq = d_tre_twothr * d_tre_twothr; + const double d_tre_twothr_times_thrfou = d_tre_twothr * d_tre_thrfou; + const double d_tre_thrfou_times_foufiv = d_tre_thrfou * d_tre_foufiv; + const double d_tre_foufiv_sq = d_tre_foufiv * d_tre_foufiv; + + const double d_qua_onetwo_times_twothr = d_qua_onetwo * d_qua_twothr; + const double d_qua_twothr_sq = d_qua_twothr * d_qua_twothr; + const double d_qua_twothr_times_thrfou = d_qua_twothr * d_qua_thrfou; + const double d_qua_thrfou_times_foufiv = d_qua_thrfou * d_qua_foufiv; + const double d_qua_foufiv_sq = d_qua_foufiv * d_qua_foufiv; + + /* + * Vertical differences useful for minmod: + */ + const double d_dostre_times_trequa_minus_trequa_sq_two = + d_dostre_times_trequa_two - d_trequa_two_sq; + const double d_trequa_times_quacin_minus_trequa_sq_two = + d_trequa_times_quacin_two - d_trequa_two_sq; + + const double d_unodos_times_dostre_minus_dostre_sq_thr = + d_unodos_times_dostre_thr - d_dostre_thr_sq; + const double d_dostre_times_trequa_minus_dostre_sq_thr = + d_dostre_times_trequa_thr - d_dostre_thr_sq; + const double d_trequa_times_quacin_minus_quacin_sq_thr = + d_trequa_times_quacin_thr - d_quacin_thr_sq; + + const double d_unodos_times_dostre_minus_dostre_sq_fou = + d_unodos_times_dostre_fou - d_dostre_fou_sq; + const double d_dostre_times_trequa_minus_dostre_sq_fou = + d_dostre_times_trequa_fou - d_dostre_fou_sq; + const double d_trequa_times_quacin_minus_quacin_sq_fou = + d_trequa_times_quacin_fou - d_quacin_fou_sq; + /* + * Horizontal differences useful for minmod: + */ + const double d_dos_twothr_times_thrfou_minus_thrfou_sq = + d_dos_twothr_times_thrfou - d_dos_thrfou_sq; + const double d_dos_thrfou_times_foufiv_minus_thrfou_sq = + d_dos_thrfou_times_foufiv - d_dos_thrfou_sq; + + const double d_tre_onetwo_times_twothr_minus_twothr_sq = + d_tre_onetwo_times_twothr - d_tre_twothr_sq; + const double d_tre_twothr_times_thrfou_minus_twothr_sq = + d_tre_twothr_times_thrfou - d_tre_twothr_sq; + const double d_tre_thrfou_times_foufiv_minus_foufiv_sq = + d_tre_thrfou_times_foufiv - d_tre_foufiv_sq; + + const double d_qua_onetwo_times_twothr_minus_twothr_sq = + d_qua_onetwo_times_twothr - d_qua_twothr_sq; + const double d_qua_twothr_times_thrfou_minus_twothr_sq = + d_qua_twothr_times_thrfou - d_qua_twothr_sq; + const double d_qua_thrfou_times_foufiv_minus_foufiv_sq = + d_qua_thrfou_times_foufiv - d_qua_foufiv_sq; + + /* + * Minmod slopes and first level pixel values: + */ + const double dos_thr_y = + FAST_MINMOD( d_dostre_thr, d_unodos_thr, + d_unodos_times_dostre_thr, + d_unodos_times_dostre_minus_dostre_sq_thr ); + const double tre_thr_y = + FAST_MINMOD( d_dostre_thr, d_trequa_thr, + d_dostre_times_trequa_thr, + d_dostre_times_trequa_minus_dostre_sq_thr ); + + const double val_uno_two_1 = + .5 * ( dos_thr + tre_thr ) + + + .25 * ( dos_thr_y - tre_thr_y ); + + const double qua_thr_y = + FAST_MINMOD( d_quacin_thr, d_trequa_thr, + d_trequa_times_quacin_thr, + d_trequa_times_quacin_minus_quacin_sq_thr ); + + const double val_tre_two_1 = + .5 * ( tre_thr + qua_thr ) + + + .25 * ( tre_thr_y - qua_thr_y ); + + const double tre_fou_y = + FAST_MINMOD( d_dostre_fou, d_trequa_fou, + d_dostre_times_trequa_fou, + d_dostre_times_trequa_minus_dostre_sq_fou ); + const double qua_fou_y = + FAST_MINMOD( d_quacin_fou, d_trequa_fou, + d_trequa_times_quacin_fou, + d_trequa_times_quacin_minus_quacin_sq_fou ); + + const double val_tre_fou_1 = + .5 * ( tre_fou + qua_fou ) + + + .25 * ( tre_fou_y - qua_fou_y ); + + const double tre_two_x = + FAST_MINMOD( d_tre_twothr, d_tre_onetwo, + d_tre_onetwo_times_twothr, + d_tre_onetwo_times_twothr_minus_twothr_sq ); + const double tre_thr_x = + FAST_MINMOD( d_tre_twothr, d_tre_thrfou, + d_tre_twothr_times_thrfou, + d_tre_twothr_times_thrfou_minus_twothr_sq ); + + const double val_dos_one_1 = + .5 * ( tre_two + tre_thr ) + + + .25 * ( tre_two_x - tre_thr_x ); + + const double tre_fou_x = + FAST_MINMOD( d_tre_foufiv, d_tre_thrfou, + d_tre_thrfou_times_foufiv, + d_tre_thrfou_times_foufiv_minus_foufiv_sq ); + + const double tre_thr_x_minus_tre_fou_x = + tre_thr_x - tre_fou_x; + + const double val_dos_thr_1 = + .5 * ( tre_thr + tre_fou ) + + + .25 * tre_thr_x_minus_tre_fou_x; + + const double qua_thr_x = + FAST_MINMOD( d_qua_twothr, d_qua_thrfou, + d_qua_twothr_times_thrfou, + d_qua_twothr_times_thrfou_minus_twothr_sq ); + const double qua_fou_x = + FAST_MINMOD( d_qua_foufiv, d_qua_thrfou, + d_qua_thrfou_times_foufiv, + d_qua_thrfou_times_foufiv_minus_foufiv_sq ); + + const double qua_thr_x_minus_qua_fou_x = + qua_thr_x - qua_fou_x; + + const double val_qua_thr_1 = + .5 * ( qua_thr + qua_fou ) + + + .25 * qua_thr_x_minus_qua_fou_x; + const double val_tre_thr_1 = + .125 * ( tre_thr_x_minus_tre_fou_x + qua_thr_x_minus_qua_fou_x ) + + + .5 * ( val_tre_two_1 + val_tre_fou_1 ); + + const double dos_fou_y = + FAST_MINMOD( d_dostre_fou, d_unodos_fou, + d_unodos_times_dostre_fou, + d_unodos_times_dostre_minus_dostre_sq_fou ); + const double dos_thr_x = + FAST_MINMOD( d_dos_thrfou, d_dos_twothr, + d_dos_twothr_times_thrfou, + d_dos_twothr_times_thrfou_minus_thrfou_sq ); + const double dos_fou_x = + FAST_MINMOD( d_dos_thrfou, d_dos_foufiv, + d_dos_thrfou_times_foufiv, + d_dos_thrfou_times_foufiv_minus_thrfou_sq ); + + const double val_uno_thr_1 = + .25 * ( dos_fou - tre_thr ) + + + .125 * ( dos_fou_y - tre_fou_y + dos_thr_x - dos_fou_x ) + + + .5 * ( val_uno_two_1 + val_dos_thr_1 ); + + const double qua_two_x = + FAST_MINMOD( d_qua_twothr, d_qua_onetwo, + d_qua_onetwo_times_twothr, + d_qua_onetwo_times_twothr_minus_twothr_sq ); + const double tre_two_y = + FAST_MINMOD( d_trequa_two, d_dostre_two, + d_dostre_times_trequa_two, + d_dostre_times_trequa_minus_trequa_sq_two ); + const double qua_two_y = + FAST_MINMOD( d_trequa_two, d_quacin_two, + d_trequa_times_quacin_two, + d_trequa_times_quacin_minus_trequa_sq_two ); + + const double val_tre_one_1 = + .25 * ( qua_two - tre_thr ) + + + .125 * ( qua_two_x - qua_thr_x + tre_two_y - qua_two_y ) + + + .5 * ( val_dos_one_1 + val_tre_two_1 ); + + /* + * Return level 1 stencil values: + */ + *uno_two_1 = val_uno_two_1; + *uno_thr_1 = val_uno_thr_1; + *dos_one_1 = val_dos_one_1; + *dos_two_1 = tre_thr; + *dos_thr_1 = val_dos_thr_1; + *dos_fou_1 = tre_fiv; + *tre_one_1 = val_tre_one_1; + *tre_two_1 = val_tre_two_1; + *tre_thr_1 = val_tre_thr_1; + *tre_fou_1 = val_tre_fou_1; + *qua_two_1 = qua_thr; + *qua_thr_1 = val_qua_thr_1; +} + +#define SELECT_REFLECT(tl,tr,bl,br) ( \ + (tl) * is_top_left_1 \ + + \ + (tr) * is_top_rite_1 \ + + \ + (bl) * is_bot_left_1 \ + + \ + (br) * is_bot_rite_1 ) + +/* Call nohalo2 with an interpolator as a parameter. It'd be nice to + * do this with templates somehow :-( but I can't see a clean way to + * do it. + */ +#define NOHALO2_INTER( inter ) \ + template static void inline \ + nohalo2_ ## inter( PEL* restrict pout, \ + REGION* restrict in, \ + const int bands, \ + const int lskip, \ + const double absolute_x, \ + const double absolute_y ) \ + { \ + const double absolute_x_minus_half = absolute_x - .5; \ + const double absolute_y_minus_half = absolute_y - .5; \ + \ + const int initial_ix = FAST_PSEUDO_FLOOR (absolute_x); \ + const int initial_iy = FAST_PSEUDO_FLOOR (absolute_y); \ + \ + const double relative_x = absolute_x_minus_half - initial_ix; \ + const double relative_y = absolute_y_minus_half - initial_iy; \ + \ + const int relative_x_is_rite = ( relative_x >= 0. ); \ + const int relative_y_is_down = ( relative_y >= 0. ); \ + \ + const PEL* restrict pin = \ + (PEL *) IM_REGION_ADDR( \ + in, \ + initial_ix + relative_x_is_rite, \ + initial_iy + relative_y_is_down ); \ + \ + { \ + const T* restrict in = ( (T *) pin ); \ + \ + const int sign_of_relative_x = 2 * relative_x_is_rite - 1; \ + const int sign_of_relative_y = 2 * relative_y_is_down - 1; \ + \ + const int shift_back_1_pix = sign_of_relative_x * bands; \ + const int shift_back_1_row = sign_of_relative_y * lskip; \ + \ + const int shift_forw_1_pix = -shift_back_1_pix; \ + const int shift_forw_1_row = -shift_back_1_row; \ + \ + const int shift_back_2_pix = 2 * shift_back_1_pix; \ + const int shift_back_2_row = 2 * shift_back_1_row; \ + const int shift_forw_2_pix = 2 * shift_forw_1_pix; \ + const int shift_forw_2_row = 2 * shift_forw_1_row; \ + \ + const int uno_thr_shift = shift_back_2_row; \ + const int uno_fou_shift = shift_forw_1_pix + shift_back_2_row; \ + \ + const int dos_two_shift = shift_back_1_pix + shift_back_1_row; \ + const int dos_thr_shift = shift_back_1_row; \ + const int dos_fou_shift = shift_forw_1_pix + shift_back_1_row; \ + const int dos_fiv_shift = shift_forw_2_pix + shift_back_1_row; \ + \ + const int tre_one_shift = shift_back_2_pix; \ + const int tre_two_shift = shift_back_1_pix; \ + const int tre_thr_shift = 0; \ + const int tre_fou_shift = shift_forw_1_pix; \ + const int tre_fiv_shift = shift_forw_2_pix; \ + \ + const int qua_one_shift = shift_back_2_pix + shift_forw_1_row; \ + const int qua_two_shift = shift_back_1_pix + shift_forw_1_row; \ + const int qua_thr_shift = shift_forw_1_row; \ + const int qua_fou_shift = shift_forw_1_pix + shift_forw_1_row; \ + const int qua_fiv_shift = shift_forw_2_pix + shift_forw_1_row; \ + \ + const int cin_two_shift = shift_back_1_pix + shift_forw_2_row; \ + const int cin_thr_shift = shift_forw_2_row; \ + const int cin_fou_shift = shift_forw_1_pix + shift_forw_2_row; \ + \ + const double w = ( 2 * sign_of_relative_x ) * relative_x; \ + const double z = ( 2 * sign_of_relative_y ) * relative_y; \ + \ + const double relative_x_1 = .5 - w; \ + const double relative_y_1 = .5 - z; \ + \ + const int relative_x_is_rite_1 = ( relative_x_1 >= 0. ); \ + const int relative_y_is_down_1 = ( relative_y_1 >= 0. ); \ + const int relative_x_is_left_1 = 1 - relative_x_is_rite_1; \ + const int relative_y_is___up_1 = 1 - relative_y_is_down_1; \ + \ + const double is_bot_rite_1 = \ + relative_x_is_rite_1 * relative_y_is_down_1; \ + const double is_bot_left_1 = \ + relative_x_is_left_1 * relative_y_is_down_1; \ + const double is_top_rite_1 = \ + relative_x_is_rite_1 * relative_y_is___up_1;\ + const double is_top_left_1 = \ + relative_x_is_left_1 * relative_y_is___up_1; \ + \ + const int sign_of_relative_x_1 = 2 * relative_x_is_rite_1 - 1; \ + const int sign_of_relative_y_1 = 2 * relative_y_is_down_1 - 1; \ + \ + const double w_1 = ( 2 * sign_of_relative_x_1 ) * relative_x_1; \ + const double z_1 = ( 2 * sign_of_relative_y_1 ) * relative_y_1; \ + const double x_1 = 1. - w_1; \ + const double w_times_z_1 = w_1 * z_1; \ + const double x_times_z_1 = x_1 * z_1; \ + \ + const double w_times_y_over_4_1 = .25 * ( w_1 - w_times_z_1 ); \ + const double x_times_z_over_4_1 = .25 * x_times_z_1; \ + const double x_times_y_over_8_1 = .125 * ( x_1 - x_times_z_1 ); \ + \ + T* restrict out = (T *) pout; \ + \ + int band = bands; \ + \ + do \ + { \ + double uno_two_1; \ + double uno_thr_1; \ + double dos_one_1; \ + double dos_two_1; \ + double dos_thr_1; \ + double dos_fou_1; \ + double tre_one_1; \ + double tre_two_1; \ + double tre_thr_1; \ + double tre_fou_1; \ + double qua_two_1; \ + double qua_thr_1; \ + \ + double dos_two_2; \ + double four_times_dos_twothr_2; \ + double four_times_dostre_two_2; \ + double eight_times_dostre_twothr_2; \ + \ + nohalo_step1( in[uno_thr_shift], \ + in[uno_fou_shift], \ + in[dos_two_shift], \ + in[dos_thr_shift], \ + in[dos_fou_shift], \ + in[dos_fiv_shift], \ + in[tre_one_shift], \ + in[tre_two_shift], \ + in[tre_thr_shift], \ + in[tre_fou_shift], \ + in[tre_fiv_shift], \ + in[qua_one_shift], \ + in[qua_two_shift], \ + in[qua_thr_shift], \ + in[qua_fou_shift], \ + in[qua_fiv_shift], \ + in[cin_two_shift], \ + in[cin_thr_shift], \ + in[cin_fou_shift], \ + &uno_two_1, \ + &uno_thr_1, \ + &dos_one_1, \ + &dos_two_1, \ + &dos_thr_1, \ + &dos_fou_1, \ + &tre_one_1, \ + &tre_two_1, \ + &tre_thr_1, \ + &tre_fou_1, \ + &qua_two_1, \ + &qua_thr_1 ); \ + \ + nohalo_step2 ( \ + SELECT_REFLECT( uno_two_1, uno_thr_1, qua_two_1, qua_thr_1 ), \ + SELECT_REFLECT( uno_thr_1, uno_two_1, qua_thr_1, qua_two_1 ), \ + SELECT_REFLECT( dos_one_1, dos_fou_1, tre_one_1, tre_fou_1 ), \ + SELECT_REFLECT( dos_two_1, dos_thr_1, tre_two_1, tre_thr_1 ), \ + SELECT_REFLECT( dos_thr_1, dos_two_1, tre_thr_1, tre_two_1 ), \ + SELECT_REFLECT( dos_fou_1, dos_one_1, tre_fou_1, tre_one_1 ), \ + SELECT_REFLECT( tre_one_1, tre_fou_1, dos_one_1, dos_fou_1 ), \ + SELECT_REFLECT( tre_two_1, tre_thr_1, dos_two_1, dos_thr_1 ), \ + SELECT_REFLECT( tre_thr_1, tre_two_1, dos_thr_1, dos_two_1 ), \ + SELECT_REFLECT( tre_fou_1, tre_one_1, dos_fou_1, dos_one_1 ), \ + SELECT_REFLECT( qua_two_1, qua_thr_1, uno_two_1, uno_thr_1 ), \ + SELECT_REFLECT( qua_thr_1, qua_two_1, uno_thr_1, uno_two_1 ), \ + &dos_two_2, \ + &four_times_dos_twothr_2, \ + &four_times_dostre_two_2, \ + &eight_times_dostre_twothr_2 ); \ + \ + const T result = \ + bilinear_ ## inter( w_times_z_1, \ + x_times_z_over_4_1, \ + w_times_y_over_4_1, \ + x_times_y_over_8_1, \ + dos_two_2, \ + four_times_dos_twothr_2, \ + four_times_dostre_two_2, \ + eight_times_dostre_twothr_2 ); \ + \ + in++; \ + *out++ = result; \ + } while (--band); \ + } \ +} + +NOHALO2_INTER( fptypes ) +NOHALO2_INTER( withsign ) +NOHALO2_INTER( nosign ) + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsInterpolateNohalo2, vips_interpolate_nohalo2, + VIPS_TYPE_INTERPOLATE ); +} + +static void +vips_interpolate_nohalo2_interpolate( VipsInterpolate* restrict interpolate, + PEL* restrict out, + REGION* restrict in, + double absolute_x, + double absolute_y ) +{ + /* + * VIPS versions of Nicolas's pixel addressing values. + */ + const int actual_bands = in->im->Bands; + const int lskip = IM_REGION_LSKIP( in ) / IM_IMAGE_SIZEOF_ELEMENT( in->im ); + + /* + * Double bands for complex images: + */ + const int bands = + ( im_iscomplex( in->im ) ? 2 * actual_bands : actual_bands ); + +#define CALL( T, inter ) \ + nohalo2_ ## inter( out, \ + in, \ + bands, \ + lskip, \ + absolute_x, \ + absolute_y ); + + switch( in->im->BandFmt ) { + case IM_BANDFMT_UCHAR: + CALL( unsigned char, nosign ); + break; + + case IM_BANDFMT_CHAR: + CALL( signed char, withsign ); + break; + + case IM_BANDFMT_USHORT: + CALL( unsigned short, nosign ); + break; + + case IM_BANDFMT_SHORT: + CALL( signed short, withsign ); + break; + + case IM_BANDFMT_UINT: + CALL( unsigned int, nosign ); + break; + + case IM_BANDFMT_INT: + CALL( signed int, withsign ); + break; + + /* Complex images handled by doubling of bands, see above. + */ + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_COMPLEX: + CALL( float, fptypes ); + break; + + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_DPCOMPLEX: + CALL( double, fptypes ); + break; + + default: + g_assert( 0 ); + break; + } +} + +static void +vips_interpolate_nohalo2_class_init( VipsInterpolateNohalo2Class *klass ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( klass ); + VipsInterpolateClass *interpolate_class = + VIPS_INTERPOLATE_CLASS( klass ); + + object_class->nickname = "nohalo2"; + object_class->description = _( "Smoother and more edge-enhancing nohalo1" ); + + interpolate_class->interpolate = vips_interpolate_nohalo2_interpolate; + interpolate_class->window_size = 5; +} + +static void +vips_interpolate_nohalo2_init( VipsInterpolateNohalo2 *nohalo2 ) +{ +} diff --git a/libvips/resample/resample_dispatch.c b/libvips/resample/resample_dispatch.c new file mode 100644 index 00000000..05b1a3b0 --- /dev/null +++ b/libvips/resample/resample_dispatch.c @@ -0,0 +1,281 @@ +/* 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 +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* 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 */ +}; + +/* affinei args + */ +static im_arg_desc affinei_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INTERPOLATE( "interpolate" ), + 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_affinei via arg vector. + */ +static int +affinei_vec( im_object *argv ) +{ + VipsInterpolate *interpolate = VIPS_INTERPOLATE( argv[2] ); + double a = *((double *) argv[3]); + double b = *((double *) argv[4]); + double c = *((double *) argv[5]); + double d = *((double *) argv[6]); + double dx = *((double *) argv[7]); + double dy = *((double *) argv[8]); + int x = *((int *) argv[9]); + int y = *((int *) argv[10]); + int w = *((int *) argv[11]); + int h = *((int *) argv[12]); + + return( im_affinei( argv[0], argv[1], interpolate, + a, b, c, d, dx, dy, x, y, w, h ) ); +} + +/* Description of im_affinei. + */ +static im_function affinei_desc = { + "im_affinei", /* Name */ + "affine transform", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + affinei_vec, /* Dispatch function */ + IM_NUMBER( affinei_args ), /* Size of arg list */ + affinei_args /* Arg list */ +}; + +/* affinei_all args + */ +static im_arg_desc affinei_all_args[] = { + IM_INPUT_IMAGE( "in" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INTERPOLATE( "interpolate" ), + IM_INPUT_DOUBLE( "a" ), + IM_INPUT_DOUBLE( "b" ), + IM_INPUT_DOUBLE( "c" ), + IM_INPUT_DOUBLE( "d" ), + IM_INPUT_DOUBLE( "dx" ), + IM_INPUT_DOUBLE( "dy" ) +}; + +/* Call im_affinei_all via arg vector. + */ +static int +affinei_all_vec( im_object *argv ) +{ + VipsInterpolate *interpolate = VIPS_INTERPOLATE( argv[2] ); + double a = *((double *) argv[3]); + double b = *((double *) argv[4]); + double c = *((double *) argv[5]); + double d = *((double *) argv[6]); + double dx = *((double *) argv[7]); + double dy = *((double *) argv[8]); + + return( im_affinei_all( argv[0], argv[1], interpolate, + a, b, c, d, dx, dy ) ); +} + +/* Description of im_affinei_all. + */ +static im_function affinei_all_desc = { + "im_affinei_all", /* Name */ + "affine transform of whole image", + IM_FN_TRANSFORM | IM_FN_PIO, /* Flags */ + affinei_all_vec, /* Dispatch function */ + IM_NUMBER( affinei_all_args ), /* Size of arg list */ + affinei_all_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 */ +}; + +/* 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 */ +}; + +/* Package up all these functions. + */ +static im_function *resample_list[] = { + &affine_desc, + &affinei_desc, + &affinei_all_desc, + &similarity_area_desc, + &similarity_desc +}; + +/* Package of functions. + */ +im_package im__resample = { + "resample", + IM_NUMBER( resample_list ), + resample_list +}; diff --git a/libvips/resample/similarity.c b/libvips/resample/similarity.c new file mode 100644 index 00000000..f50d1bd0 --- /dev/null +++ b/libvips/resample/similarity.c @@ -0,0 +1,122 @@ +/* @(#) 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +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 ) ); +} + +/* 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/libvips/resample/snohalo1.cpp b/libvips/resample/snohalo1.cpp new file mode 100644 index 00000000..1cd48cd0 --- /dev/null +++ b/libvips/resample/snohalo1.cpp @@ -0,0 +1,606 @@ +/* snohalo level 1 interpolator + * + * (smooth nohalo = nohalo with custom antialiasing blur) + * + * Tweaks by N. Robidoux and J. Cupitt 4-17/3/09 + * + * Tweaks by N. Robidoux 25-28/5/09 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + * 2009 (c) Nicolas Robidoux + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "templates.h" + +/* Properties. + */ +enum { + PROP_BLUR = 1, + PROP_LAST +}; + +#define VIPS_TYPE_INTERPOLATE_SNOHALO1 \ + (vips_interpolate_snohalo1_get_type()) +#define VIPS_INTERPOLATE_SNOHALO1( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE_SNOHALO1, VipsInterpolateSnohalo1 )) +#define VIPS_INTERPOLATE_SNOHALO1_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE_SNOHALO1, VipsInterpolateSnohalo1Class)) +#define VIPS_IS_INTERPOLATE_SNOHALO1( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_SNOHALO1 )) +#define VIPS_IS_INTERPOLATE_SNOHALO1_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_SNOHALO1 )) +#define VIPS_INTERPOLATE_SNOHALO1_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE_SNOHALO1, VipsInterpolateSnohalo1Class )) + +typedef struct _VipsInterpolateSnohalo1 { + VipsInterpolate parent_object; + + double blur; +} VipsInterpolateSnohalo1; + +typedef struct _VipsInterpolateSnohalo1Class { + VipsInterpolateClass parent_class; + +} VipsInterpolateSnohalo1Class; + +static void inline +snohalo1( const double blur, + const double zer_two_in, + const double zer_thr_in, + const double uno_one_in, + const double uno_two_in, + const double uno_thr_in, + const double uno_fou_in, + const double dos_zer_in, + const double dos_one_in, + const double dos_two_in, + const double dos_thr_in, + const double dos_fou_in, + const double dos_fiv_in, + const double tre_zer_in, + const double tre_one_in, + const double tre_two_in, + const double tre_thr_in, + const double tre_fou_in, + const double tre_fiv_in, + const double qua_one_in, + const double qua_two_in, + const double qua_thr_in, + const double qua_fou_in, + const double cin_two_in, + const double cin_thr_in, + double* restrict r0, + double* restrict r1, + double* restrict r2, + double* restrict r3 ) +{ + const double beta = 1. + -.5 * blur; + const double gamma = .125 * blur; + + /* + * Computation of the blurred pixel values: + */ + const double uno_one_plus_zer_two_in = uno_one_in + zer_two_in; + const double uno_two_plus_zer_thr_in = uno_two_in + zer_thr_in; + + const double dos_zer_plus_uno_one_in = dos_zer_in + uno_one_in; + const double dos_one_plus_uno_two_in = dos_one_in + uno_two_in; + const double dos_two_plus_uno_thr_in = dos_two_in + uno_thr_in; + const double dos_thr_plus_uno_fou_in = dos_thr_in + uno_fou_in; + + const double tre_zer_plus_dos_one_in = tre_zer_in + dos_one_in; + const double tre_one_plus_dos_two_in = tre_one_in + dos_two_in; + const double tre_two_plus_dos_thr_in = tre_two_in + dos_thr_in; + const double tre_thr_plus_dos_fou_in = tre_thr_in + dos_fou_in; + const double tre_fou_plus_dos_fiv_in = tre_fou_in + dos_fiv_in; + + const double qua_one_plus_tre_two_in = qua_one_in + tre_two_in; + const double qua_two_plus_tre_thr_in = qua_two_in + tre_thr_in; + const double qua_thr_plus_tre_fou_in = qua_thr_in + tre_fou_in; + const double qua_fou_plus_tre_fiv_in = qua_fou_in + tre_fiv_in; + + const double cin_two_plus_qua_thr_in = cin_two_in + qua_thr_in; + const double cin_thr_plus_qua_fou_in = cin_thr_in + qua_fou_in; + + const double uno_two = + beta * uno_two_in + + + gamma * ( uno_one_plus_zer_two_in + dos_two_plus_uno_thr_in ); + + const double uno_thr = + beta * uno_thr_in + + + gamma * ( uno_two_plus_zer_thr_in + dos_thr_plus_uno_fou_in ); + + const double dos_one = + beta * dos_one_in + + + gamma * ( dos_zer_plus_uno_one_in + tre_one_plus_dos_two_in ); + + const double dos_two = + beta * dos_two_in + + + gamma * ( dos_one_plus_uno_two_in + tre_two_plus_dos_thr_in ); + + const double dos_thr = + beta * dos_thr_in + + + gamma * ( dos_two_plus_uno_thr_in + tre_thr_plus_dos_fou_in ); + + const double dos_fou = + beta * dos_fou_in + + + gamma * ( dos_thr_plus_uno_fou_in + tre_fou_plus_dos_fiv_in ); + + const double tre_one = + beta * tre_one_in + + + gamma * ( tre_zer_plus_dos_one_in + qua_one_plus_tre_two_in ); + + const double tre_two = + beta * tre_two_in + + + gamma * ( tre_one_plus_dos_two_in + qua_two_plus_tre_thr_in ); + + const double tre_thr = + beta * tre_thr_in + + + gamma * ( tre_two_plus_dos_thr_in + qua_thr_plus_tre_fou_in ); + + const double tre_fou = + beta * tre_fou_in + + + gamma * ( tre_thr_plus_dos_fou_in + qua_fou_plus_tre_fiv_in ); + + const double qua_two = + beta * qua_two_in + + + gamma * ( qua_one_plus_tre_two_in + cin_two_plus_qua_thr_in ); + + const double qua_thr = + beta * qua_thr_in + + + gamma * ( qua_two_plus_tre_thr_in + cin_thr_plus_qua_fou_in ); + + /* + * Dos(s) horizontal differences: + */ + const double prem_dos = dos_two - dos_one; + const double deux_dos = dos_thr - dos_two; + const double troi_dos = dos_fou - dos_thr; + /* + * Tre(s) horizontal differences: + */ + const double prem_tre = tre_two - tre_one; + const double deux_tre = tre_thr - tre_two; + const double troi_tre = tre_fou - tre_thr; + /* + * Two vertical differences: + */ + const double prem_two = dos_two - uno_two; + const double deux_two = tre_two - dos_two; + const double troi_two = qua_two - tre_two; + /* + * Thr(ee) vertical differences: + */ + const double prem_thr = dos_thr - uno_thr; + const double deux_thr = tre_thr - dos_thr; + const double troi_thr = qua_thr - tre_thr; + + /* + * Products useful for minmod: + */ + const double deux_prem_dos = deux_dos * prem_dos; + const double deux_deux_dos = deux_dos * deux_dos; + const double deux_troi_dos = deux_dos * troi_dos; + + const double deux_prem_two = deux_two * prem_two; + const double deux_deux_two = deux_two * deux_two; + const double deux_troi_two = deux_two * troi_two; + + const double deux_prem_tre = deux_tre * prem_tre; + const double deux_deux_tre = deux_tre * deux_tre; + const double deux_troi_tre = deux_tre * troi_tre; + + const double deux_prem_thr = deux_thr * prem_thr; + const double deux_deux_thr = deux_thr * deux_thr; + const double deux_troi_thr = deux_thr * troi_thr; + + /* + * Differences useful for minmod: + */ + const double deux_prem_minus_deux_deux_dos = deux_prem_dos - deux_deux_dos; + const double deux_troi_minus_deux_deux_dos = deux_troi_dos - deux_deux_dos; + + const double deux_prem_minus_deux_deux_two = deux_prem_two - deux_deux_two; + const double deux_troi_minus_deux_deux_two = deux_troi_two - deux_deux_two; + + const double deux_prem_minus_deux_deux_tre = deux_prem_tre - deux_deux_tre; + const double deux_troi_minus_deux_deux_tre = deux_troi_tre - deux_deux_tre; + + const double deux_prem_minus_deux_deux_thr = deux_prem_thr - deux_deux_thr; + const double deux_troi_minus_deux_deux_thr = deux_troi_thr - deux_deux_thr; + + /* + * The following terms are computed here to put "space" between the + * computation of components of flag variables and their use: + */ + const double twice_dos_two_plus_dos_thr = ( dos_two + dos_thr ) * 2.; + const double twice_dos_two_plus_tre_two = ( dos_two + tre_two ) * 2.; + const double twice_deux_thr_plus_deux_dos = ( deux_thr + deux_dos ) * 2.; + + /* + * Compute the needed "right" (at the boundary between one input + * pixel areas) double resolution pixel value: + */ + const double four_times_dos_twothr = + twice_dos_two_plus_dos_thr + + + FAST_MINMOD( deux_dos, prem_dos, deux_prem_dos, + deux_prem_minus_deux_deux_dos ) + - + FAST_MINMOD( deux_dos, troi_dos, deux_troi_dos, + deux_troi_minus_deux_deux_dos ); + + /* + * Compute the needed "down" double resolution pixel value: + */ + const double four_times_dostre_two = + twice_dos_two_plus_tre_two + + + FAST_MINMOD( deux_two, prem_two, deux_prem_two, + deux_prem_minus_deux_deux_two ) + - + FAST_MINMOD( deux_two, troi_two, deux_troi_two, + deux_troi_minus_deux_deux_two ); + + /* + * Compute the "diagonal" (at the boundary between four input + * pixel areas) double resolution pixel value: + */ + const double eight_times_dostre_twothr = + twice_deux_thr_plus_deux_dos + + + FAST_MINMOD( deux_tre, prem_tre, deux_prem_tre, + deux_prem_minus_deux_deux_tre ) + - + FAST_MINMOD( deux_tre, troi_tre, deux_troi_tre, + deux_troi_minus_deux_deux_tre ) + + + FAST_MINMOD( deux_thr, prem_thr, deux_prem_thr, + deux_prem_minus_deux_deux_thr ) + - + FAST_MINMOD( deux_thr, troi_thr, deux_troi_thr, + deux_troi_minus_deux_deux_thr ) + + + four_times_dos_twothr + + + four_times_dostre_two; + + /* + * Return the first newly computed double density values: + */ + *r0 = dos_two; + *r1 = four_times_dos_twothr; + *r2 = four_times_dostre_two; + *r3 = eight_times_dostre_twothr; +} + +/* Call snohalo1 with an interpolator as a parameter. + * It'd be nice to do this with templates somehow :-( but I can't see a + * clean way to do it. + */ +#define SNOHALO1_INTER( inter ) \ + template static void inline \ + snohalo1_ ## inter( PEL* restrict pout, \ + const PEL* restrict pin, \ + const int bands, \ + const int lskip, \ + const double blur, \ + const double relative_x, \ + const double relative_y ) \ + { \ + T* restrict out = (T *) pout; \ + \ + const int relative_x_is_rite = ( relative_x >= 0. ); \ + const int relative_y_is_down = ( relative_y >= 0. ); \ + \ + const int sign_of_relative_x = 2 * relative_x_is_rite - 1; \ + const int sign_of_relative_y = 2 * relative_y_is_down - 1; \ + \ + const int corner_reflection_shift = \ + relative_x_is_rite * bands + relative_y_is_down * lskip; \ + \ + const int shift_back_1_pix = sign_of_relative_x * bands; \ + const int shift_back_1_row = sign_of_relative_y * lskip; \ + \ + const T* restrict in = ( (T *) pin ) + corner_reflection_shift; \ + \ + const int shift_forw_1_pix = -shift_back_1_pix; \ + const int shift_forw_1_row = -shift_back_1_row; \ + \ + const int shift_back_2_pix = 2 * shift_back_1_pix; \ + const int shift_back_2_row = 2 * shift_back_2_row; \ + \ + const double w = ( 2 * sign_of_relative_x ) * relative_x; \ + const double z = ( 2 * sign_of_relative_y ) * relative_y; \ + \ + const int shift_forw_2_pix = 2 * shift_forw_1_pix; \ + const int shift_forw_2_row = 2 * shift_forw_1_row; \ + \ + const int shift_forw_3_pix = 3 * shift_forw_1_pix; \ + const int shift_forw_3_row = 3 * shift_forw_1_row; \ + \ + const int zer_two_shift = shift_back_2_row; \ + const int zer_thr_shift = shift_forw_1_pix + shift_back_2_row; \ + \ + const int uno_one_shift = shift_back_1_pix + shift_back_1_row; \ + const int uno_two_shift = shift_back_1_row; \ + const int uno_thr_shift = shift_forw_1_pix + shift_back_1_row; \ + const int uno_fou_shift = shift_forw_2_pix + shift_back_1_row; \ + \ + const double x = 1. - w; \ + const double w_times_z = w * z; \ + \ + const int dos_zer_shift = shift_back_2_pix; \ + const int dos_one_shift = shift_back_1_pix; \ + const int dos_two_shift = 0; \ + const int dos_thr_shift = shift_forw_1_pix; \ + const int dos_fou_shift = shift_forw_2_pix; \ + const int dos_fiv_shift = shift_forw_3_pix; \ + \ + const int tre_zer_shift = shift_back_2_pix + shift_forw_1_row; \ + const int tre_one_shift = shift_back_1_pix + shift_forw_1_row; \ + const int tre_two_shift = shift_forw_1_row; \ + const int tre_thr_shift = shift_forw_1_pix + shift_forw_1_row; \ + const int tre_fou_shift = shift_forw_2_pix + shift_forw_1_row; \ + const int tre_fiv_shift = shift_forw_3_pix + shift_forw_1_row; \ + \ + const double x_times_z = x * z; \ + \ + const int qua_one_shift = shift_back_1_pix + shift_forw_2_row; \ + const int qua_two_shift = shift_forw_2_row; \ + const int qua_thr_shift = shift_forw_1_pix + shift_forw_2_row; \ + const int qua_fou_shift = shift_forw_2_pix + shift_forw_2_row; \ + \ + const int cin_two_shift = shift_forw_3_row; \ + const int cin_thr_shift = shift_forw_1_pix + shift_forw_3_row; \ + \ + const double w_times_y_over_4 = .25 * ( w - w_times_z ); \ + const double x_times_z_over_4 = .25 * x_times_z; \ + const double x_times_y_over_8 = .125 * ( x - x_times_z ); \ + \ + int band = bands; \ + \ + do \ + { \ + double dos_two; \ + double four_times_dos_twothr; \ + double four_times_dostre_two; \ + double eight_times_dostre_twothr; \ + \ + snohalo1( blur, \ + in[zer_two_shift], in[zer_thr_shift], \ + in[uno_one_shift], in[uno_two_shift], \ + in[uno_thr_shift], in[uno_fou_shift], \ + in[dos_zer_shift], in[dos_one_shift], \ + in[dos_two_shift], in[dos_thr_shift], \ + in[dos_fou_shift], in[dos_fiv_shift], \ + in[tre_zer_shift], in[tre_one_shift], \ + in[tre_two_shift], in[tre_thr_shift], \ + in[tre_fou_shift], in[tre_fiv_shift], \ + in[qua_one_shift], in[qua_two_shift], \ + in[qua_thr_shift], in[qua_fou_shift], \ + in[cin_two_shift], in[cin_thr_shift], \ + &dos_two, \ + &four_times_dos_twothr, \ + &four_times_dostre_two, \ + &eight_times_dostre_twothr ); \ + \ + const T result = bilinear_ ## inter( w_times_z, \ + x_times_z_over_4, \ + w_times_y_over_4, \ + x_times_y_over_8, \ + dos_two, \ + four_times_dos_twothr, \ + four_times_dostre_two, \ + eight_times_dostre_twothr ); \ + \ + in++; \ + *out++ = result; \ + } while (--band); \ + } + +SNOHALO1_INTER( fptypes ) +SNOHALO1_INTER( withsign ) +SNOHALO1_INTER( nosign ) + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsInterpolateSnohalo1, vips_interpolate_snohalo1, + VIPS_TYPE_INTERPOLATE ); +} + +static void +vips_interpolate_snohalo1_interpolate( VipsInterpolate* restrict interpolate, + PEL* restrict out, + REGION* restrict in, + double absolute_x, + double absolute_y ) +{ + VipsInterpolateSnohalo1 *snohalo1 = + VIPS_INTERPOLATE_SNOHALO1( interpolate ); + /* + * VIPS versions of Nicolas's pixel addressing values. + */ + const int actual_bands = in->im->Bands; + const int lskip = IM_REGION_LSKIP( in ) / IM_IMAGE_SIZEOF_ELEMENT( in->im ); + + const double absolute_y_minus_half = absolute_y - .5; + const double absolute_x_minus_half = absolute_x - .5; + /* + * floor's surrogate FAST_PSEUDO_FLOOR is used to make sure that the + * transition through 0 is smooth. If it is known that absolute_x + * and absolute_y will never be less than 0, plain cast---that is, + * const int ix = absolute_x---should be used instead. Actually, + * any function which agrees with floor for non-integer values, and + * picks one of the two possibilities for integer values, can be + * used. FAST_PSEUDO_FLOOR fits the bill. + * + * Then, x is the x-coordinate of the sampling point relative to the + * position of the center of the convex hull of the 2x2 block of + * closest pixels. Similarly for y. Range of values: [-.5,.5). + */ + const int iy = FAST_PSEUDO_FLOOR (absolute_y); + const double relative_y = absolute_y_minus_half - iy; + const int ix = FAST_PSEUDO_FLOOR (absolute_x); + const double relative_x = absolute_x_minus_half - ix; + + /* + * Move the pointer to (the first band of) the top/left pixel of the + * 2x2 group of pixel centers which contains the sampling location + * in its convex hull: + */ + const PEL* restrict p = (PEL *) IM_REGION_ADDR( in, ix, iy ); + + /* + * Double bands for complex images: + */ + const int bands = + ( im_iscomplex( in->im ) ? 2 * actual_bands : actual_bands ); + +#define CALL( T, inter ) \ + snohalo1_ ## inter( out, \ + p, \ + bands, \ + lskip, \ + snohalo1->blur, \ + relative_x, \ + relative_y ); + + switch( in->im->BandFmt ) { + case IM_BANDFMT_UCHAR: + CALL( unsigned char, nosign ); + break; + + case IM_BANDFMT_CHAR: + CALL( signed char, withsign ); + break; + + case IM_BANDFMT_USHORT: + CALL( unsigned short, nosign ); + break; + + case IM_BANDFMT_SHORT: + CALL( signed short, withsign ); + break; + + case IM_BANDFMT_UINT: + CALL( unsigned int, nosign ); + break; + + case IM_BANDFMT_INT: + CALL( signed int, withsign ); + break; + + /* Complex images handled by doubling of bands, see above. + */ + case IM_BANDFMT_FLOAT: + case IM_BANDFMT_COMPLEX: + CALL( float, fptypes ); + break; + + case IM_BANDFMT_DOUBLE: + case IM_BANDFMT_DPCOMPLEX: + CALL( double, fptypes ); + break; + + default: + g_assert( 0 ); + break; + } +} + +static void +vips_interpolate_snohalo1_class_init( VipsInterpolateSnohalo1Class *klass ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( klass ); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( klass ); + VipsInterpolateClass *interpolate_class = + VIPS_INTERPOLATE_CLASS( klass ); + + GParamSpec *pspec; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "snohalo1"; + object_class->description = _( "Nohalo level 1 with antialiasing blur" ); + + interpolate_class->interpolate = + vips_interpolate_snohalo1_interpolate; + interpolate_class->window_size = 6; + + /* Create properties. + */ + pspec = + g_param_spec_double( "blur", + _( "Blur" ), + _( "Antialiasing (diagonal straightening) blur amount" ), + 0., 1., 1., + (GParamFlags) G_PARAM_READWRITE ); + g_object_class_install_property( gobject_class, + PROP_BLUR, pspec ); + vips_object_class_install_argument( object_class, pspec, + VIPS_ARGUMENT_SET_ONCE, + G_STRUCT_OFFSET( VipsInterpolateSnohalo1, blur ) ); + +} + +static void +vips_interpolate_snohalo1_init( VipsInterpolateSnohalo1 *snohalo1 ) +{ +} diff --git a/libvips/resample/templates.h b/libvips/resample/templates.h new file mode 100644 index 00000000..ed7ae10d --- /dev/null +++ b/libvips/resample/templates.h @@ -0,0 +1,255 @@ +/* various interpolation templates + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + * FAST_PSEUDO_FLOOR is a floor and floorf replacement which has been + * found to be faster on several linux boxes than the library + * version. It returns the floor of its argument unless the argument + * is a negative integer, in which case it returns one less than the + * floor. For example: + * + * FAST_PSEUDO_FLOOR(0.5) = 0 + * + * FAST_PSEUDO_FLOOR(0.) = 0 + * + * FAST_PSEUDO_FLOOR(-.5) = -1 + * + * as expected, but + * + * FAST_PSEUDO_FLOOR(-1.) = -2 + * + * The locations of the discontinuities of FAST_PSEUDO_FLOOR are the + * same as floor and floorf; it is just that at negative integers the + * function is discontinuous on the right instead of the left. + */ +#define FAST_PSEUDO_FLOOR(x) ( (int)(x) - ( (x) < 0. ) ) + +/* + * FAST_MINMOD is an implementation of the minmod function which only + * needs two conditional moves. (Nicolas: I think that this may be + * the very first two branch minmod.) The product of the two arguments + * and a useful difference involving them are precomputed as far ahead + * of branching as possible. + */ +#define FAST_MINMOD(a,b,ab,abminusaa) \ + ( (ab)>=0. ? ( (abminusaa)>=0. ? (a) : (b) ) : 0. ) + +/* + * Comment from Nicolas: I don't understand why the following restrict + * defs cannot be offloaded to config files. + */ +#ifndef restrict +#ifdef __restrict +#define restrict __restrict +#else +#ifdef __restrict__ +#define restrict __restrict__ +#else +#define restrict +#endif +#endif +#endif + +/* + * Various bilinear implementation templates. Note that no clampling + * is used: There is an assumption that the data is such that + * over/underflow is not an issue: + */ +/* + * Bilinear interpolation for float and double types. The first four + * inputs are weights, the last four are the corresponding pixel + * values: + */ +template static T inline +bilinear_fptypes( + const double w_times_z, + const double x_times_z, + const double w_times_y, + const double x_times_y, + const double tre_thr, + const double tre_thrfou, + const double trequa_thr, + const double trequa_thrfou ) +{ + const T newval = + w_times_z * tre_thr + + x_times_z * tre_thrfou + + w_times_y * trequa_thr + + x_times_y * trequa_thrfou; + + return( newval ); +} + +/* + * Bilinear interpolation for signed integer types: + */ +template static T inline +bilinear_withsign( + const double w_times_z, + const double x_times_z, + const double w_times_y, + const double x_times_y, + const double tre_thr, + const double tre_thrfou, + const double trequa_thr, + const double trequa_thrfou ) +{ + const double val = + w_times_z * tre_thr + + x_times_z * tre_thrfou + + w_times_y * trequa_thr + + x_times_y * trequa_thrfou; + + const int sign_of_val = 2 * ( val >= 0. ) - 1; + + const int rounded_abs_val = .5 + sign_of_val * val; + + const T newval = sign_of_val * rounded_abs_val; + + return( newval ); +} + +/* + * Bilinear Interpolation for unsigned integer types: + */ +template static T inline +bilinear_nosign( + const double w_times_z, + const double x_times_z, + const double w_times_y, + const double x_times_y, + const double tre_thr, + const double tre_thrfou, + const double trequa_thr, + const double trequa_thrfou ) +{ + const T newval = + w_times_z * tre_thr + + x_times_z * tre_thrfou + + w_times_y * trequa_thr + + x_times_y * trequa_thrfou + + 0.5; + + return( newval ); +} + +/* + * Bicubic (Catmull-Rom) interpolation templates: + */ + +/* Fixed-point integer bicubic, used for 8 and 16-bit types. + */ +template static int inline +bicubic_int( + const T uno_one, const T uno_two, const T uno_thr, const T uno_fou, + const T dos_one, const T dos_two, const T dos_thr, const T dos_fou, + const T tre_one, const T tre_two, const T tre_thr, const T tre_fou, + const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, + const int* restrict cx, const int* restrict cy ) +{ + const int r0 = + (cx[0] * uno_one + + cx[1] * uno_two + + cx[2] * uno_thr + + cx[3] * uno_fou) >> VIPS_INTERPOLATE_SHIFT; + + const int r1 = + (cx[0] * dos_one + + cx[1] * dos_two + + cx[2] * dos_thr + + cx[3] * dos_fou) >> VIPS_INTERPOLATE_SHIFT; + + const int r2 = + (cx[0] * tre_one + + cx[1] * tre_two + + cx[2] * tre_thr + + cx[3] * tre_fou) >> VIPS_INTERPOLATE_SHIFT; + + const int r3 = + (cx[0] * qua_one + + cx[1] * qua_two + + cx[2] * qua_thr + + cx[3] * qua_fou) >> VIPS_INTERPOLATE_SHIFT; + + return( (cy[0] * r0 + + cy[1] * r1 + + cy[2] * r2 + + cy[3] * r3) >> VIPS_INTERPOLATE_SHIFT ); +} + +/* Floating-point bicubic, used for int/float/double types. + */ +template static T inline +bicubic_float( + const T uno_one, const T uno_two, const T uno_thr, const T uno_fou, + const T dos_one, const T dos_two, const T dos_thr, const T dos_fou, + const T tre_one, const T tre_two, const T tre_thr, const T tre_fou, + const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, + const double* restrict cx, const double* restrict cy ) +{ + return( + cy[0] * (cx[0] * uno_one + + cx[1] * uno_two + + cx[2] * uno_thr + + cx[3] * uno_fou) + + + cy[1] * (cx[0] * dos_one + + cx[1] * dos_two + + cx[2] * dos_thr + + cx[3] * dos_fou) + + + cy[2] * (cx[0] * tre_one + + cx[1] * tre_two + + cx[2] * tre_thr + + cx[3] * tre_fou) + + + cy[3] * (cx[0] * qua_one + + cx[1] * qua_two + + cx[2] * qua_thr + + cx[3] * qua_fou) ); +} + +/* Given an offset in [0,1] (we can have x == 1 when building tables), + * calculate c0, c1, c2, c3, the catmull-rom coefficients. This is called + * from the interpolator as well as from the table builder. + */ +static void inline +calculate_coefficients_catmull( const double x, double c[4] ) +{ + const double dx = 1. - x; + const double x2 = dx * x; + const double mx2 = -.5 * x2; + + g_assert( x >= 0 && x <= 1 ); + + c[0] = mx2 * dx; + c[1] = x2 * (-1.5 * x + 1.) + dx; + c[2] = 1. - (mx2 + c[1]); + c[3] = mx2 * x; +} diff --git a/libvips/resample/transform.c b/libvips/resample/transform.c new file mode 100644 index 00000000..20abddfa --- /dev/null +++ b/libvips/resample/transform.c @@ -0,0 +1,242 @@ +/* affine transforms + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* 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( const 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 ); +} + +/* Combine two transformations. out can be one of the ins. + */ +int +im__transform_add( const Transformation *in1, const 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( const 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 pixel coordinate through the transform. + */ +void +im__transform_forward_point( const Transformation *trn, + const double x, const 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_invert_point( const Transformation *trn, + const double x, const 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; +} + +typedef void (*transform_fn)( const Transformation *, + const double, const double, double*, double* ); + +/* Transform a rect using a point transformer. + */ +static void +transform_rect( const Transformation *trn, transform_fn transform, + const Rect *in, /* In input space */ + Rect *out ) /* In output space */ +{ + double x1, y1; /* Map corners */ + double x2, y2; + double x3, y3; + double x4, y4; + double left, right, top, bottom; + + /* Map input Rect. + */ + transform( trn, in->left, in->top, &x1, &y1 ); + transform( trn, in->left, IM_RECT_BOTTOM( in ), &x3, &y3 ); + transform( trn, IM_RECT_RIGHT( in ), in->top, &x2, &y2 ); + transform( trn, IM_RECT_RIGHT( in ), IM_RECT_BOTTOM( in ), &x4, &y4 ); + + /* Find bounding box for these four corners. Round-to-nearest to try + * to stop rounding errors growing images. + */ + 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 ) ) ); + + out->left = IM_RINT( left ); + out->top = IM_RINT( top ); + out->width = IM_RINT( right - left ); + out->height = IM_RINT( bottom - top ); +} + +/* Given an area in the input image, calculate the bounding box for those + * pixels in the output image. + */ +void +im__transform_forward_rect( const Transformation *trn, + const Rect *in, /* In input space */ + Rect *out ) /* In output space */ +{ + transform_rect( trn, im__transform_forward_point, in, out ); +} + +/* Given an area in the output image, calculate the bounding box for the + * corresponding pixels in the input image. + */ +void +im__transform_invert_rect( const Transformation *trn, + const Rect *in, /* In output space */ + Rect *out ) /* In input space */ +{ + transform_rect( trn, im__transform_invert_point, in, out ); +} + +/* Set output area of trn so that it just holds all of our input pels. + */ +void +im__transform_set_area( Transformation *trn ) +{ + im__transform_forward_rect( trn, &trn->iarea, &trn->oarea ); +} diff --git a/libvips/resample/yafrsmooth.cpp b/libvips/resample/yafrsmooth.cpp new file mode 100644 index 00000000..a4a382a5 --- /dev/null +++ b/libvips/resample/yafrsmooth.cpp @@ -0,0 +1,803 @@ +/* yafrsmooth interpolator + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + * 2008 (c) Nicolas Robidoux (developer of Yet Another Fast + * Resampler). + * + * Acknowledgement: N. Robidoux's research on YAFRSMOOTH funded in part by + * an NSERC (National Science and Engineering Research Council of + * Canada) Discovery Grant. + */ + +/* Hacked for vips by J. Cupitt, 12/11/08. + * + * Bicubic component replaced with the one from bicubbic.cpp. + */ + +/* + * YAFRSMOOTH = Yet Another Fast Resampler + * + * Yet Another Fast Resampler is a nonlinear resampler which consists + * of a linear scheme (in this version, Catmull-Rom) plus a nonlinear + * sharpening correction the purpose of which is the straightening of + * diagonal interfaces between flat colour areas. + * + * Key properties: + * + * YAFRSMOOTH (smooth) is interpolatory: + * + * If asked for the value at the center of an input pixel, it will + * return the corresponding value, unchanged. + * + * YAFRSMOOTH (smooth) preserves local averages: + * + * The average of the reconstructed intensity surface over any region + * is the same as the average of the piecewise constant surface with + * values over pixel areas equal to the input pixel values (the + * "nearest neighbour" surface), except for a small amount of blur at + * the boundary of the region. More precicely: YAFRSMOOTH (smooth) is a box + * filtered exact area method. + * + * Main weaknesses of YAFRSMOOTH (smooth): + * + * Weakness 1: YAFRSMOOTH (smooth) improves on Catmull-Rom only for images + * with at least a little bit of smoothness. + * + * Weakness 2: Catmull-Rom introduces a lot of haloing. YAFRSMOOTH (smooth) + * is based on Catmull-Rom, and consequently it too introduces a lot + * of haloing. + * + * More details regarding Weakness 1: + * + * If a portion of the image is such that every pixel has immediate + * neighbours in the horizontal and vertical directions which have + * exactly the same pixel value, then YAFRSMOOTH (smooth) boils down to + * Catmull-Rom, and the computation of the correction is a waste. + * Extreme case: If all the pixels are either pure black or pure white + * in some region, as in some text images (more generally, if the + * region is "bichromatic"), then the YAFRSMOOTH (smooth) correction is 0 in + * the interior of the bichromatic region. + */ + + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "templates.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* "fast" floor() ... on my laptop, anyway. + */ +#define FLOOR( V ) ((V) >= 0 ? (int)(V) : (int)((V) - 1)) + +#ifndef restrict +#ifdef __restrict +#define restrict __restrict +#else +#ifdef __restrict__ +#define restrict __restrict__ +#else +#define restrict +#endif +#endif +#endif + +/* Scale sharpening by this to normalise. + */ +#define SMOOTH_SHARPENING_SCALE (0.453125f) + +/* Properties. + */ +enum { + PROP_SHARPENING = 1, + PROP_LAST +}; + + +#define VIPS_TYPE_INTERPOLATE_YAFRSMOOTH \ + (vips_interpolate_yafrsmooth_get_type()) +#define VIPS_INTERPOLATE_YAFRSMOOTH( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE_YAFRSMOOTH, VipsInterpolateYafrsmooth )) +#define VIPS_INTERPOLATE_YAFRSMOOTH_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE_YAFRSMOOTH, VipsInterpolateYafrsmoothClass)) +#define VIPS_IS_INTERPOLATE_YAFRSMOOTH( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_YAFRSMOOTH )) +#define VIPS_IS_INTERPOLATE_YAFRSMOOTH_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_YAFRSMOOTH )) +#define VIPS_INTERPOLATE_YAFRSMOOTH_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE_YAFRSMOOTH, VipsInterpolateYafrsmoothClass )) + +typedef struct _VipsInterpolateYafrsmooth { + VipsInterpolate parent_object; + + /* "sharpening" is a continuous method parameter which is + * proportional to the amount of "diagonal straightening" which the + * nonlinear correction part of the method may add to the underlying + * linear scheme. You may also think of it as a sharpening + * parameter: higher values correspond to more sharpening, and + * negative values lead to strange looking effects. + * + * The default value is sharpening = 29/32 when the scheme being + * "straightened" is Catmull-Rom---as is the case here. This value + * fixes key pixel values near the diagonal boundary between two + * monochrome regions (the diagonal boundary pixel values being set + * to the halfway colour). + * + * If resampling seems to add unwanted texture artifacts, push + * sharpening toward 0. It is not generally not recommended to set + * sharpening to a value larger than 4. + * + * Sharpening is halved because the .5 which has to do with the + * relative coordinates of the evaluation points (which has to do + * with .5*rite_width etc) is folded into the constant to save + * flops. Consequently, the largest recommended value of + * sharpening_over_two is 2=4/2. + * + * In order to simplify interfacing with users, the parameter which + * should be set by the user is normalized so that user_sharpening = + * 1 when sharpening is equal to the recommended value. Consistently + * with the above discussion, values of user_sharpening between 0 + * and about 3.625 give good results. + */ + double sharpening; +} VipsInterpolateYafrsmooth; + +typedef struct _VipsInterpolateYafrsmoothClass { + VipsInterpolateClass parent_class; + + /* Precalculated 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. + */ + + /* We could keep a large set of 2d 4x4 matricies, but this actually + * works out slower, since for many resizes the thing will no longer + * fit in L1. + */ + int matrixi[VIPS_TRANSFORM_SCALE + 1][4]; + double matrixf[VIPS_TRANSFORM_SCALE + 1][4]; +} VipsInterpolateYafrsmoothClass; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsInterpolateYafrsmooth, vips_interpolate_yafrsmooth, + VIPS_TYPE_INTERPOLATE ); +} + +/* T is the type of pixels we are computing, D is a type large enough to hold + * (Ta - Tb) ** 2. + */ + +/* The 16 values for this interpolation, four constants for this + * interpolation position. + */ + +template static float inline +yafrsmooth( + const T uno_one, const T uno_two, const T uno_thr, const T uno_fou, + const T dos_one, const T dos_two, const T dos_thr, const T dos_fou, + const T tre_one, const T tre_two, const T tre_thr, const T tre_fou, + const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, + const double *c ) +{ + /* + * Computation of the YAFRSMOOTH correction: + * + * Basically, if two consecutive pixel value differences have the + * same sign, the smallest one (in absolute value) is taken to be + * the corresponding slope. If they don't have the same sign, the + * corresponding slope is set to 0. + * + * Four such pairs (vertical and horizontal) of slopes need to be + * computed, one pair for each of the pixels which potentially + * overlap the unit area centered at the interpolation point. + */ + /* + * Beginning of the computation of the "up" horizontal slopes: + */ + const D prem__up = dos_two - dos_one; + const D deux__up = dos_thr - dos_two; + const D troi__up = dos_fou - dos_thr; + /* + * "down" horizontal slopes: + */ + const D prem_dow = tre_two - tre_one; + const D deux_dow = tre_thr - tre_two; + const D troi_dow = tre_fou - tre_thr; + /* + * "left" vertical slopes: + */ + const D prem_left = dos_two - uno_two; + const D deux_left = tre_two - dos_two; + const D troi_left = qua_two - tre_two; + /* + * "right" vertical slopes: + */ + const D prem_rite = dos_thr - uno_thr; + const D deux_rite = tre_thr - dos_thr; + const D troi_rite = qua_thr - tre_thr; + + /* + * Back to "up": + */ + const D prem__up_squared = prem__up * prem__up; + const D deux__up_squared = deux__up * deux__up; + const D troi__up_squared = troi__up * troi__up; + /* + * Back to "down": + */ + const D prem_dow_squared = prem_dow * prem_dow; + const D deux_dow_squared = deux_dow * deux_dow; + const D troi_dow_squared = troi_dow * troi_dow; + /* + * Back to "left": + */ + const D prem_left_squared = prem_left * prem_left; + const D deux_left_squared = deux_left * deux_left; + const D troi_left_squared = troi_left * troi_left; + /* + * Back to "right": + */ + const D prem_rite_squared = prem_rite * prem_rite; + const D deux_rite_squared = deux_rite * deux_rite; + const D troi_rite_squared = troi_rite * troi_rite; + + /* + * "up": + */ + const D prem__up_times_deux__up = prem__up * deux__up; + const D deux__up_times_troi__up = deux__up * troi__up; + /* + * "down": + */ + const D prem_dow_times_deux_dow = prem_dow * deux_dow; + const D deux_dow_times_troi_dow = deux_dow * troi_dow; + /* + * "left": + */ + const D prem_left_times_deux_left = prem_left * deux_left; + const D deux_left_times_troi_left = deux_left * troi_left; + /* + * "right": + */ + const D prem_rite_times_deux_rite = prem_rite * deux_rite; + const D deux_rite_times_troi_rite = deux_rite * troi_rite; + + /* + * Branching parts of the computation of the YAFRSMOOTH correction + * (could be unbranched using arithmetic branching and C99 math + * intrinsics, although the compiler may be smart enough to remove + * the branching on its own): + */ + /* + * "up": + */ + const D prem__up_vs_deux__up = + prem__up_squared < deux__up_squared ? prem__up : deux__up; + const D deux__up_vs_troi__up = + deux__up_squared < troi__up_squared ? deux__up : troi__up; + /* + * "down": + */ + const D prem_dow_vs_deux_dow = + prem_dow_squared < deux_dow_squared ? prem_dow : deux_dow; + const D deux_dow_vs_troi_dow = + deux_dow_squared < troi_dow_squared ? deux_dow : troi_dow; + /* + * "left": + */ + const D prem_left_vs_deux_left = + prem_left_squared < deux_left_squared ? prem_left : deux_left; + const D deux_left_vs_troi_left = + deux_left_squared < troi_left_squared ? deux_left : troi_left; + /* + * "right": + */ + const D prem_rite_vs_deux_rite = + prem_rite_squared < deux_rite_squared ? prem_rite : deux_rite; + const D deux_rite_vs_troi_rite = + deux_rite_squared < troi_rite_squared ? deux_rite : troi_rite; + + /* + * Computation of the YAFRSMOOTH slopes. + */ + /* + * "up": + */ + const D mx_left__up = + prem__up_times_deux__up < 0.f ? 0.f : prem__up_vs_deux__up; + const D mx_rite__up = + deux__up_times_troi__up < 0.f ? 0.f : deux__up_vs_troi__up; + /* + * "down": + */ + const D mx_left_dow = + prem_dow_times_deux_dow < 0.f ? 0.f : prem_dow_vs_deux_dow; + const D mx_rite_dow = + deux_dow_times_troi_dow < 0.f ? 0.f : deux_dow_vs_troi_dow; + /* + * "left": + */ + const D my_left__up = + prem_left_times_deux_left < 0.f ? 0.f : prem_left_vs_deux_left; + const D my_left_dow = + deux_left_times_troi_left < 0.f ? 0.f : deux_left_vs_troi_left; + /* + * "right": + */ + const D my_rite__up = + prem_rite_times_deux_rite < 0.f ? 0.f : prem_rite_vs_deux_rite; + const D my_rite_dow = + deux_rite_times_troi_rite < 0.f ? 0.f : deux_rite_vs_troi_rite; + + /* + * Assemble the unweighted YAFRSMOOTH correction: + */ + const float yafr = + c[0] * (mx_left__up - mx_rite__up) + + c[1] * (mx_left_dow - mx_rite_dow) + + c[2] * (my_left__up - my_left_dow) + + c[3] * (my_rite__up - my_rite_dow); + + return( yafr ); +} + +/* Pointers to write to / read from, number of bands, + * how many bytes to add to move down a line. + */ + +/* T is the type of pixels we are reading and writing, D is a type large + * enough to hold (T1 - T2) ** 2. + */ + +/* Fixed-point version for 8/16 bit ints. + */ +template +static void inline +yafrsmooth_int_tab( PEL *pout, const PEL *pin, + const int bands, const int lskip, + const double sharpening, + const int *cx, const int *cy, const double *cs ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = 2 * bands; + const int b3 = 3 * bands; + + const int l1 = lskip / sizeof( T ); + const int l2 = 2 * lskip / sizeof( T ); + const int l3 = 3 * lskip / sizeof( T ); + + for( int z = 0; z < bands; z++ ) { + + const T uno_one = in[0]; + const T uno_two = in[b1]; + const T uno_thr = in[b2]; + const T uno_fou = in[b3]; + + const T dos_one = in[l1]; + const T dos_two = in[b1 + l1]; + const T dos_thr = in[b2 + l1]; + const T dos_fou = in[b3 + l1]; + + const T tre_one = in[l2]; + const T tre_two = in[b1 + l2]; + const T tre_thr = in[b2 + l2]; + const T tre_fou = in[b3 + l2]; + + const T qua_one = in[l3]; + const T qua_two = in[b1 + l3]; + const T qua_thr = in[b2 + l3]; + const T qua_fou = in[b3 + l3]; + + const int bicubic = bicubic_int( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cx, cy ); + + const float yafr = yafrsmooth( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cs ); + + int result = bicubic + + sharpening * SMOOTH_SHARPENING_SCALE * yafr; + + if( result < min_value ) + result = min_value; + else if( result > max_value ) + result = max_value; + + *out = result; + + in += 1; + out += 1; + } +} + +/* Float version for int/float types. + */ +template static void inline +yafrsmooth_float_tab( PEL *pout, const PEL *pin, + const int bands, const int lskip, + const double sharpening, + const double *cx, const double *cy, const double *cs ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = 2 * bands; + const int b3 = 3 * bands; + + const int l1 = lskip / sizeof( T ); + const int l2 = 2 * lskip / sizeof( T ); + const int l3 = 3 * lskip / sizeof( T ); + + for( int z = 0; z < bands; z++ ) { + + const T uno_one = in[0]; + const T uno_two = in[b1]; + const T uno_thr = in[b2]; + const T uno_fou = in[b3]; + + const T dos_one = in[l1]; + const T dos_two = in[b1 + l1]; + const T dos_thr = in[b2 + l1]; + const T dos_fou = in[b3 + l1]; + + const T tre_one = in[l2]; + const T tre_two = in[b1 + l2]; + const T tre_thr = in[b2 + l2]; + const T tre_fou = in[b3 + l2]; + + const T qua_one = in[l3]; + const T qua_two = in[b1 + l3]; + const T qua_thr = in[b2 + l3]; + const T qua_fou = in[b3 + l3]; + + const T bicubic = bicubic_float( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cx, cy ); + + const float yafr = yafrsmooth( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cs ); + + *out = bicubic + sharpening * SMOOTH_SHARPENING_SCALE * yafr; + + in += 1; + out += 1; + } +} + +/* Given an offset in [0,1], calculate c0, c1, c2, c3, the yafr-smooth pixel + * weights. + */ +static void inline +calculate_coefficients_smooth( const double x, const double y, double c[4] ) +{ + const double dx = 1.f - x; + const double dy = 1.f - y; + + g_assert( x >= 0 && x < 1 ); + g_assert( y >= 0 && y < 1 ); + + c[0] = dx * x * dy; + c[1] = dx * x * y; + c[2] = dy * y * dx; + c[3] = dy * y * x; +} + +/* High-quality double-only version. + */ +static void inline +yafrsmooth_notab( PEL *pout, const PEL *pin, + const int bands, const int lskip, + const double sharpening, + double x, double y ) +{ + double * restrict out = (double *) pout; + const double * restrict in = (double *) pin; + + const int b1 = bands; + const int b2 = 2 * bands; + const int b3 = 3 * bands; + + const int l1 = lskip / sizeof( double ); + const int l2 = 2 * lskip / sizeof( double ); + const int l3 = 3 * lskip / sizeof( double ); + + double cx[4]; + double cy[4]; + + calculate_coefficients_catmull( x, cx ); + calculate_coefficients_catmull( y, cy ); + + double cs[4]; + + calculate_coefficients_smooth( x, y, cs ); + + for( int z = 0; z < bands; z++ ) { + const double uno_one = in[0]; + const double uno_two = in[b1]; + const double uno_thr = in[b2]; + const double uno_fou = in[b3]; + + const double dos_one = in[l1]; + const double dos_two = in[b1 + l1]; + const double dos_thr = in[b2 + l1]; + const double dos_fou = in[b3 + l1]; + + const double tre_one = in[l2]; + const double tre_two = in[b1 + l2]; + const double tre_thr = in[b2 + l2]; + const double tre_fou = in[b3 + l2]; + + const double qua_one = in[l3]; + const double qua_two = in[b1 + l3]; + const double qua_thr = in[b2 + l3]; + const double qua_fou = in[b3 + l3]; + + const double bicubic = bicubic_float( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cx, cy ); + + const double yafr = yafrsmooth( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cs ); + + *out = bicubic + sharpening * SMOOTH_SHARPENING_SCALE * yafr; + + in += 1; + out += 1; + } +} + +static void +vips_interpolate_yafrsmooth_interpolate( VipsInterpolate *interpolate, + PEL *out, REGION *in, double x, double y ) +{ + VipsInterpolateYafrsmoothClass *yafrsmooth_class = + VIPS_INTERPOLATE_YAFRSMOOTH_GET_CLASS( interpolate ); + VipsInterpolateYafrsmooth *yafrsmooth = + VIPS_INTERPOLATE_YAFRSMOOTH( interpolate ); + + /* Scaled int. + */ + const double sx = x * VIPS_TRANSFORM_SCALE; + const double sy = y * VIPS_TRANSFORM_SCALE; + const int sxi = FLOOR( sx ); + const int syi = FLOOR( sy ); + + /* Get index into interpolation table and unscaled integer + * position. + */ + const int tx = sxi & (VIPS_TRANSFORM_SCALE - 1); + const int ty = syi & (VIPS_TRANSFORM_SCALE - 1); + const int xi = sxi >> VIPS_TRANSFORM_SHIFT; + const int yi = syi >> VIPS_TRANSFORM_SHIFT; + + /* Look up the tables we need. + */ + const int *cxi = yafrsmooth_class->matrixi[tx]; + const int *cyi = yafrsmooth_class->matrixi[ty]; + const double *cxf = yafrsmooth_class->matrixf[tx]; + const double *cyf = yafrsmooth_class->matrixf[ty]; + + /* Position weights for yafrsmooth. + */ + double cs[4]; + calculate_coefficients_smooth( x - xi, y - yi, cs ); + + /* Back and up one to get the top-left of the 4x4. + */ + const PEL *p = (PEL *) IM_REGION_ADDR( in, xi - 1, yi - 1 ); + + /* Pel size and line size. + */ + const int bands = in->im->Bands; + const int lskip = IM_REGION_LSKIP( in ); + +#ifdef DEBUG + printf( "vips_interpolate_yafrsmooth_interpolate: %g %g\n", x, y ); + printf( "\tleft=%d, top=%d, width=%d, height=%d\n", + xi - 1, yi - 1, 4, 4 ); +#endif /*DEBUG*/ + + switch( in->im->BandFmt ) { + case IM_BANDFMT_UCHAR: + yafrsmooth_int_tab( + out, p, bands, lskip, + yafrsmooth->sharpening, + cxi, cyi, cs ); + break; + + case IM_BANDFMT_CHAR: + yafrsmooth_int_tab( + out, p, bands, lskip, + yafrsmooth->sharpening, + cxi, cyi, cs ); + break; + + case IM_BANDFMT_USHORT: + yafrsmooth_int_tab( + out, p, bands, lskip, + yafrsmooth->sharpening, + cxi, cyi, cs ); + break; + + case IM_BANDFMT_SHORT: + yafrsmooth_int_tab( + out, p, bands, lskip, + yafrsmooth->sharpening, + cxi, cyi, cs ); + break; + + case IM_BANDFMT_UINT: + yafrsmooth_float_tab( + out, p, bands, lskip, + yafrsmooth->sharpening, + cxf, cyf, cs ); + break; + + case IM_BANDFMT_INT: + yafrsmooth_float_tab( + out, p, bands, lskip, + yafrsmooth->sharpening, + cxf, cyf, cs ); + break; + + case IM_BANDFMT_FLOAT: + yafrsmooth_float_tab( + out, p, bands, lskip, + yafrsmooth->sharpening, + cxf, cyf, cs ); + break; + + case IM_BANDFMT_DOUBLE: + yafrsmooth_notab( + out, p, bands, lskip, + yafrsmooth->sharpening, + x - xi, y - yi ); + break; + + case IM_BANDFMT_COMPLEX: + yafrsmooth_float_tab( + out, p, bands * 2, lskip, + yafrsmooth->sharpening, + cxf, cyf, cs ); + break; + + case IM_BANDFMT_DPCOMPLEX: + yafrsmooth_notab( + out, p, bands * 2, lskip, + yafrsmooth->sharpening, + x - xi, y - yi ); + break; + + default: + break; + } +} + +static void +vips_interpolate_yafrsmooth_class_init( VipsInterpolateYafrsmoothClass *iclass ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( iclass ); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( iclass ); + VipsInterpolateClass *interpolate_class = + VIPS_INTERPOLATE_CLASS( iclass ); + + GParamSpec *pspec; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "yafrsmooth"; + object_class->description = _( "Bicubic plus edge enhance" ); + + interpolate_class->interpolate = + vips_interpolate_yafrsmooth_interpolate; + interpolate_class->window_size = 4; + + /* Build the tables of pre-computed coefficients. + */ + for( int x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) { + calculate_coefficients_catmull( + (float) x / VIPS_TRANSFORM_SCALE, + iclass->matrixf[x] ); + + for( int i = 0; i < 4; i++ ) + iclass->matrixi[x][i] = + iclass->matrixf[x][i] * VIPS_INTERPOLATE_SCALE; + } + + /* Create properties. + */ + pspec = g_param_spec_double( "sharpening", + _( "Sharpening" ), + _( "Degree of extra edge enhancement" ), + 0, 4, 1, + (GParamFlags) G_PARAM_READWRITE ); + g_object_class_install_property( gobject_class, + PROP_SHARPENING, pspec ); + vips_object_class_install_argument( object_class, pspec, + VIPS_ARGUMENT_SET_ONCE, + G_STRUCT_OFFSET( VipsInterpolateYafrsmooth, sharpening ) ); +} + +static void +vips_interpolate_yafrsmooth_init( VipsInterpolateYafrsmooth *yafrsmooth ) +{ +#ifdef DEBUG + printf( "vips_interpolate_yafrsmooth_init: " ); + vips_object_print( VIPS_OBJECT( yafrsmooth ) ); +#endif /*DEBUG*/ + + yafrsmooth->sharpening = 1.0; +} diff --git a/libvips/video/Makefile.am b/libvips/video/Makefile.am new file mode 100644 index 00000000..7ac9db09 --- /dev/null +++ b/libvips/video/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = libvideo.la + +libvideo_la_SOURCES = \ + video_dispatch.c \ + im_video_v4l1.c \ + im_video_test.c + +INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/video/im_video_test.c b/libvips/video/im_video_test.c new file mode 100644 index 00000000..31f81e53 --- /dev/null +++ b/libvips/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", "%s", _( "error requested" ) ); + return( -1 ); + } + else + return( im_gaussnoise( im, 720, 576, brightness, 20 ) ); +} + diff --git a/libvips/video/im_video_v4l1.c b/libvips/video/im_video_v4l1.c new file mode 100644 index 00000000..112d86b6 --- /dev/null +++ b/libvips/video/im_video_v4l1.c @@ -0,0 +1,679 @@ +/* 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", "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "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", + "%s", _( "compiled without im_video_v4l1 support" ) ); + return( -1 ); +} + +#endif /*HAVE_VIDEODEV*/ diff --git a/libvips/video/im_video_v4l1.h b/libvips/video/im_video_v4l1.h new file mode 100644 index 00000000..57ece650 --- /dev/null +++ b/libvips/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/libvips/video/video_dispatch.c b/libvips/video/video_dispatch.c new file mode 100644 index 00000000..6a9c959e --- /dev/null +++ b/libvips/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/libvipsCC/Makefile.am b/libvipsCC/Makefile.am new file mode 100644 index 00000000..1fb79bba --- /dev/null +++ b/libvipsCC/Makefile.am @@ -0,0 +1,24 @@ +INCLUDES = \ + -I$(top_srcdir)/libvips/include \ + -I$(top_srcdir)/libvipsCC/include \ + @VIPS_CFLAGS@ + +lib_LTLIBRARIES = libvipsCC.la + +libvipsCC_la_SOURCES = \ + VImage.cc \ + VError.cc \ + VDisplay.cc \ + VMask.cc + +libvipsCC_la_LDFLAGS = \ + -no-undefined \ + -version-info @LIBRARY_CURRENT@:@LIBRARY_REVISION@:@LIBRARY_AGE@ + +libvipsCC_la_LIBADD = \ + $(top_builddir)/libvips/libvips.la @VIPS_LIBS@ + +vipsc++.cc: + vips --cppc all > vipsc++.cc + +EXTRA_DIST = vipsc++.cc diff --git a/libvipsCC/VDisplay.cc b/libvipsCC/VDisplay.cc new file mode 100644 index 00000000..2c11bb4a --- /dev/null +++ b/libvipsCC/VDisplay.cc @@ -0,0 +1,189 @@ +// 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 + +#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/libvipsCC/VError.cc b/libvipsCC/VError.cc new file mode 100644 index 00000000..6905f1ec --- /dev/null +++ b/libvipsCC/VError.cc @@ -0,0 +1,99 @@ +// 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/libvipsCC/VImage.cc b/libvipsCC/VImage.cc new file mode 100644 index 00000000..13e9f041 --- /dev/null +++ b/libvipsCC/VImage.cc @@ -0,0 +1,513 @@ +// 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 + +#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 VipsImage 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 ... mode defaults to "r" +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 VipsImage structure +VImage::VImage( _VipsImage *in ) +{ + _ref = new refblock; + + _ref->im = in; + _ref->close_on_delete = 0; + +#ifdef DEBUG + printf( "VImage::VImage( VipsImage* %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() ); +} + +// Like jpeg2vips, but convert to a disc file rather than to memory +// We can handle huge files without running out of RAM +VImage VImage::convert2disc( const char* convert, + const char* in, const char* disc ) throw( VError ) +{ + VImage out( disc, "w" ); + + Vargv _vec( convert ); + + _vec.data(0) = (im_object) in; + _vec.data(1) = out.image(); + _vec.call(); + + return( out ); +} + +// 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 ) ); } + +// metadata + +// base functionality +void VImage::meta_set( const char *field, GValue *value ) throw( VError ) +{ + if( im_meta_set( _ref->im, field, value ) ) + verror(); +} + +void VImage::meta_get( const char *field, GValue *value_copy ) throw( VError ) +{ + if( im_meta_get( _ref->im, field, value_copy ) ) + verror(); +} + +GType VImage::meta_get_typeof( const char *field ) throw( VError ) +{ + return( im_meta_get_typeof( _ref->im, field ) ); +} + +// convenience functions +int VImage::meta_get_int( const char *field ) + throw( VError ) +{ + int result; + + if( im_meta_get_int( _ref->im, field, &result ) ) + verror(); + + return( result ); +} + +double VImage::meta_get_double( const char *field ) + throw( VError ) +{ + double result; + + if( im_meta_get_double( _ref->im, field, &result ) ) + verror(); + + return( result ); +} + +const char *VImage::meta_get_string( const char *field ) + throw( VError ) +{ + char *result; + + if( im_meta_get_string( _ref->im, field, &result ) ) + verror(); + + return( result ); +} + +void *VImage::meta_get_area( const char *field ) throw( VError ) +{ + void *result; + + if( im_meta_get_area( _ref->im, field, &result ) ) + verror(); + + return( result ); +} + +void *VImage::meta_get_blob( const char *field, size_t *length ) throw( VError ) +{ + void *result; + + if( im_meta_get_blob( _ref->im, field, &result, length ) ) + verror(); + + return( result ); +} + +void VImage::meta_set( const char *field, int value ) + throw( VError ) +{ + if( im_meta_set_int( _ref->im, field, value ) ) + verror(); +} + +void VImage::meta_set( const char *field, double value ) + throw( VError ) +{ + if( im_meta_set_double( _ref->im, field, value ) ) + verror(); +} + +void VImage::meta_set( const char *field, const char *value ) + throw( VError ) +{ + if( im_meta_set_string( _ref->im, field, value ) ) + verror(); +} + +void VImage::meta_set( const char *field, + VCallback free_fn, void *value ) + throw( VError ) +{ + if( im_meta_set_area( _ref->im, field, free_fn, value ) ) + verror(); +} + +void VImage::meta_set( const char *field, + VCallback free_fn, void *value, size_t length ) + throw( VError ) +{ + if( im_meta_set_blob( _ref->im, field, free_fn, value, length ) ) + verror(); +} + +// 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/libvipsCC/VMask.cc b/libvipsCC/VMask.cc new file mode 100644 index 00000000..f9f78b49 --- /dev/null +++ b/libvipsCC/VMask.cc @@ -0,0 +1,661 @@ +// 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 vector +_private_detail::VPIMask::VPIMask( int xsize, int ysize, + int scale, int offset, std::vector coeff ) + throw( VError ) +{ + int i; + + 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( i = 0; i < xsize * ysize; i++ ) + data.iptr->coeff[i] = coeff[i]; +} + +// 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 vector +_private_detail::VPDMask::VPDMask( int xsize, int ysize, + double scale, double offset, std::vector coeff ) throw( VError ) +{ + int i; + + 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( i = 0; i < xsize * ysize; i++ ) + data.dptr->coeff[i] = coeff[i]; +} + +// 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 ); +} + +VIMask VIMask::gauss_sep( double sig, double minamp ) throw( VError ) +{ + VIMask out; + INTMASK *msk; + + if( !(msk = im_gauss_imask_sep( "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/libvipsCC/include/Makefile.am b/libvipsCC/include/Makefile.am new file mode 100644 index 00000000..5b4fdf05 --- /dev/null +++ b/libvipsCC/include/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = vipsCC diff --git a/libvipsCC/include/vipsCC/Makefile.am b/libvipsCC/include/vipsCC/Makefile.am new file mode 100644 index 00000000..24f06686 --- /dev/null +++ b/libvipsCC/include/vipsCC/Makefile.am @@ -0,0 +1,11 @@ +pkginclude_HEADERS = \ + VDisplay.h \ + VError.h \ + VImage.h \ + VMask.h \ + vipscpp.h \ + vips \ + vipsc++.h + +vipsc++.h: + vips --cpph all > vipsc++.h diff --git a/libvipsCC/include/vipsCC/VDisplay.h b/libvipsCC/include/vipsCC/VDisplay.h new file mode 100644 index 00000000..5e027a65 --- /dev/null +++ b/libvipsCC/include/vipsCC/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/libvipsCC/include/vipsCC/VError.h b/libvipsCC/include/vipsCC/VError.h new file mode 100644 index 00000000..69aa4b83 --- /dev/null +++ b/libvipsCC/include/vipsCC/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/libvipsCC/include/vipsCC/VImage.h b/libvipsCC/include/vipsCC/VImage.h new file mode 100644 index 00000000..9e4f1017 --- /dev/null +++ b/libvipsCC/include/vipsCC/VImage.h @@ -0,0 +1,447 @@ +// 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 _VipsImage; + + /* Needed by Vargv, see below. + */ + struct im__function; + typedef void *im__object; +} + +VIPS_NAMESPACE_START + +/* A VIPS callback, our name for im_callback_fn. + */ +typedef int (*VCallback)( void *, void * ); + +/* 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 { + _VipsImage *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, + RAD = 6 + }; + + // 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( _VipsImage *image ); + VImage() throw( VError ); + + // Convert to a disc file, eg: + // VImage fred = VImage::convert2disc( "im_jpeg2vips", + // "file.jpg", "temp.v" ); + // Runs im_jpeg2vips to the temp file, then opens that and returns + // it. Useful for opening very large files without using a lot of RAM. + // Now superceeded by the format API, though that's not yet wrapped in + // C++ + static VImage convert2disc( const char* convert, + const char* in, const char* disc ) 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 + _VipsImage *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(); + + // metadata +#ifndef SWIG + // base functionality + // we don't wrap GValue, so we can't wrap these for now + void meta_set( const char *field, GValue *value ) throw( VError ); + void meta_get( const char *field, GValue *value_copy ) throw( VError ); + GType meta_get_typeof( const char *field ) throw( VError ); +#endif /*SWIG*/ + + // convenience functions + int meta_get_int( const char *field ) throw( VError ); + double meta_get_double( const char *field ) throw( VError ); + const char *meta_get_string( const char *field ) throw( VError ); + void *meta_get_area( const char *field ) throw( VError ); + void *meta_get_blob( const char *field, size_t *length ) + throw( VError ); + + void meta_set( const char *field, int value ) throw( VError ); + void meta_set( const char *field, double value ) throw( VError ); + void meta_set( const char *field, const char *value ) throw( VError ); + +#ifndef SWIG + // we don't wrap callbacks yet, so we can't wrap these for now + void meta_set( const char *field, + VCallback free_fn, void *value ) + throw( VError ); + void meta_set( const char *field, + VCallback free_fn, void *value, size_t length ) + throw( VError ); +#endif /*SWIG*/ + + // 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 we need +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/libvipsCC/include/vipsCC/VMask.h b/libvipsCC/include/vipsCC/VMask.h new file mode 100644 index 00000000..471a0cd2 --- /dev/null +++ b/libvipsCC/include/vipsCC/VMask.h @@ -0,0 +1,410 @@ +/* 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 +# 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, + std::vector coeff ) 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, std::vector coeff ) + 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 ); + } + +/* Don't wrap the varargs constructor. We want Python to use the vector one. + */ +#ifndef SWIG + VIMask( int xsize, int ysize, int scale, int offset, ... ) + { + va_list ap; + int i; + std::vector coeff( xsize * ysize ); + + va_start( ap, offset ); + for( i = 0; i < xsize * ysize; i++ ) + coeff[i] = va_arg( ap, int ); + va_end( ap ); + + ref->pmask = new _private_detail::VPIMask( xsize, ysize, + scale, offset, coeff ); + } +#endif /*!SWIG*/ + + VIMask( int xsize, int ysize, int scale, int offset, + std::vector coeff ) + { + ref->pmask = new _private_detail::VPIMask( xsize, ysize, + scale, offset, coeff ); + } + + 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 gauss_sep( 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 ); + } + +/* Don't wrap the varargs constructor. We want Python to use the vector one. + */ +#ifndef SWIG + VDMask( int xsize, int ysize, double scale, double offset, ... ) + { + va_list ap; + int i; + std::vector coeff( xsize * ysize ); + + va_start( ap, offset ); + for( i = 0; i < xsize * ysize; i++ ) + coeff[i] = va_arg( ap, double ); + va_end( ap ); + + ref->pmask = new _private_detail::VPDMask( xsize, ysize, + scale, offset, coeff ); + } +#endif /*!SWIG*/ + + VDMask( int xsize, int ysize, double scale, double offset, + std::vector coeff ) + { + ref->pmask = new _private_detail::VPDMask( xsize, ysize, + scale, offset, coeff ); + } + + 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/libvipsCC/include/vipsCC/vips b/libvipsCC/include/vipsCC/vips new file mode 100644 index 00000000..2b32076e --- /dev/null +++ b/libvipsCC/include/vipsCC/vips @@ -0,0 +1,109 @@ +// 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 + +// VImage.h uses GValue for metadata +#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/libvipsCC/include/vipsCC/vipsc++.h b/libvipsCC/include/vipsCC/vipsc++.h new file mode 100644 index 00000000..369f21de --- /dev/null +++ b/libvipsCC/include/vipsCC/vipsc++.h @@ -0,0 +1,315 @@ +// this file automatically generated from +// VIPS library 7.19.0-Mon Jun 29 15:52:56 BST 2009 +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 ); +VImage cross_phase( VImage ) 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 greyc( int, double, double, double, double, double, double, double, double, int, int ) throw( VError ); +VImage greyc_mask( VImage, int, double, double, double, double, double, double, double, double, int, 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 float2rad() 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 rad2float() 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 ); +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 ); +VImage lrjoin( VImage ) throw( VError ); +static VImage mask2vips( VDMask ) throw( VError ); +VImage msb() throw( VError ); +VImage msb_band( int ) 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 ); +VDMask vips2mask() throw( VError ); +VImage wrap( int, int ) 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 phasecor_fft( VImage ) 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 stretch3( double, double ) throw( VError ); +VImage zerox( int ) throw( VError ); +static VImage csv2vips( char* ) throw( VError ); +static VImage jpeg2vips( char* ) throw( VError ); +static VImage magick2vips( char* ) 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 ); +static VImage tiff2vips( char* ) throw( VError ); +void vips2csv( char* ) throw( VError ); +void vips2jpeg( char* ) throw( VError ); +void vips2mimejpeg( int ) throw( VError ); +void vips2png( char* ) throw( VError ); +void vips2ppm( char* ) throw( VError ); +void vips2tiff( char* ) 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_typeof( 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 align_bands() 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 ); +double maxpos_subpel( double& ) throw( VError ); +VImage remosaic( char*, char* ) 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 ); +VImage affine( double, double, double, double, double, double, int, int, int, int ) throw( VError ); +VImage similarity_area( double, double, double, double, int, int, int, int ) throw( VError ); +VImage similarity( double, double, double, 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/libvipsCC/include/vipsCC/vipscpp.h b/libvipsCC/include/vipsCC/vipscpp.h new file mode 100644 index 00000000..ec394911 --- /dev/null +++ b/libvipsCC/include/vipsCC/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/libvipsCC/vipsc++.cc b/libvipsCC/vipsc++.cc new file mode 100644 index 00000000..7267673a --- /dev/null +++ b/libvipsCC/vipsc++.cc @@ -0,0 +1,5502 @@ +// this file automatically generated from +// VIPS library 7.19.0-Mon Jun 29 15:52:56 BST 2009 +// 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_cross_phase: phase of cross power spectrum of two complex images +VImage VImage::cross_phase( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_cross_phase" ); + + _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_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_greyc: noise-removing filter +VImage VImage::greyc( int iterations, double amplitude, double sharpness, double anisotropy, double alpha, double sigma, double dl, double da, double gauss_prec, int interpolation, int fast_approx ) throw( VError ) +{ + VImage src = *this; + VImage dst; + + Vargv _vec( "im_greyc" ); + + _vec.data(0) = src.image(); + _vec.data(1) = dst.image(); + *((int*) _vec.data(2)) = iterations; + *((double*) _vec.data(3)) = amplitude; + *((double*) _vec.data(4)) = sharpness; + *((double*) _vec.data(5)) = anisotropy; + *((double*) _vec.data(6)) = alpha; + *((double*) _vec.data(7)) = sigma; + *((double*) _vec.data(8)) = dl; + *((double*) _vec.data(9)) = da; + *((double*) _vec.data(10)) = gauss_prec; + *((int*) _vec.data(11)) = interpolation; + *((int*) _vec.data(12)) = fast_approx; + _vec.call(); + dst._ref->addref( src._ref ); + + return( dst ); +} + +// im_greyc_mask: noise-removing filter, with a mask +VImage VImage::greyc_mask( VImage mask, int iterations, double amplitude, double sharpness, double anisotropy, double alpha, double sigma, double dl, double da, double gauss_prec, int interpolation, int fast_approx ) throw( VError ) +{ + VImage src = *this; + VImage dst; + + Vargv _vec( "im_greyc_mask" ); + + _vec.data(0) = src.image(); + _vec.data(1) = dst.image(); + _vec.data(2) = mask.image(); + *((int*) _vec.data(3)) = iterations; + *((double*) _vec.data(4)) = amplitude; + *((double*) _vec.data(5)) = sharpness; + *((double*) _vec.data(6)) = anisotropy; + *((double*) _vec.data(7)) = alpha; + *((double*) _vec.data(8)) = sigma; + *((double*) _vec.data(9)) = dl; + *((double*) _vec.data(10)) = da; + *((double*) _vec.data(11)) = gauss_prec; + *((int*) _vec.data(12)) = interpolation; + *((int*) _vec.data(13)) = fast_approx; + _vec.call(); + dst._ref->addref( src._ref ); + dst._ref->addref( mask._ref ); + + return( dst ); +} + +// 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_float2rad: convert float to Radiance packed +VImage VImage::float2rad() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_float2rad" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _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_rad2float: convert Radiance packed to float +VImage VImage::rad2float() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_rad2float" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _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_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_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_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_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_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_wrap: shift image origin, wrapping at sides +VImage VImage::wrap( int x, int y ) throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_wrap" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + *((int*) _vec.data(2)) = x; + *((int*) _vec.data(3)) = y; + _vec.call(); + out._ref->addref( in._ref ); + + return( out ); +} + +// 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: horizontal difference 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: vertical difference 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_phasecor_fft: non-normalised correlation of gradient of in2 within in1 +VImage VImage::phasecor_fft( VImage in2 ) throw( VError ) +{ + VImage in1 = *this; + VImage out; + + Vargv _vec( "im_phasecor_fft" ); + + _vec.data(0) = in1.image(); + _vec.data(1) = in2.image(); + _vec.data(2) = out.image(); + _vec.call(); + + return( out ); +} + +// 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_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_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_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_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_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_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_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_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_typeof: return field type +int VImage::header_get_typeof( char* field ) throw( VError ) +{ + VImage image = *this; + int gtype; + + Vargv _vec( "im_header_get_typeof" ); + + _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_align_bands: align the bands of an image +VImage VImage::align_bands() throw( VError ) +{ + VImage in = *this; + VImage out; + + Vargv _vec( "im_align_bands" ); + + _vec.data(0) = in.image(); + _vec.data(1) = out.image(); + _vec.call(); + + 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_maxpos_subpel: subpixel position of maximum of (phase correlation) image +double VImage::maxpos_subpel( double& y ) throw( VError ) +{ + VImage im = *this; + double x; + + Vargv _vec( "im_maxpos_subpel" ); + + _vec.data(0) = im.image(); + _vec.call(); + x = *((double*)_vec.data(1)); + y = *((double*)_vec.data(2)); + + return( x ); +} + +// 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_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_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_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_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/swig/Makefile.am b/swig/Makefile.am new file mode 100644 index 00000000..f232c586 --- /dev/null +++ b/swig/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = \ + vipsCC + +EXTRA_DIST = \ + test diff --git a/swig/test/bench_pil.py b/swig/test/bench_pil.py new file mode 100755 index 00000000..99e7374d --- /dev/null +++ b/swig/test/bench_pil.py @@ -0,0 +1,24 @@ +#!/usr/bin/python + +import Image, sys +import ImageFilter + +im = Image.open (sys.argv[1]) + +# Crop 100 pixels off all edges. +im = im.crop ((100, 100, im.size[0] - 100, im.size[1] - 100)) + +# Shrink by 10% +im = im.resize ((int (im.size[0] * 0.9), int (im.size[1] * 0.9)), + Image.BILINEAR) + +# sharpen +filter = ImageFilter.Kernel ((3, 3), + (-1, -1, -1, + -1, 16, -1, + -1, -1, -1)) +im = im.filter (filter) + +# write back again +im.save (sys.argv[2]) + diff --git a/swig/test/bench_vips.py b/swig/test/bench_vips.py new file mode 100755 index 00000000..cf8e0bc2 --- /dev/null +++ b/swig/test/bench_vips.py @@ -0,0 +1,24 @@ +#!/usr/bin/python + +import sys +from vipsCC import * + +im = VImage.VImage (sys.argv[1]) + +# Crop 100 pixels off all edges. +im = im.extract_area (100, 100, im.Xsize() - 200, im.Ysize() - 200) + +# Shrink by 10% +im = im.affine (0.9, 0, 0, 0.9, 0, 0, 0, 0, + int (im.Xsize() * 0.9), int (im.Ysize() * 0.9)) + +# sharpen +mask = VMask.VIMask (3, 3, 8, 0, + [-1, -1, -1, + -1, 16, -1, + -1, -1, -1]) +im = im.conv (mask) + +# write back again +im.write (sys.argv[2]) + diff --git a/swig/test/pilvips.py b/swig/test/pilvips.py new file mode 100755 index 00000000..4bd62102 --- /dev/null +++ b/swig/test/pilvips.py @@ -0,0 +1,37 @@ +#!/usr/bin/python + +import sys + +from vipsCC import * +import Image + +# try this 1,000 times and check for leaks +for i in range (0,1000): + vim = VImage.VImage (sys.argv[1]) + + # do some processing in vips ... cut out a small piece of image + vim = vim.extract_area (500, 500, 100, 100) + + # make a PIL image + # we use Image.frombuffer (), so PIL is using vim's memory + # you need to be very careful not to destroy vim until you're done with pim + # ideally you should make a proxy class that wraps this lifetime problem up + mode = VImage.PIL_mode_from_vips (vim) + size = (vim.Xsize (), vim.Ysize ()) + data = vim.tobuffer () + pim = Image.frombuffer (mode, size, data, 'raw', mode, 0, 1) + + # rotate 12 degrees with PIL + pim = pim.rotate (12, Image.BILINEAR, 1) + + # back to vips again + # PIL doesn't have a tobuffer method, so we have to use tostring to copy the + # data out of PIL and then fromstring to copy back into VIPS + str = pim.tostring () + bands, format, type = VImage.vips_from_PIL_mode (pim.mode) + width, height = pim.size + vim2 = VImage.VImage.fromstring (str, width, height, bands, format) + + # finally write from vips + vim2.write (sys.argv[2]) + diff --git a/swig/test/testvipsCC.py b/swig/test/testvipsCC.py new file mode 100755 index 00000000..c8057e15 --- /dev/null +++ b/swig/test/testvipsCC.py @@ -0,0 +1,44 @@ +#!/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]) + m = VMask.VIMask (3, 3, 1, 0, + [-1, -1, -1, + -1, 8, -1, + -1, -1, -1]) + d = a.conv (m) + d.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 +del d +del m +# 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/swig/vipsCC/Makefile.am b/swig/vipsCC/Makefile.am new file mode 100644 index 00000000..29eae526 --- /dev/null +++ b/swig/vipsCC/Makefile.am @@ -0,0 +1,58 @@ +# Let make substitute the value of PYTHON_INCLUDES rather than auto* +# this makes it easier to support multiple python installs +INCLUDES = \ + -I${top_srcdir}/libvips/include \ + -I${top_srcdir}/libvipsCC/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 + +# I tried making a suffix rule for this (and defining SUFFIXES) but I couldn't +# get it to work, how annoying +# FIXME at some point +# +# need an expanded VImage.h ... SWIG's preprocessor b0rks on includes inside +# class definitions +vimagemodule.cxx: VImage.i + cpp -DSWIG -E $(top_srcdir)/libvipsCC/include/vipsCC/VImage.h > VImage.h + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/libvipsCC/include -o $@ $< + +vdisplaymodule.cxx: VDisplay.i + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/libvipsCC/include -o $@ $< +verrormodule.cxx: VError.i + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/libvipsCC/include -o $@ $< +vmaskmodule.cxx: VMask.i + swig -python -c++ -interface $(@:.cxx=) -I$(top_srcdir)/libvipsCC/include -o $@ $< + +vipscc_LTLIBRARIES = vimagemodule.la vdisplaymodule.la verrormodule.la vmaskmodule.la + +# maybe there's a clever way to avoid repeating the link stuff 4 times +# vimagemodule uses the C API as well, so it needs libvips too +vimagemodule_la_LDFLAGS = -module -avoid-version +vimagemodule_la_LIBADD = ../../libvipsCC/libvipsCC.la ../../libvips/libvips.la $(VIPS_LIBS) +nodist_vimagemodule_la_SOURCES = vimagemodule.cxx + +vdisplaymodule_la_LDFLAGS = -module -avoid-version +vdisplaymodule_la_LIBADD = ../../libvipsCC/libvipsCC.la $(VIPS_LIBS) +nodist_vdisplaymodule_la_SOURCES = vdisplaymodule.cxx + +verrormodule_la_LDFLAGS = -module -avoid-version +verrormodule_la_LIBADD = ../../libvipsCC/libvipsCC.la $(VIPS_LIBS) +nodist_verrormodule_la_SOURCES = verrormodule.cxx + +vmaskmodule_la_LDFLAGS = -module -avoid-version +vmaskmodule_la_LIBADD = ../../libvipsCC/libvipsCC.la $(VIPS_LIBS) +nodist_vmaskmodule_la_SOURCES = vmaskmodule.cxx + +CLEANFILES = VImage.h + +EXTRA_DIST = \ + VImage.i VDisplay.i VError.i VMask.i __init__.py \ + vimagemodule.cxx \ + verrormodule.cxx vdisplaymodule.cxx vmaskmodule.cxx \ + VImage.py VDisplay.py VError.py VMask.py diff --git a/swig/vipsCC/VDisplay.i b/swig/vipsCC/VDisplay.i new file mode 100644 index 00000000..64868980 --- /dev/null +++ b/swig/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 vipsCC/VDisplay.h diff --git a/swig/vipsCC/VError.i b/swig/vipsCC/VError.i new file mode 100644 index 00000000..4a09690b --- /dev/null +++ b/swig/vipsCC/VError.i @@ -0,0 +1,19 @@ +/* SWIG interface file for VError. + */ + +%module VError +%{ +#include +%} + +%include "std_except.i" +%include "std_string.i" + +%include vipsCC/VError.h + +%extend vips::VError { + const char *__str__ () { + return $self->what (); + } +} + diff --git a/swig/vipsCC/VImage.i b/swig/vipsCC/VImage.i new file mode 100644 index 00000000..2d33a19c --- /dev/null +++ b/swig/vipsCC/VImage.i @@ -0,0 +1,335 @@ +/* SWIG interface file for vipsCC7 + * + * 5/9/07 + * - use g_option_context_set_ignore_unknown_options() so we don't fail + * on unrecognied -args (thanks Simon) + * 3/8/08 + * - add .tobuffer() / .frombuffer (), .tostring (), .fromstring () + * methods + * - add PIL_mode_from_vips () and vips_from_PIL_mode () utility + * functions + */ + +%module VImage + +%{ +#include + +/* We need the C API too for the args init and some of the + * frombuffer/tobuffer stuff. + */ +#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" +%include "std_string.i" +%include "cstring.i" + +%import "VError.i" +%import "VMask.i" +%import "VDisplay.i" + +namespace std { + %template(IntVector) vector; + %template(DoubleVector) vector; + %template(ImageVector) vector; +} + +/* To get image data to and from VImage (eg. when interfacing with PIL) we + * need to be able to import and export Python buffer() objects. Add new + * methods to construct from and return pointer/length pairs, then wrap them + * ourselves with a couple of typemaps. + */ + +%{ +struct VBuffer { + void *data; + size_t size; +}; +%} + +%typemap (out) VBuffer { + $result = PyBuffer_FromMemory ($1.data, $1.size); +} + +%typemap (in) VBuffer { + const char *buffer; + Py_ssize_t buffer_len; + + if (PyObject_AsCharBuffer ($input, &buffer, &buffer_len) == -1) { + PyErr_SetString (PyExc_TypeError,"Type error. Unable to get char pointer from buffer"); + return NULL; + } + + $1.data = (void *) buffer; + $1.size = buffer_len; +} + +/* Need the expanded VImage.h in this directory, rather than the usual + * vips/VImage.h. SWIG b0rks on #include inside class definitions. + */ +%include VImage.h + +%extend vips::VImage { +public: + VBuffer tobuffer () throw (VError) + { + VBuffer buffer; + + buffer.data = $self->data (); + buffer.size = (size_t) $self->Xsize () * $self->Ysize () * + IM_IMAGE_SIZEOF_PEL ($self->image ()); + + return buffer; + } + + static VImage frombuffer (VBuffer buffer, int width, int height, + int bands, TBandFmt format) throw (VError) + { + return VImage (buffer.data, width, height, bands, format); + } + + %cstring_output_allocate_size (char **buffer, int *buffer_len, im_free (*$1)) + + void tostring (char **buffer, int *buffer_len) throw (VError) + { + void *vips_memory; + + /* Eval the vips image first. This may throw an exception and we want to + * make sure we do this before we try to malloc() space for the copy. + */ + vips_memory = $self->data (); + + /* We have to copy the image data to make a string that Python can + * manage. Use frombuffer() / tobuffer () if you want to avoid the copy + * and manage memory lifetime yourself. + */ + *buffer_len = (size_t) $self->Xsize () * $self->Ysize () * + IM_IMAGE_SIZEOF_PEL ($self->image ()); + if (!(*buffer = (char *) im_malloc (NULL, *buffer_len))) + verror ("Unable to allocate memory for image copy."); + memcpy (*buffer, vips_memory, *buffer_len); + } + + static VImage fromstring (std::string buffer, int width, int height, + int bands, TBandFmt format) throw (VError) + { + void *vips_memory; + VImage result; + + /* We have to copy the string, then add a callback to the VImage to free + * it when we free the VImage. Use frombuffer() / tobuffer () if you want + * to avoid the copy and manage memory lifetime yourself. + */ + if (!(vips_memory = im_malloc (NULL, buffer.length ()))) + verror ("Unable to allocate memory for image copy."); + + /* We have to use .c_str () since the string may not be contiguous. + */ + memcpy (vips_memory, buffer.c_str (), buffer.length ()); + result = VImage (vips_memory, width, height, bands, format); + + if (im_add_close_callback (result.image (), + (im_callback_fn) im_free, vips_memory, NULL)) + verror (); + + return result; + } +} + +%pythoncode %{ +# try to guess a PIL mode string from a VIPS image +def PIL_mode_from_vips (vim): + if vim.Bands () == 3 and vim.BandFmt () == VImage.FMTUCHAR: + return 'RGB' + elif vim.Bands () == 4 and vim.BandFmt () == VImage.FMTUCHAR and vim.Type == VImage.VImage.RGB: + return 'RGBA' + elif vim.Bands () == 4 and vim.BandFmt () == VImage.FMTUCHAR and vim.Type == VImage.CMYK: + return 'CMYK' + elif vim.Bands () == 1 and vim.BandFmt () == VImage.FMTUCHAR: + return 'L' + elif vim.Bands () == 1 and vim.BandFmt () == VImage.FMTINT: + return 'I' + elif vim.Bands () == 1 and vim.BandFmt () == VImage.FMTFLOAT: + return 'F' + elif vim.Bands () == 2 and vim.BandFmt () == VImage.FMTUCHAR: + return 'LA' + else: + raise ValueError ('unsupported vips -> pil image') + +# return vips (bands, format, type) for a PIL mode +def vips_from_PIL_mode (mode): + if mode == 'RGB': + return (3, VImage.FMTUCHAR, VImage.RGB) + elif mode == 'RGBA': + return (4, VImage.FMTUCHAR, VImage.RGB) + elif mode == 'CMYK': + return (4, VImage.FMTUCHAR, VImage.CMYK) + elif mode == 'L': + return (1, VImage.FMTUCHAR, VImage.B_W) + elif mode == 'I': + return (1, VImage.FMTINT, VImage.B_W) + elif mode == 'F': + return (1, VImage.FMTFLOAT, VImage.B_W) + elif mode == 'LA': + return (2, VImage.FMTUCHAR, VImage.B_W) + else: + raise ValueError ('unsupported pil -> vips image') +%} + +/* Helper code for vips_init(). + */ +%{ +/* 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()); + + g_option_context_set_ignore_unknown_options (context, TRUE); + 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"); + } + 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/swig/vipsCC/VMask.i b/swig/vipsCC/VMask.i new file mode 100644 index 00000000..9d82d419 --- /dev/null +++ b/swig/vipsCC/VMask.i @@ -0,0 +1,35 @@ +/* SWIG interface file for VMask. + */ + +%module VMask +%{ +#include +#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 vipsCC/VMask.h diff --git a/swig/vipsCC/__init__.py b/swig/vipsCC/__init__.py new file mode 100644 index 00000000..0b05d457 --- /dev/null +++ b/swig/vipsCC/__init__.py @@ -0,0 +1 @@ +__all__=["VImage","VMask","VError","VDisplay"] diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 00000000..f15088b8 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,6 @@ + +SUBDIRS = \ + iofuncs \ + mosaicing \ + other \ + scripts diff --git a/tools/iofuncs/Makefile.am b/tools/iofuncs/Makefile.am new file mode 100644 index 00000000..88f6a8b0 --- /dev/null +++ b/tools/iofuncs/Makefile.am @@ -0,0 +1,27 @@ +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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libvips/libvips.la @VIPS_LIBS@ + +if ENABLE_LINKS +install-exec-hook: + ${top_srcdir}/src/scripts/post_install ${DESTDIR}${bindir} +endif + +uninstall-hook: + ${RM} ${bindir}/im_* + diff --git a/tools/iofuncs/binfile.c b/tools/iofuncs/binfile.c new file mode 100644 index 00000000..6238fbfc --- /dev/null +++ b/tools/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/tools/iofuncs/debugim.c b/tools/iofuncs/debugim.c new file mode 100644 index 00000000..44796f0e --- /dev/null +++ b/tools/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/tools/iofuncs/edvips.c b/tools/iofuncs/edvips.c new file mode 100644 index 00000000..137acf3e --- /dev/null +++ b/tools/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( "%s", _( "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( "%s", _( "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( "%s", _( "could not set extension" ) ); + im_free( xml ); + } + + im_close( im ); + + return( 0 ); +} + diff --git a/tools/iofuncs/header.c b/tools/iofuncs/header.c new file mode 100644 index 00000000..27cc9168 --- /dev/null +++ b/tools/iofuncs/header.c @@ -0,0 +1,192 @@ +/* @(#) 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() + * 29/2/08 + * - don't stop 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 + + */ + +#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 } +}; + +/* A non-fatal error. Print the vips error buffer and continue. + */ +static void +print_error( const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + vfprintf( stderr, fmt, ap ); + va_end( ap ); + fprintf( stderr, "\n%s", im_error_buffer() ); + im_error_clear(); +} + +/* 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" )) ) + print_error( "%s: unable to open", argv[i] ); + + if( im && print_header( im ) ) + print_error( "%s: unable to print header", argv[i] ); + + if( im ) + im_close( im ); + } + + return( 0 ); +} diff --git a/tools/iofuncs/printlines.c b/tools/iofuncs/printlines.c new file mode 100644 index 00000000..8930bd01 --- /dev/null +++ b/tools/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/tools/iofuncs/vips.c b/tools/iofuncs/vips.c new file mode 100644 index 00000000..74e55ad5 --- /dev/null +++ b/tools/iofuncs/vips.c @@ -0,0 +1,978 @@ +/* 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 + * 17/8/08 + * - add --list formats + * 29/11/08 + * - add --list interpolators + * 9/2/09 + * - and now we just have --list packages/classes/package-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 + + */ + +/* +#define DEBUG_FATAL + */ + +#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\", \"classes\")" ), + "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( "%-20s - %d operations\n", pack->name, pack->nfuncs ); + + return( NULL ); +} + +static void * +list_function( im_function *func ) +{ + printf( "%-20s - %s\n", func->name, _( func->desc ) ); + + return( NULL ); +} + +static void * +list_class( VipsObjectClass *class ) +{ + vips_object_print_class( class ); + + return( NULL ); +} + +static void +print_list( const char *name ) +{ + if( strcmp( name, "packages" ) == 0 ) + im_map_packages( (VSListMap2Fn) list_package, NULL ); + else if( strcmp( name, "classes" ) == 0 ) + vips_class_map_concrete_all( g_type_from_name( "VipsObject" ), + (VipsClassMap) list_class, 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" ); +#ifdef DEBUG_FATAL + /* Set masks for debugging ... stop on any problem. + */ + g_log_set_always_fatal( + G_LOG_FLAG_RECURSION | + G_LOG_FLAG_FATAL | + G_LOG_LEVEL_ERROR | + G_LOG_LEVEL_CRITICAL | + G_LOG_LEVEL_WARNING ); + + fprintf( stderr, "*** DEBUG_FATAL: will abort() on first warning\n" ); +#endif /*!DEBUG_FATAL*/ + + 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/tools/mosaicing/Makefile.am b/tools/mosaicing/Makefile.am new file mode 100644 index 00000000..e6592c3b --- /dev/null +++ b/tools/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}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +AM_LDFLAGS = @LDFLAGS@ +LDADD = @VIPS_CFLAGS@ ${top_builddir}/libvips/libvips.la @VIPS_LIBS@ + diff --git a/tools/mosaicing/find_mosaic.c b/tools/mosaicing/find_mosaic.c new file mode 100644 index 00000000..46b62ec4 --- /dev/null +++ b/tools/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/tools/other/cooc_features.c b/tools/other/cooc_features.c new file mode 100644 index 00000000..7b8a9ea6 --- /dev/null +++ b/tools/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/tools/other/glds.c b/tools/other/glds.c new file mode 100644 index 00000000..814df4ff --- /dev/null +++ b/tools/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/tools/other/glds_features.c b/tools/other/glds_features.c new file mode 100644 index 00000000..07aff552 --- /dev/null +++ b/tools/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/tools/other/simcontr.c b/tools/other/simcontr.c new file mode 100644 index 00000000..d0f1e53e --- /dev/null +++ b/tools/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/tools/other/sines.c b/tools/other/sines.c new file mode 100644 index 00000000..4ba99469 --- /dev/null +++ b/tools/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/tools/other/spatres.c b/tools/other/spatres.c new file mode 100644 index 00000000..c41b46a5 --- /dev/null +++ b/tools/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/tools/other/squares.c b/tools/other/squares.c new file mode 100644 index 00000000..a91a989f --- /dev/null +++ b/tools/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/tools/scripts/Makefile.am b/tools/scripts/Makefile.am new file mode 100644 index 00000000..aee1ab20 --- /dev/null +++ b/tools/scripts/Makefile.am @@ -0,0 +1,25 @@ +bin_SCRIPTS = \ + light_correct \ + shrink_width \ + batch_image_convert \ + batch_rubber_sheet \ + batch_crop \ + vips-7.19 + +noinst_SCRIPTS = post_install + +EXTRA_DIST = \ + vips-7.18 \ + ${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/tools/scripts/batch_crop.in b/tools/scripts/batch_crop.in new file mode 100644 index 00000000..982b9e66 --- /dev/null +++ b/tools/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/tools/scripts/batch_image_convert.in b/tools/scripts/batch_image_convert.in new file mode 100644 index 00000000..e4fff85a --- /dev/null +++ b/tools/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/tools/scripts/batch_rubber_sheet.in b/tools/scripts/batch_rubber_sheet.in new file mode 100644 index 00000000..d36891f9 --- /dev/null +++ b/tools/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/tools/scripts/light_correct.in b/tools/scripts/light_correct.in new file mode 100644 index 00000000..a96f400e --- /dev/null +++ b/tools/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/tools/scripts/post_install b/tools/scripts/post_install new file mode 100755 index 00000000..6bb9d65a --- /dev/null +++ b/tools/scripts/post_install @@ -0,0 +1,5 @@ +#!/bin/sh + +bin=$1 + +( cd $bin ; $bin/vips -k | sh ) diff --git a/tools/scripts/shrink_width.in b/tools/scripts/shrink_width.in new file mode 100644 index 00000000..c9ba5764 --- /dev/null +++ b/tools/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/tools/scripts/vips-7.19 b/tools/scripts/vips-7.19 new file mode 100755 index 00000000..ee887528 --- /dev/null +++ b/tools/scripts/vips-7.19 @@ -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 $*